├── .gitignore ├── CHANGELOG ├── COPYRIGHT ├── Makefile ├── Makefile.BSD ├── Makefile.inc ├── Makefile.prog ├── README.md ├── STYLE ├── USAGE ├── freebsd-conf ├── newsyslog-imds.conf ├── rc.d-imds-filterd ├── rc.d-imds-proxy └── syslog-imds.conf ├── imds-filterd ├── Makefile ├── Makefile.BSD ├── conns.c ├── ident.c ├── imds-filterd.h ├── main.c ├── netconfig.c ├── packets.c └── tunsetup.c ├── imds-proxy ├── Makefile ├── Makefile.BSD ├── conf.c ├── http.c ├── ident.c ├── imds-proxy.h ├── main.c ├── request.c └── uri2path.c ├── imds.conf ├── libcperciva ├── datastruct │ ├── elasticarray.c │ ├── elasticarray.h │ ├── mpool.h │ ├── ptrheap.c │ ├── ptrheap.h │ ├── timerqueue.c │ └── timerqueue.h ├── events │ ├── events.c │ ├── events.h │ ├── events_immediate.c │ ├── events_internal.h │ ├── events_network.c │ ├── events_network_selectstats.c │ └── events_timer.c ├── network │ ├── network.h │ ├── network_accept.c │ ├── network_read.c │ └── network_write.c └── util │ ├── asprintf.c │ ├── asprintf.h │ ├── ctassert.h │ ├── daemonize.c │ ├── daemonize.h │ ├── getopt.c │ ├── getopt.h │ ├── hexify.c │ ├── hexify.h │ ├── imalloc.h │ ├── monoclock.c │ ├── monoclock.h │ ├── noeintr.c │ ├── noeintr.h │ ├── parsenum.h │ ├── setuidgid.c │ ├── setuidgid.h │ ├── sock.c │ ├── sock.h │ ├── sock_internal.h │ ├── warnp.c │ └── warnp.h └── release-tools └── metabuild.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # Config 2 | cflags-filter.sh 3 | cpusupport-config.h 4 | posix-flags.sh 5 | 6 | # Compiled files 7 | *.o 8 | imds-filterd/imds-filterd 9 | imds-proxy/imds-proxy -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | imds-filtered-0.1 2 | * Initial release for beta testing. 3 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | The included code and documentation ("imds-filterd") is distributed under the 2 | following terms: 3 | 4 | Copyright 2005-2020 Colin Percival. All rights reserved. 5 | Copyright 2005-2020 Tarsnap Backup Inc. All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 1. Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in the 14 | documentation and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 | SUCH DAMAGE. 27 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | 3 | PROGS= imds-filterd imds-proxy 4 | TESTS= 5 | BINDIR_DEFAULT= /usr/local/sbin 6 | CFLAGS_DEFAULT= -O2 7 | LIBCPERCIVA_DIR= libcperciva 8 | TEST_CMD= true 9 | 10 | ### Shared code between Tarsnap projects. 11 | 12 | all: cflags-filter.sh cpusupport-config.h posix-flags.sh 13 | export CFLAGS="$${CFLAGS:-${CFLAGS_DEFAULT}}"; \ 14 | . ./posix-flags.sh; \ 15 | . ./cpusupport-config.h; \ 16 | . ./cflags-filter.sh; \ 17 | export HAVE_BUILD_FLAGS=1; \ 18 | for D in ${PROGS} ${TESTS}; do \ 19 | ( cd $${D} && ${MAKE} all ) || exit 2; \ 20 | done 21 | 22 | # For "loop-back" building of a subdirectory 23 | buildsubdir: cflags-filter.sh cpusupport-config.h posix-flags.sh 24 | . ./posix-flags.sh; \ 25 | . ./cpusupport-config.h; \ 26 | . ./cflags-filter.sh; \ 27 | export HAVE_BUILD_FLAGS=1; \ 28 | cd ${BUILD_SUBDIR} && ${MAKE} ${BUILD_TARGET} 29 | 30 | posix-flags.sh: 31 | if [ -d ${LIBCPERCIVA_DIR}/POSIX/ ]; then \ 32 | export CC="${CC}"; \ 33 | cd ${LIBCPERCIVA_DIR}/POSIX; \ 34 | printf "export \"LDADD_POSIX="; \ 35 | command -p sh posix-l.sh "$$PATH"; \ 36 | printf "\"\n"; \ 37 | printf "export \"CFLAGS_POSIX="; \ 38 | command -p sh posix-cflags.sh "$$PATH"; \ 39 | printf "\"\n"; \ 40 | fi > $@ 41 | if [ ! -s $@ ]; then \ 42 | printf "#define POSIX_COMPATIBILITY_NOT_CHECKED 1\n"; \ 43 | fi >> $@ 44 | 45 | cflags-filter.sh: 46 | if [ -d ${LIBCPERCIVA_DIR}/POSIX/ ]; then \ 47 | export CC="${CC}"; \ 48 | cd ${LIBCPERCIVA_DIR}/POSIX; \ 49 | command -p sh posix-cflags-filter.sh "$$PATH"; \ 50 | fi > $@ 51 | if [ ! -s $@ ]; then \ 52 | printf "# Compiler understands normal flags; "; \ 53 | printf "nothing to filter out\n"; \ 54 | fi >> $@ 55 | 56 | cpusupport-config.h: 57 | if [ -d ${LIBCPERCIVA_DIR}/cpusupport/ ]; then \ 58 | export CC="${CC}"; \ 59 | command -p sh \ 60 | ${LIBCPERCIVA_DIR}/cpusupport/Build/cpusupport.sh \ 61 | "$$PATH"; \ 62 | fi > $@ 63 | if [ ! -s $@ ]; then \ 64 | printf "#define CPUSUPPORT_NONE 1\n"; \ 65 | fi >> $@ 66 | 67 | install: all 68 | export BINDIR=$${BINDIR:-${BINDIR_DEFAULT}}; \ 69 | for D in ${PROGS}; do \ 70 | ( cd $${D} && ${MAKE} install ) || exit 2; \ 71 | done 72 | 73 | clean: 74 | rm -f cflags-filter.sh cpusupport-config.h posix-flags.sh 75 | for D in ${PROGS} ${TESTS}; do \ 76 | ( cd $${D} && ${MAKE} clean ) || exit 2; \ 77 | done 78 | 79 | .PHONY: test test-clean 80 | test: all 81 | ${TEST_CMD} 82 | 83 | test-clean: 84 | rm -rf tests-output/ tests-valgrind/ 85 | 86 | # Developer targets: These only work with BSD make 87 | Makefiles: 88 | ${MAKE} -f Makefile.BSD Makefiles 89 | 90 | publish: 91 | ${MAKE} -f Makefile.BSD publish 92 | -------------------------------------------------------------------------------- /Makefile.BSD: -------------------------------------------------------------------------------- 1 | PKG= imds-filterd 2 | PROGS= imds-filterd imds-proxy 3 | TESTS= 4 | SUBST_VERSION_FILES= 5 | PUBLISH= ${PROGS} BUILDING CHANGELOG COPYRIGHT README.md STYLE Makefile libcperciva 6 | 7 | ### Shared code between Tarsnap projects. 8 | 9 | # These definitions improve the readability of the below material. 10 | MAKEBSD:= ${MAKE} -f Makefile.BSD 11 | RELEASEDATE!= date "+%B %d, %Y" 12 | CFLAGS_HARDCODED= -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\\\"cpusupport-config.h\\\" 13 | 14 | # This creates (and deletes) a fake cpusupport-config.h that is 15 | # blank (and thus does not require any special CFLAGS to compile). 16 | .for D in ${PROGS} ${TESTS} 17 | ${D}/Makefile:: 18 | CPP="${CPP}" ./release-tools/metabuild.sh \ 19 | "${D}" "${MAKEBSD}" "${CFLAGS_HARDCODED}" 20 | .endfor 21 | 22 | Makefiles: 23 | .for D in ${PROGS} ${TESTS} 24 | ${MAKEBSD} ${D}/Makefile 25 | .endfor 26 | 27 | # This uses temporary files for sed because the FreeBSD and GNU 28 | # behaviour of sed -i is different. 29 | publish: clean Makefiles 30 | if [ -z "${VERSION}" ]; then \ 31 | echo "VERSION must be specified!"; \ 32 | exit 1; \ 33 | fi 34 | if find . | grep \~; then \ 35 | echo "Delete temporary files before publishing!"; \ 36 | exit 1; \ 37 | fi 38 | rm -f ${PKG}-${VERSION}.tgz 39 | mkdir ${PKG}-${VERSION} 40 | tar -cf- --exclude 'Makefile.*' ${PUBLISH} | \ 41 | tar -xf- -C ${PKG}-${VERSION} 42 | .for F in ${SUBST_VERSION_FILES} 43 | sed -e 's/@VERSION@/${VERSION}/' -e 's/@DATE@/${RELEASEDATE}/' \ 44 | < ${PKG}-${VERSION}/${F} > ${PKG}-${VERSION}/${F}.tmp 45 | mv ${PKG}-${VERSION}/${F}.tmp ${PKG}-${VERSION}/${F} 46 | .endfor 47 | tar -cvzf ${PKG}-${VERSION}.tgz ${PKG}-${VERSION} 48 | rm -r ${PKG}-${VERSION} 49 | 50 | SUBDIR= ${PROGS} 51 | .include 52 | -------------------------------------------------------------------------------- /Makefile.inc: -------------------------------------------------------------------------------- 1 | # Used by Makefile code which generates POSIX Makefiles 2 | .for X in ${SRCS} 3 | source-${X:.c=.o}: ${X} 4 | @echo $> 5 | cflags-${X:.c=.o}: 6 | .endfor 7 | 8 | # Used to track the subdirectory depth (other directories append to this) 9 | SUBDIR_DEPTH = .. 10 | 11 | # Expand ${IDIRS} before it is used 12 | IDIRS := ${IDIRS} 13 | 14 | # Default is no man pages 15 | MAN = 16 | 17 | # Lines below this point are only relevant if running 18 | # make -f Makefile.BSD 19 | # in a subdirectory; they have no influence on the generated Makefiles. 20 | 21 | # Use POSIX standard 22 | CFLAGS += -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 23 | 24 | # Make logic 25 | CFLAGS += ${IDIRS} 26 | LDADD += ${LDADD_REQ} 27 | -------------------------------------------------------------------------------- /Makefile.prog: -------------------------------------------------------------------------------- 1 | 2 | all: 3 | if [ -z "$${HAVE_BUILD_FLAGS}" ]; then \ 4 | cd ${SUBDIR_DEPTH}; \ 5 | ${MAKE} BUILD_SUBDIR=${RELATIVE_DIR} \ 6 | BUILD_TARGET=${PROG} buildsubdir; \ 7 | else \ 8 | ${MAKE} ${PROG}; \ 9 | fi 10 | 11 | install:${PROG} 12 | mkdir -p ${BINDIR} 13 | cp ${PROG} ${BINDIR}/_inst.${PROG}.$$$$_ && \ 14 | strip ${BINDIR}/_inst.${PROG}.$$$$_ && \ 15 | chmod 0555 ${BINDIR}/_inst.${PROG}.$$$$_ && \ 16 | mv -f ${BINDIR}/_inst.${PROG}.$$$$_ ${BINDIR}/${PROG} 17 | if ! [ -z "${MAN1DIR}" ]; then \ 18 | mkdir -p ${MAN1DIR}; \ 19 | for MPAGE in ${MAN1}; do \ 20 | cp $$MPAGE ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ 21 | chmod 0444 ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ 22 | mv -f ${MAN1DIR}/_inst.$$MPAGE.$$$$_ ${MAN1DIR}/$$MPAGE; \ 23 | done; \ 24 | fi 25 | 26 | clean: 27 | rm -f ${PROG} ${SRCS:.c=.o} 28 | 29 | ${PROG}:${SRCS:.c=.o} 30 | ${CC} -o ${PROG} ${SRCS:.c=.o} ${LDFLAGS} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} 31 | 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | imds-filterd 2 | ============ 3 | 4 | **`imds-filterd`** (pronounced "I M D S Filter D") is a pair of utilities 5 | which work together to intercept and filter requests to the EC2 Instance 6 | Metadata Service -- or theoretically any other service at 169.254.169.254:80. 7 | 8 | It validates requests against a configured ruleset which specifies whether 9 | given users and groups should be allowed or denied access to certain prefixes 10 | in the Instance Metadata Service. For example, "root" could be granted 11 | access to everything; most unprivileged users granted access to everything 12 | except IAM role credentials; but the www user denied access to the entire 13 | Instance Metadata Service in order to guard against SSRF and similar attacks. 14 | 15 | At present this code only works on FreeBSD; we hope to support other 16 | platforms (e.g., Linux) in the future. (Send patches!) 17 | 18 | Code layout 19 | ----------- 20 | 21 | ``` 22 | imds-filterd/* -- Privileged code 23 | main.c -- Initialization and event loop 24 | netconfig.c -- Gathers information about the network configuration (e.g. 25 | where and how to access the IMDS). 26 | tunsetup.c -- Creates a virtualized environment and creates tunnels used 27 | to redirect packets in and out of it. 28 | packets.c -- Pushes packets in and out of the virtualized environment. 29 | conns.c -- Provides a mechanism for imds-proxy to connect to the IMDS. 30 | ident.c -- Provides an "ident" service used by imds-proxy. 31 | imds-proxy/* -- Unprivileged filtering HTTP proxy 32 | main.c -- Command line parsing, initialization, and connection 33 | acceptance. 34 | conf.c -- Reads the configuration and performs queries against it. 35 | http.c -- Handles an HTTP connection (possibly forwarding it). 36 | ident.c -- Uses imds-filterd to determine the source of a request. 37 | request.c -- Parses an HTTP request. 38 | uri2path.c -- Extracts and normalizes the path from a Request-URI. 39 | ``` 40 | -------------------------------------------------------------------------------- /STYLE: -------------------------------------------------------------------------------- 1 | Code style 2 | ========== 3 | 4 | In general, FreeBSD style(9) should be followed unless it is irrelevant 5 | (e.g., $FreeBSD$ tags). 6 | 7 | Functions with external linkage are declared like this: 8 | /** 9 | * module_func(arg1, arg2): 10 | * Description of what the function does, referring to arguments as 11 | * ${arg1} or suchlike. 12 | */ 13 | int module_func(void *, int); 14 | 15 | The identical comment appears in the C file where the function is defined. 16 | 17 | Static functions may have the above form of comment, or simply a 18 | /* Brief description of what the function does. */ 19 | line before the function. 20 | 21 | "Unrewrappable" comments starting in the first column should be 22 | /** 23 | * Written like this. 24 | * 25 | * Because (some of) the line-breaks are important. 26 | */ 27 | whereas when such comments are indented, they should be 28 | /*- 29 | * Written like this. 30 | * 31 | * Because (some of) the line-breaks are important. 32 | */ 33 | 34 | Line lengths should generally be 78 characters, and not more than 80 35 | characters. 36 | 37 | In general, functions should return (int)(-1) or NULL to indicate error. 38 | 39 | Errors should be printed via warnp (if errno is relevant) or warn0 (if errno 40 | is not relevant) when they are first detected and also at higher levels where 41 | useful. As an exception to this, malloc failures (i.e., errno = ENOMEM) can 42 | result in failure being passed back up the call chain without being printed 43 | immediately. (Naturally, other errors can be passed back where a function 44 | definition so specifies; e.g., ENOENT in cases where a file not existing is 45 | not erroneous.) 46 | 47 | The first statement in main(), after variable declarations, should be 48 | "WARNP_INIT;" in order to set the program name used for printing warnings. 49 | 50 | In general, functions should be structured with one return statement per 51 | status, e.g., one return() for success and one return() for failure. Errors 52 | should be handled by using goto to enter the error return path, e.g., 53 | int 54 | foo(int bar) 55 | { 56 | 57 | if (something fails) 58 | goto err0; 59 | /* ... */ 60 | if (something else fails) 61 | goto err1; 62 | /* ... */ 63 | if (yet another operation fails) 64 | goto err2; 65 | 66 | /* Success! */ 67 | return (0); 68 | 69 | err2: 70 | /* Clean up something. */ 71 | err1: 72 | /* Clean up something else. */ 73 | err0: 74 | /* Failure! */ 75 | return (-1); 76 | } 77 | 78 | As an exception to the above, if there is only one way for the function to 79 | fail, the idioms 80 | return (baz(bar)); 81 | and 82 | int rc; 83 | 84 | rc = baz(bar); 85 | /* ... cleanup code here ... */ 86 | return (rc); 87 | are allowed; furthermore, in cases such as foo_free(), the idiom 88 | if (we shouldn't do anything) 89 | return; 90 | is preferred over 91 | if (we shouldn't do anything) 92 | goto done; 93 | at the start of a function. 94 | 95 | Headers should be included in the following groups, with a blank line after 96 | each (non-empty) group: 97 | 1. , with first followed by others alphabetically. 98 | 2. , in alphabetical order. 99 | 3. <*.h>, in alphabetical order. 100 | 4. header files from /lib/, in alphabetical order. 101 | 5. header files from the program being built, in alphabetical order. 102 | 6. header files (usually just one) defining the interface for this C file. 103 | 104 | If ssize_t is needed, should be included to provide it. 105 | 106 | If size_t is needed, should be included to provide it unless 107 | , , , or is already required. 108 | 109 | If the C99 integer types (uint8_t, int64_t, etc.) are required, 110 | should be included to provide them unless is already required. 111 | 112 | The type 'char' should only be used to represent human-readable characters 113 | (input from users, output to users, pathnames, et cetera). The type 114 | 'char *' should normally be a NUL-terminated string. The types 'signed 115 | char' and 'unsigned char' should never be used; C99 integer types should 116 | be used instead. 117 | 118 | When a variable is declared to have a pointer type, there should be a space 119 | between the '*' and the variable name, e.g., 120 | int 121 | main(int argc, char * argv[]) 122 | { 123 | char * opt_p = NULL; 124 | Note that this is inconsistent with FreeBSD style(9). When used as a unary 125 | operator, '*' is not separated from its argument, e.g., 126 | while (*p != '\0') 127 | p++; 128 | 129 | When a struct is referenced, the idiom 130 | /* Opaque types. */ 131 | struct foo; 132 | 133 | struct bar * bar_from_foo(struct foo *); 134 | is preferable to 135 | #include "foo.h" /* needed for struct foo */ 136 | 137 | struct bar * bar_from_foo(struct foo *); 138 | unless there is some reason why the internal layout of struct foo is needed 139 | (e.g., if struct bar contains a struct foo rather than a struct foo *). Such 140 | struct declarations should be sorted alphabetically. 141 | 142 | The file foo.c should only export symbols of the following forms: 143 | foo_* -- most symbols should be of this form. 144 | FOO_* / BAR_FOO_* 145 | -- allowed in cases where FOO or BAR_FOO is idiomatic (e.g., 146 | MD5, HMAC_SHA256). 147 | foo() / defoo() / unfoo() 148 | -- where "foo" is a verb and this improves code clarity. 149 | 150 | Functions named foo_free should return void, and foo_free(NULL) should have 151 | no effect. The right way to spell a comment about this is 152 | /* Behave consistently with free(NULL). */ 153 | 154 | If static variables need to be initialized to 0 (or NULL) then they should be 155 | explicitly declared that way; implicit initialization should not be used. 156 | 157 | In non-trivial code, comments should be included which describe in English 158 | what is being done by the surrounding code with sufficient detail that if the 159 | code were removed, it could be replaced based on reading the comments without 160 | requiring any significant creativity. 161 | 162 | Comments and documentation should be written in en-GB-oed; i.e., with 163 | the 'u' included in words such as "honour", "colour", and "neighbour", 164 | and the ending '-ize' in words such as "organize" and "realize". The 165 | Oxford (aka. serial) comma should be used in lists. Quotation marks 166 | should be placed logically, i.e., not including punctuation marks which 167 | do not form a logical part of the quoted text. Two spaces should be used 168 | after a period which ends a sentence. 169 | 170 | The first local variable declaration in cookie-using functions should be 171 | struct foo * bar = cookie; 172 | 173 | When versions of functions are written to exploit special CPU features 174 | (using the cpusupport framework), that code should be placed into a 175 | separate file (e.g., crypto_aes_aesni.c) so that it can be compiled with 176 | different compiler flags. Such a file should start with 177 | #include "cpusupport.h" 178 | #ifdef CPUSUPPORT_FOO_BAR 179 | and end with 180 | #endif /* CPUSUPPORT_FOO_BAR */ 181 | 182 | Functions for which special CPU-feature-exploiting variants exist should 183 | take the form 184 | { 185 | /* Variable declarations here. */ 186 | 187 | /* Asserts here, if any. */ 188 | 189 | #ifdef CPUSUPPORT_FOO_BAR 190 | if (/* We've decided we can use the variant code */) { 191 | /* Call variant code and return. */ 192 | } 193 | #endif 194 | 195 | /* Normal implementation of the function. */ 196 | } 197 | -------------------------------------------------------------------------------- /USAGE: -------------------------------------------------------------------------------- 1 | Installing 2 | ---------- 3 | 4 | This code should build and install on FreeBSD via "make all install". 5 | 6 | Configuration 7 | ------------- 8 | 9 | This code ships with several configuration files: 10 | 11 | imds.conf - Sample configuration for imds-proxy 12 | freebsd-conf/newsyslog-imds.conf - newsyslog conf for /var/log/imds.log 13 | freebsd-conf/syslog-imds.conf - syslog conf for /var/log/imds.log 14 | freebsd-conf/rc.d-imds-{filterd, proxy} - FreeBSD rc.d scripts. 15 | 16 | In addition, the user and group imds:imds must be created in order for the 17 | rc.d scripts to function. 18 | 19 | Using the FreeBSD port is recommended since it will automate dispersing the 20 | configuration files and creating the user and group. 21 | 22 | Usage 23 | ----- 24 | 25 | On FreeBSD: 26 | 1. Install the "security/imds-filterd" port or the "imds-filterd" package. 27 | 2. Add imds_filterd_enable=YES to /etc/rc.conf. 28 | 3. Edit imds.conf as desired. 29 | 4. Reboot, or start daemons: 30 | service imds-filterd start 31 | service imds-proxy start 32 | -------------------------------------------------------------------------------- /freebsd-conf/newsyslog-imds.conf: -------------------------------------------------------------------------------- 1 | /var/log/imds.log 640 10 1000 * JC 2 | -------------------------------------------------------------------------------- /freebsd-conf/rc.d-imds-filterd: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # PROVIDE: imds-filterd 4 | # REQUIRE: NETWORKING 5 | # BEFORE: SERVERS 6 | # KEYWORD: shutdown 7 | # 8 | # Add the following lines to /etc/rc.conf.local or /etc/rc.conf 9 | # to enable imds-filterd: 10 | # 11 | # imds_filterd_enable: Set to YES to enable imds-filterd. 12 | # 13 | # Note that the same variable is used for the imds-proxy rc.d script. 14 | 15 | . /etc/rc.subr 16 | 17 | name="imds_filterd" 18 | rcvar=${name}_enable 19 | : ${imds_filterd_enable=NO} 20 | command=/usr/local/sbin/imds-filterd 21 | pidfile="/var/run/imds-filterd.pid" 22 | start_precmd="${name}_prestart" 23 | start_postcmd="${name}_poststart" 24 | proxyuser="imds:imds" 25 | 26 | imds_filterd_prestart() 27 | { 28 | if [ "`sysctl -qn kern.features.vimage`" != "1" ]; then 29 | warn "imds-filterd requires a kernel with VIMAGE support" 30 | return 1 31 | fi 32 | # Make sure that the gateway to the IMDS is in the ARP cache. 33 | route -n get 169.254.169.254 | 34 | grep -E '^ +gateway:' | 35 | cut -f 2 -d : | 36 | xargs ping -c 1 >/dev/null 37 | } 38 | 39 | imds_filterd_poststart() 40 | { 41 | chown ${proxyuser} /var/run/imds.sock /var/run/imds-ident.sock 42 | chmod 600 /var/run/imds.sock /var/run/imds-ident.sock 43 | } 44 | 45 | load_rc_config $name 46 | run_rc_command "$1" 47 | -------------------------------------------------------------------------------- /freebsd-conf/rc.d-imds-proxy: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # PROVIDE: imds-proxy 4 | # REQUIRE: imds-filterd 5 | # BEFORE: SERVERS 6 | # KEYWORD: shutdown 7 | # 8 | # Add the following lines to /etc/rc.conf.local or /etc/rc.conf 9 | # to enable imds-proxy: 10 | # 11 | # imds_filterd_enable: Set to YES to enable imds-proxy. 12 | # 13 | # Note that the same variable is used for the imds-filterd rc.d script. 14 | 15 | . /etc/rc.subr 16 | 17 | name="imds_proxy" 18 | rcvar=imds_filterd_enable 19 | : ${imds_filterd_enable=NO} 20 | command=/usr/local/sbin/imds-proxy 21 | pidfile="/var/run/imds-proxy.pid" 22 | command_args="-f /usr/local/etc/imds.conf -p ${pidfile} -u imds:imds" 23 | start_cmd=imds_proxy_start 24 | stop_cmd=imds_proxy_stop 25 | 26 | imds_proxy_start() { 27 | echo "Starting ${name}." 28 | jexec imds ${command} ${command_args} 29 | } 30 | imds_proxy_stop() { 31 | # We can't use the normal check_pidfile logic since it won't look 32 | # inside the imds jail. Just assume that the pidfile is correct. 33 | if ! [ -f ${pidfile} ]; then 34 | echo "${name} is not running?" 35 | return 1 36 | fi 37 | echo "Stopping ${name}." 38 | rc_pid=`cat ${pidfile}` 39 | kill -TERM $rc_pid 40 | wait_for_pids $rc_pid 41 | rm ${pidfile} 42 | } 43 | 44 | load_rc_config $name 45 | run_rc_command "$1" 46 | -------------------------------------------------------------------------------- /freebsd-conf/syslog-imds.conf: -------------------------------------------------------------------------------- 1 | !imds-proxy 2 | *.* /var/log/imds.log 3 | -------------------------------------------------------------------------------- /imds-filterd/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | # AUTOGENERATED FILE, DO NOT EDIT 3 | PROG=imds-filterd 4 | SRCS=main.c netconfig.c tunsetup.c packets.c conns.c ident.c elasticarray.c ptrheap.c timerqueue.c events.c events_immediate.c events_network.c events_network_selectstats.c events_timer.c network_accept.c network_read.c network_write.c asprintf.c daemonize.c monoclock.c noeintr.c sock.c warnp.c 5 | IDIRS=-I ../libcperciva/datastruct -I ../libcperciva/events -I ../libcperciva/network -I ../libcperciva/util 6 | LDADD_REQ=-ljail 7 | SUBDIR_DEPTH=.. 8 | RELATIVE_DIR=imds-filterd 9 | 10 | all: 11 | if [ -z "$${HAVE_BUILD_FLAGS}" ]; then \ 12 | cd ${SUBDIR_DEPTH}; \ 13 | ${MAKE} BUILD_SUBDIR=${RELATIVE_DIR} \ 14 | BUILD_TARGET=${PROG} buildsubdir; \ 15 | else \ 16 | ${MAKE} ${PROG}; \ 17 | fi 18 | 19 | install:${PROG} 20 | mkdir -p ${BINDIR} 21 | cp ${PROG} ${BINDIR}/_inst.${PROG}.$$$$_ && \ 22 | strip ${BINDIR}/_inst.${PROG}.$$$$_ && \ 23 | chmod 0555 ${BINDIR}/_inst.${PROG}.$$$$_ && \ 24 | mv -f ${BINDIR}/_inst.${PROG}.$$$$_ ${BINDIR}/${PROG} 25 | if ! [ -z "${MAN1DIR}" ]; then \ 26 | mkdir -p ${MAN1DIR}; \ 27 | for MPAGE in ${MAN1}; do \ 28 | cp $$MPAGE ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ 29 | chmod 0444 ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ 30 | mv -f ${MAN1DIR}/_inst.$$MPAGE.$$$$_ ${MAN1DIR}/$$MPAGE; \ 31 | done; \ 32 | fi 33 | 34 | clean: 35 | rm -f ${PROG} ${SRCS:.c=.o} 36 | 37 | ${PROG}:${SRCS:.c=.o} 38 | ${CC} -o ${PROG} ${SRCS:.c=.o} ${LDFLAGS} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} 39 | 40 | main.o: main.c ../libcperciva/util/daemonize.h ../libcperciva/events/events.h ../libcperciva/util/warnp.h imds-filterd.h 41 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c main.c -o main.o 42 | netconfig.o: netconfig.c ../libcperciva/util/warnp.h imds-filterd.h 43 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c netconfig.c -o netconfig.o 44 | tunsetup.o: tunsetup.c ../libcperciva/util/asprintf.h ../libcperciva/util/warnp.h imds-filterd.h 45 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c tunsetup.c -o tunsetup.o 46 | packets.o: packets.c ../libcperciva/util/asprintf.h ../libcperciva/events/events.h ../libcperciva/util/warnp.h imds-filterd.h 47 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c packets.c -o packets.o 48 | conns.o: conns.c ../libcperciva/datastruct/elasticarray.h ../libcperciva/events/events.h ../libcperciva/network/network.h ../libcperciva/util/sock.h ../libcperciva/util/warnp.h imds-filterd.h 49 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c conns.c -o conns.o 50 | ident.o: ident.c ../libcperciva/network/network.h ../libcperciva/util/sock.h ../libcperciva/util/warnp.h imds-filterd.h 51 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ident.c -o ident.o 52 | elasticarray.o: ../libcperciva/datastruct/elasticarray.c ../libcperciva/datastruct/elasticarray.h 53 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/datastruct/elasticarray.c -o elasticarray.o 54 | ptrheap.o: ../libcperciva/datastruct/ptrheap.c ../libcperciva/datastruct/elasticarray.h ../libcperciva/datastruct/ptrheap.h 55 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/datastruct/ptrheap.c -o ptrheap.o 56 | timerqueue.o: ../libcperciva/datastruct/timerqueue.c ../libcperciva/datastruct/ptrheap.h ../libcperciva/datastruct/timerqueue.h 57 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/datastruct/timerqueue.c -o timerqueue.o 58 | events.o: ../libcperciva/events/events.c ../libcperciva/datastruct/mpool.h ../libcperciva/events/events.h ../libcperciva/events/events_internal.h 59 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/events/events.c -o events.o 60 | events_immediate.o: ../libcperciva/events/events_immediate.c ../libcperciva/datastruct/mpool.h ../libcperciva/events/events.h ../libcperciva/events/events_internal.h 61 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/events/events_immediate.c -o events_immediate.o 62 | 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 63 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/events/events_network.c -o events_network.o 64 | events_network_selectstats.o: ../libcperciva/events/events_network_selectstats.c ../libcperciva/util/monoclock.h ../libcperciva/events/events.h ../libcperciva/events/events_internal.h 65 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/events/events_network_selectstats.c -o events_network_selectstats.o 66 | 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 67 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/events/events_timer.c -o events_timer.o 68 | network_accept.o: ../libcperciva/network/network_accept.c ../libcperciva/events/events.h ../libcperciva/network/network.h 69 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/network/network_accept.c -o network_accept.o 70 | network_read.o: ../libcperciva/network/network_read.c ../libcperciva/events/events.h ../libcperciva/datastruct/mpool.h ../libcperciva/network/network.h 71 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/network/network_read.c -o network_read.o 72 | network_write.o: ../libcperciva/network/network_write.c ../libcperciva/events/events.h ../libcperciva/datastruct/mpool.h ../libcperciva/util/warnp.h ../libcperciva/network/network.h 73 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/network/network_write.c -o network_write.o 74 | asprintf.o: ../libcperciva/util/asprintf.c ../libcperciva/util/asprintf.h 75 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/asprintf.c -o asprintf.o 76 | daemonize.o: ../libcperciva/util/daemonize.c ../libcperciva/util/noeintr.h ../libcperciva/util/warnp.h ../libcperciva/util/daemonize.h 77 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/daemonize.c -o daemonize.o 78 | monoclock.o: ../libcperciva/util/monoclock.c ../libcperciva/util/warnp.h ../libcperciva/util/monoclock.h 79 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/monoclock.c -o monoclock.o 80 | noeintr.o: ../libcperciva/util/noeintr.c ../libcperciva/util/noeintr.h 81 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/noeintr.c -o noeintr.o 82 | sock.o: ../libcperciva/util/sock.c ../libcperciva/util/imalloc.h ../libcperciva/util/parsenum.h ../libcperciva/util/warnp.h ../libcperciva/util/sock.h ../libcperciva/util/sock_internal.h 83 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/sock.c -o sock.o 84 | warnp.o: ../libcperciva/util/warnp.c ../libcperciva/util/warnp.h 85 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/warnp.c -o warnp.o 86 | -------------------------------------------------------------------------------- /imds-filterd/Makefile.BSD: -------------------------------------------------------------------------------- 1 | PROG= imds-filterd 2 | MAN1= 3 | 4 | # Library code required 5 | LDADD_REQ= -ljail 6 | 7 | # Useful relative directory 8 | LIBCPERCIVA_DIR = ../libcperciva 9 | 10 | # imds-filterd code 11 | SRCS = main.c 12 | SRCS += netconfig.c 13 | SRCS += tunsetup.c 14 | SRCS += packets.c 15 | SRCS += conns.c 16 | SRCS += ident.c 17 | 18 | # Data structures 19 | .PATH.c : ${LIBCPERCIVA_DIR}/datastruct 20 | SRCS += elasticarray.c 21 | SRCS += ptrheap.c 22 | SRCS += timerqueue.c 23 | IDIRS += -I ${LIBCPERCIVA_DIR}/datastruct 24 | 25 | # Event loop 26 | .PATH.c : ${LIBCPERCIVA_DIR}/events 27 | SRCS += events.c 28 | SRCS += events_immediate.c 29 | SRCS += events_network.c 30 | SRCS += events_network_selectstats.c 31 | SRCS += events_timer.c 32 | IDIRS += -I ${LIBCPERCIVA_DIR}/events 33 | 34 | # Event-driven networking 35 | .PATH.c : ${LIBCPERCIVA_DIR}/network 36 | SRCS += network_accept.c 37 | SRCS += network_read.c 38 | SRCS += network_write.c 39 | IDIRS += -I ${LIBCPERCIVA_DIR}/network 40 | 41 | # Utility functions 42 | .PATH.c : ${LIBCPERCIVA_DIR}/util 43 | SRCS += asprintf.c 44 | SRCS += daemonize.c 45 | SRCS += monoclock.c 46 | SRCS += noeintr.c 47 | SRCS += sock.c 48 | SRCS += warnp.c 49 | IDIRS += -I ${LIBCPERCIVA_DIR}/util 50 | 51 | .include 52 | -------------------------------------------------------------------------------- /imds-filterd/ident.c: -------------------------------------------------------------------------------- 1 | #define __BSD_VISIBLE 1 /* Needed for sys/ucred.h header. */ 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "network.h" 15 | #include "sock.h" 16 | #include "warnp.h" 17 | 18 | #include "imds-filterd.h" 19 | 20 | /* State for connection accepting. */ 21 | struct astate { 22 | int s; 23 | }; 24 | 25 | /* State for a single connection. */ 26 | struct cstate { 27 | int s; 28 | uint8_t inbuf[12]; 29 | char outbuf[sizeof(uid_t) * 3 + 1 + XU_NGROUPS * (sizeof(gid_t) * 3 + 1) + 1]; 30 | size_t olen; 31 | }; 32 | 33 | /* We have sent a response. */ 34 | static int 35 | sentdata(void * cookie, ssize_t len) 36 | { 37 | struct cstate * cs = cookie; 38 | 39 | /* Don't care if we succeeded. */ 40 | (void)len; /* UNUSED */ 41 | 42 | /* Clean up the connection. */ 43 | close(cs->s); 44 | free(cs); 45 | 46 | /* Success! */ 47 | return (0); 48 | } 49 | 50 | /* We have data from the client. */ 51 | static int 52 | gotdata(void * cookie, ssize_t len) 53 | { 54 | struct cstate * cs = cookie; 55 | struct sockaddr_in addrs[2]; 56 | struct xucred uc; 57 | int printlen; 58 | size_t size = sizeof(struct xucred); 59 | size_t i; 60 | 61 | /* Did the read succeed? */ 62 | if ((len == -1) || (len == 0)) 63 | goto drop; 64 | 65 | /* Parse the query. */ 66 | addrs[0].sin_len = sizeof(struct sockaddr_in); 67 | addrs[0].sin_family = AF_INET; 68 | memcpy(&addrs[0].sin_addr, &cs->inbuf[0], 4); 69 | memcpy(&addrs[0].sin_port, &cs->inbuf[4], 2); 70 | addrs[1].sin_len = sizeof(struct sockaddr_in); 71 | addrs[1].sin_family = AF_INET; 72 | memcpy(&addrs[1].sin_addr, &cs->inbuf[6], 4); 73 | memcpy(&addrs[1].sin_port, &cs->inbuf[10], 2); 74 | 75 | /* Ask the kernel who owns this TCP connection. */ 76 | if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, 77 | addrs, sizeof(addrs))) { 78 | /* Not fatal; we might have lost a race against a close. */ 79 | warnp("sysctlbyname"); 80 | goto drop; 81 | } 82 | 83 | /* Sanity-check. */ 84 | assert(uc.cr_ngroups <= XU_NGROUPS); 85 | 86 | /* Construct and send a response. */ 87 | if ((printlen = sprintf(cs->outbuf, "%u\n", 88 | (unsigned int)uc.cr_uid)) < 0) { 89 | warnp("sprintf"); 90 | goto drop; 91 | } 92 | cs->olen = (size_t)printlen; 93 | for (i = 0; i < (size_t)uc.cr_ngroups; i++) { 94 | if ((printlen = sprintf(&cs->outbuf[cs->olen], "%u,", 95 | (unsigned int)uc.cr_groups[i])) < 0) { 96 | warnp("sprintf"); 97 | goto drop; 98 | } 99 | cs->olen += (size_t)printlen; 100 | } 101 | cs->outbuf[cs->olen - 1] = '\n'; 102 | if (network_write(cs->s, (uint8_t *)cs->outbuf, cs->olen, cs->olen, 103 | sentdata, cs) == NULL) { 104 | warnp("network_write"); 105 | goto drop; 106 | } 107 | 108 | /* Success! */ 109 | return (0); 110 | 111 | drop: 112 | close(cs->s); 113 | free(cs); 114 | return (0); 115 | } 116 | 117 | /* A connection has arrived. */ 118 | static int 119 | gotconn(void * cookie, int s) 120 | { 121 | struct astate * as = cookie; 122 | struct cstate * cs; 123 | 124 | /* If we got a -1 descriptor, something went seriously wrong. */ 125 | if (s == -1) { 126 | warnp("network_accept"); 127 | goto err0; 128 | } 129 | 130 | /* Allocate a state structure. */ 131 | if ((cs = malloc(sizeof(struct cstate))) == NULL) 132 | goto err1; 133 | 134 | /* Record the incoming connection. */ 135 | cs->s = s; 136 | 137 | /* Read the TCP source and destination IP addresses and ports. */ 138 | if (network_read(cs->s, cs->inbuf, 12, 12, gotdata, cs) == NULL) { 139 | warnp("network_read"); 140 | goto err2; 141 | } 142 | 143 | /* Accept more connections. */ 144 | if (network_accept(as->s, gotconn, as) == NULL) { 145 | warnp("network_accept"); 146 | goto err0; 147 | } 148 | 149 | /* Success! */ 150 | return (0); 151 | 152 | err2: 153 | free(cs); 154 | err1: 155 | close(s); 156 | err0: 157 | /* Failure! */ 158 | return (-1); 159 | } 160 | 161 | /** 162 | * ident_setup(path): 163 | * Create a socke at ${path}. Receive connections and read 12 bytes 164 | * [4 byte src IP][2 byte src port][4 byte dst IP][2 byte dst port] 165 | * (in network byte order) then write back "uid\ngid[,gid]*\n". 166 | */ 167 | int 168 | ident_setup(const char * path) 169 | { 170 | struct sock_addr ** sas_s; 171 | struct astate * as; 172 | 173 | /* Allocate a state structure. */ 174 | if ((as = malloc(sizeof(struct astate))) == NULL) 175 | goto err0; 176 | 177 | /* Resolve the listening path and target address. */ 178 | if ((sas_s = sock_resolve(path)) == NULL) { 179 | warnp("sock_resolve"); 180 | goto err1; 181 | } 182 | 183 | /* Listen for incoming connections. */ 184 | if ((as->s = sock_listener(sas_s[0])) == -1) { 185 | warnp("sock_listener"); 186 | goto err2; 187 | } 188 | if (network_accept(as->s, gotconn, as) == NULL) { 189 | warnp("network_accept"); 190 | goto err3; 191 | } 192 | 193 | /* Free the source addresses; we don't need them any more. */ 194 | sock_addr_freelist(sas_s); 195 | 196 | /* Success! */ 197 | return (0); 198 | 199 | err3: 200 | close(as->s); 201 | err2: 202 | sock_addr_freelist(sas_s); 203 | err1: 204 | free(as); 205 | err0: 206 | /* Failure! */ 207 | return (-1); 208 | } 209 | -------------------------------------------------------------------------------- /imds-filterd/imds-filterd.h: -------------------------------------------------------------------------------- 1 | #ifndef IMDS_FILTER_H 2 | #define IMDS_FILTER_H 3 | 4 | #include 5 | 6 | /** 7 | * netconfig_getif(srcaddr, gwaddr, host, ifname): 8 | * Find the IPv4 route used for sending packets to ${host}; return via 9 | * ${ifname}, ${gwaddr}, and ${srcaddr} the name of the network interface, 10 | * the gateway, and the appropriate source IPv4 address. 11 | */ 12 | int netconfig_getif(struct sockaddr_in *, struct sockaddr_in *, 13 | in_addr_t, char **); 14 | 15 | /* 16 | * netconfig_getmac(host, mac): 17 | * Look up the MAC address associated with the IPv4 address ${host} and 18 | * return it via ${mac}. Note that this can fail if ${host} is not in the 19 | * operating system's ARP cache. 20 | */ 21 | int netconfig_getmac(struct sockaddr_in *, uint8_t[6]); 22 | 23 | /** 24 | * makejail(name, jid): 25 | * Create a jail with name and hostname ${name} which is persistent and has 26 | * its own virtualized network stack. Return the jail id via ${jid}. 27 | */ 28 | int makejail(const char *, int *); 29 | 30 | /** 31 | * rmjail(jid): 32 | * Remove the jail with the jail ID ${jid}. 33 | */ 34 | int rmjail(int); 35 | 36 | /** 37 | * tunsetup(tunin, tunout, srcaddr, dstaddr, jid): 38 | * Set up a pair of tunnels: 39 | * 1. From ${srcaddr} to ${dstaddr}, named "imds-tun", with file descriptor 40 | * returned via ${tunin}; and 41 | * 2. From ${dstaddr} to ${srcaddr}, named "imds-tunout", placed inside jail 42 | * ID ${jid}, with file descriptor returned via ${tunout}. 43 | */ 44 | int tunsetup(int *, int *, struct sockaddr_in *, struct sockaddr_in *, int); 45 | 46 | /** 47 | * tuncleanup(tunin, tunout, jid): 48 | * Clean up the work done by tunsetup. 49 | */ 50 | void tuncleanup(int, int, int); 51 | 52 | /** 53 | * outpath(tunin, tunout, dstaddr, ifname, srcmac, gwmac): 54 | * Read packets from ${tunin} and either write them to ${tunout} or wrap them 55 | * into ethernet frames and send them via ${ifname}. 56 | */ 57 | int outpath(int, int, struct sockaddr_in *, const char *, 58 | uint8_t[6], uint8_t[6]); 59 | 60 | /** 61 | * inpath(tunin, tunout): 62 | * Read packets from ${tunout} and write them to ${tunin}. 63 | */ 64 | int inpath(int, int); 65 | 66 | /** 67 | * conns_setup(path, dstaddr): 68 | * Create a socket at ${path}. Forward data between incoming connections and 69 | * TCP connection to ${dstaddr}. 70 | */ 71 | int conns_setup(const char *, const char *); 72 | 73 | /** 74 | * conns_isours(srcaddr, srcport): 75 | * Return nonzero if one of our connections to the target has source address 76 | * srcaddr:srcport. 77 | */ 78 | int conns_isours(in_addr_t, uint16_t); 79 | 80 | /** 81 | * ident_setup(path): 82 | * Create a socke at ${path}. Receive connections and read 12 bytes 83 | * [4 byte src IP][2 byte src port][4 byte dst IP][2 byte dst port] 84 | * (in network byte order) then write back "uid\ngid[,gid]*\n". 85 | */ 86 | int ident_setup(const char *); 87 | 88 | #endif /* !IMDS_FILTER_H */ 89 | -------------------------------------------------------------------------------- /imds-filterd/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "daemonize.h" 13 | #include "events.h" 14 | #include "warnp.h" 15 | 16 | #include "imds-filterd.h" 17 | 18 | #define IMDSIP "169.254.169.254" 19 | 20 | static sig_atomic_t got_sigterm = 0; 21 | 22 | static void 23 | sigterm_handler(int signo) 24 | { 25 | 26 | (void)signo; /* UNUSED */ 27 | 28 | /* We've received a signal. */ 29 | got_sigterm = 1; 30 | 31 | /* Stop handling events. */ 32 | events_interrupt(); 33 | } 34 | 35 | int 36 | main(int argc, char * argv[]) 37 | { 38 | char * ifname; 39 | struct sockaddr_in srcaddr; 40 | struct sockaddr_in gwaddr; 41 | struct sockaddr_in dstaddr; 42 | uint8_t srcmac[6]; 43 | uint8_t gwmac[6]; 44 | int jid; 45 | int tunin; 46 | int tunout; 47 | 48 | (void)argc; /* UNUSED */ 49 | (void)argv; /* UNUSED */ 50 | 51 | WARNP_INIT; 52 | 53 | /* Construct a sockaddr for the IMDS address. */ 54 | memset(&dstaddr, 0, sizeof(dstaddr)); 55 | dstaddr.sin_len = sizeof(dstaddr); 56 | dstaddr.sin_family = AF_INET; 57 | dstaddr.sin_addr.s_addr = inet_addr(IMDSIP); 58 | dstaddr.sin_port = htons(80); 59 | 60 | /* 61 | * Look up the interface and associated local address to be used for 62 | * making connections to the Instance Metadata Service. 63 | */ 64 | if (netconfig_getif(&srcaddr, &gwaddr, dstaddr.sin_addr.s_addr, 65 | &ifname)) { 66 | warnp("Could not find route to IMDS"); 67 | goto err0; 68 | } 69 | 70 | /* 71 | * Look up the MAC addresses for our external interface and for the 72 | * gateway we use for accessing the Instance Metadata Service. 73 | */ 74 | if (netconfig_getmac(&srcaddr, srcmac)) { 75 | warnp("Could not look up MAC address for interface"); 76 | goto err0; 77 | } 78 | if (netconfig_getmac(&gwaddr, gwmac)) { 79 | warnp("Could not look up MAC address for gateway"); 80 | goto err0; 81 | } 82 | 83 | /* Create a jail for the IMDS filtering proxy. */ 84 | if (makejail("imds", &jid)) { 85 | warnp("Failed to create jail"); 86 | goto err0; 87 | } 88 | 89 | /* Create tunnels in and out of the jail. */ 90 | if (tunsetup(&tunin, &tunout, &srcaddr, &dstaddr, jid)) { 91 | warnp("Failed to set up tunnel devices"); 92 | goto err1; 93 | } 94 | 95 | /* 96 | * Read packets destined for the Instance Metadata Service and either 97 | * forward them into the jail or pass them out the network interface. 98 | */ 99 | if (outpath(tunin, tunout, &dstaddr, ifname, srcmac, gwmac)) { 100 | warnp("Failed to set up packet forwarding"); 101 | goto err2; 102 | } 103 | 104 | /* Read packets coming out of the jail and pass them to the host. */ 105 | if (inpath(tunin, tunout)) { 106 | warnp("Failed to set up packet forwarding"); 107 | goto err2; 108 | } 109 | 110 | /* Accept connections from the proxy and forward them out. */ 111 | if (conns_setup("/var/run/imds.sock", "[" IMDSIP "]:80")) { 112 | warnp("Failed to set up connection forwarding"); 113 | goto err2; 114 | } 115 | 116 | /* Answer TCP connection ownership queries. */ 117 | if (ident_setup("/var/run/imds-ident.sock")) { 118 | warnp("Failed to set up connection identification"); 119 | goto err3; 120 | } 121 | 122 | /* 123 | * Catch SIGTERM; this allows us to clean up our tunnels and jail 124 | * if the user wants us to stop running. 125 | */ 126 | if (signal(SIGTERM, sigterm_handler) == SIG_ERR) { 127 | warnp("signal(SIGTERM)"); 128 | goto err4; 129 | } 130 | 131 | /* Daemonize. */ 132 | if (daemonize("/var/run/imds-filterd.pid")) { 133 | warnp("daemonize"); 134 | goto err4; 135 | } 136 | 137 | /* Loop until an error occurs or we get SIGTERM. */ 138 | while (got_sigterm == 0) { 139 | if (events_run()) { 140 | warnp("Error in event loop"); 141 | break; 142 | } 143 | } 144 | 145 | /* Clean up the pidfile, sockets, tunnels and jail. */ 146 | unlink("/var/run/imds-filterd.pid"); 147 | unlink("/var/run/imds-ident.sock"); 148 | unlink("/var/run/imds.sock"); 149 | tuncleanup(tunin, tunout, jid); 150 | rmjail(jid); 151 | 152 | exit(0); 153 | 154 | err4: 155 | unlink("/var/run/imds-ident.sock"); 156 | err3: 157 | unlink("/var/run/imds.sock"); 158 | err2: 159 | tuncleanup(tunin, tunout, jid); 160 | err1: 161 | rmjail(jid); 162 | err0: 163 | exit(1); 164 | } 165 | -------------------------------------------------------------------------------- /imds-filterd/netconfig.c: -------------------------------------------------------------------------------- 1 | #define __BSD_VISIBLE 1 /* Needed for net/ headers. */ 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "warnp.h" 19 | 20 | #include "imds-filterd.h" 21 | 22 | static int 23 | sysctldump(int mib[], size_t miblen, char ** buf, size_t * len) 24 | { 25 | 26 | /* Sanity checks. */ 27 | assert(miblen <= UINT_MAX); 28 | 29 | /* Loop until we manage to dump the table. */ 30 | while (1) { 31 | /* How large a buffer do we need? */ 32 | if (sysctl(mib, (u_int)miblen, NULL, len, NULL, 0)) { 33 | warnp("sysctl"); 34 | goto err0; 35 | } 36 | 37 | /* Allocate a buffer based on the size the kernel reported. */ 38 | if ((*buf = malloc(*len)) == NULL) { 39 | warnp("malloc"); 40 | goto err0; 41 | } 42 | 43 | /* Try to dump the table. */ 44 | if (sysctl(mib, (u_int)miblen, *buf, len, NULL, 0)) { 45 | if (errno == ENOMEM) { 46 | /* 47 | * The table we're dumping must have grown; 48 | * free our buffer and start over. 49 | */ 50 | free(*buf); 51 | continue; 52 | } 53 | warnp("sysctl"); 54 | goto err1; 55 | } 56 | 57 | /* It worked this time. */ 58 | break; 59 | } 60 | 61 | /* Success! */ 62 | return (0); 63 | 64 | err1: 65 | free(*buf); 66 | err0: 67 | /* Failure! */ 68 | return (-1); 69 | } 70 | 71 | static int 72 | extractaddrs(char * p, struct sockaddr * sas[RTAX_MAX]) 73 | { 74 | struct rt_msghdr * rt = (struct rt_msghdr *)p; 75 | struct sockaddr * sa; 76 | char * p2; 77 | int i; 78 | 79 | /* No addresses yet. */ 80 | for (i = 0; i < RTAX_MAX; i++) 81 | sas[i] = NULL; 82 | 83 | /* Move through the buffer recording pointers to addresses. */ 84 | for (i = 0, p2 = &p[sizeof(struct rt_msghdr)]; 85 | p2 < &p[rt->rtm_msglen]; 86 | p2 += SA_SIZE(sa)) { 87 | sa = (struct sockaddr *)p2; 88 | if (p2 + SA_SIZE(sa) > &p[rt->rtm_msglen]) { 89 | warn0("Socket address overflows routing message!"); 90 | goto err0; 91 | } 92 | 93 | /* Which address type is this? */ 94 | if ((i = ffs(rt->rtm_addrs & ~((1 << i) - 1))) == 0) { 95 | warn0("Routing message contains wrong number of addresses!"); 96 | goto err0; 97 | } 98 | sas[i - 1] = sa; 99 | } 100 | if ((rt->rtm_addrs & ~((1 << i) - 1)) != 0) { 101 | warn0("Routing message contains wrong number of addresses!"); 102 | goto err0; 103 | } 104 | 105 | /* Success! */ 106 | return (0); 107 | 108 | err0: 109 | /* Failure! */ 110 | return (-1); 111 | } 112 | 113 | /** 114 | * netconfig_getif(srcaddr, gwaddr, host, ifname): 115 | * Find the IPv4 route used for sending packets to ${host}; return via 116 | * ${ifname}, ${gwaddr}, and ${srcaddr} the name of the network interface, 117 | * the gateway, and the appropriate source IPv4 address. 118 | */ 119 | int 120 | netconfig_getif(struct sockaddr_in * srcaddr, struct sockaddr_in * gwaddr, 121 | in_addr_t imdsaddr, char ** ifname) 122 | { 123 | struct sockaddr * sas[RTAX_MAX]; 124 | int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0}; 125 | in_addr_t rtdst, rtmsk; 126 | int64_t best_rtmsk = -1; 127 | u_short best_index = (u_short)-1; 128 | struct sockaddr * best_addr = NULL; 129 | struct sockaddr * best_gwaddr = NULL; 130 | char * buf; 131 | char * p; 132 | struct rt_msghdr * rt; 133 | size_t len; 134 | 135 | /* Dump the routing table. */ 136 | if (sysctldump(mib, sizeof(mib) / sizeof(mib[0]), &buf, &len)) 137 | goto err0; 138 | 139 | /* 140 | * Walk through the routing table we just dumped, looking for the 141 | * best route to the Instance Metadata Service. 142 | */ 143 | for (p = buf; p < &buf[len]; p += rt->rtm_msglen) { 144 | /* We have a routing message. */ 145 | rt = (struct rt_msghdr *)p; 146 | if ((&p[sizeof(struct rt_msghdr)] > &buf[len]) || 147 | (&p[rt->rtm_msglen] > &buf[len])) { 148 | warn0("Routing message overflows sysctl buffer!"); 149 | goto err1; 150 | } 151 | 152 | /* It should be an RTM_GET message. */ 153 | if (rt->rtm_type != RTM_GET) { 154 | warn0("Unexpected routing message type: %d", 155 | (int)rt->rtm_type); 156 | continue; 157 | } 158 | 159 | /* Extract addresses from the message. */ 160 | if (extractaddrs(p, sas)) 161 | goto err1; 162 | 163 | /* We only care about IPv4 destinations. */ 164 | if ((sas[RTAX_DST] == NULL) || 165 | (sas[RTAX_DST]->sa_family != AF_INET)) 166 | continue; 167 | rtdst = ((struct sockaddr_in *)(sas[RTAX_DST]))->sin_addr.s_addr; 168 | 169 | /* Ignore any route which doesn't match the netmask. */ 170 | if (sas[RTAX_NETMASK] != NULL) { 171 | /* Sanity-check. */ 172 | if (sas[RTAX_NETMASK]->sa_family != AF_INET) { 173 | warn0("Interface has IPv4 address" 174 | " but non-IPv4 netmask!"); 175 | continue; 176 | } 177 | rtmsk = ((struct sockaddr_in *)(sas[RTAX_NETMASK]))->sin_addr.s_addr; 178 | } else { 179 | rtmsk = (in_addr_t)0xffffffff; 180 | } 181 | if (((imdsaddr ^ rtdst) & rtmsk) != 0) 182 | continue; 183 | 184 | /* Pick the most specific route. */ 185 | if (ntohl(rtmsk) >= best_rtmsk) { 186 | best_rtmsk = ntohl(rtmsk); 187 | best_index = rt->rtm_index; 188 | best_gwaddr = sas[RTAX_GATEWAY]; 189 | best_addr = sas[RTAX_IFA]; 190 | } 191 | } 192 | 193 | /* Did we find a route? */ 194 | if (best_rtmsk == -1) { 195 | warn0("No route to Instance Metadata Service found!"); 196 | goto err1; 197 | } 198 | 199 | /* Does that interface have a local address? */ 200 | if (best_addr == NULL) { 201 | warn0("Best route has no local address!"); 202 | goto err1; 203 | } 204 | if (best_addr->sa_family != AF_INET) { 205 | warn0("IPv4 route has non-IPv4 interface address!"); 206 | goto err1; 207 | } 208 | 209 | /* Is there a gateway? */ 210 | if (best_gwaddr == NULL) { 211 | warn0("Best route has no gateway address!"); 212 | goto err1; 213 | } 214 | if (best_gwaddr->sa_family != AF_INET) { 215 | warn0("IPv4 route has non-IPv4 gateway address!"); 216 | goto err1; 217 | } 218 | 219 | /* Allocate space for the interface name. */ 220 | if ((*ifname = malloc(IFNAMSIZ)) == NULL) 221 | goto err1; 222 | 223 | /* Return the local address and interface name. */ 224 | memcpy(srcaddr, best_addr, best_addr->sa_len); 225 | memcpy(gwaddr, best_gwaddr, best_gwaddr->sa_len); 226 | if_indextoname(best_index, *ifname); 227 | 228 | /* Success! */ 229 | return (0); 230 | 231 | err1: 232 | free(buf); 233 | err0: 234 | /* Failure! */ 235 | return (-1); 236 | } 237 | 238 | /* 239 | * netconfig_getmac(host, mac): 240 | * Look up the MAC address associated with the IPv4 address ${host} and 241 | * return it via ${mac}. Note that this can fail if ${host} is not in the 242 | * operating system's ARP cache. 243 | */ 244 | int 245 | netconfig_getmac(struct sockaddr_in * host, uint8_t mac[6]) 246 | { 247 | struct sockaddr * sas[RTAX_MAX]; 248 | int mib[] = {CTL_NET, PF_ROUTE, 0, AF_INET, NET_RT_FLAGS, RTF_LLINFO}; 249 | char * buf; 250 | char * p; 251 | struct rt_msghdr * rt; 252 | size_t len; 253 | 254 | /* Dump the ARP table. */ 255 | if (sysctldump(mib, sizeof(mib) / sizeof(mib[0]), &buf, &len)) 256 | goto err0; 257 | 258 | /* 259 | * Walk through the ARP table we just dumped, looking for the entry 260 | * corresponding to the IPv4 address we have. 261 | */ 262 | for (p = buf; p < &buf[len]; p += rt->rtm_msglen) { 263 | /* We have a routing message. */ 264 | rt = (struct rt_msghdr *)p; 265 | if ((&p[sizeof(struct rt_msghdr)] > &buf[len]) || 266 | (&p[rt->rtm_msglen] > &buf[len])) { 267 | warn0("Routing message overflows sysctl buffer!"); 268 | goto err1; 269 | } 270 | 271 | /* It should be an RTM_GET message. */ 272 | if (rt->rtm_type != RTM_GET) { 273 | warn0("Unexpected routing message type: %d", 274 | (int)rt->rtm_type); 275 | continue; 276 | } 277 | 278 | /* Extract addresses from the message. */ 279 | if (extractaddrs(p, sas)) 280 | goto err1; 281 | 282 | /* Is this the one we're looking for? */ 283 | if (sas[RTAX_DST] == NULL) 284 | continue; 285 | if (sas[RTAX_DST]->sa_family != AF_INET) 286 | continue; 287 | if (((struct sockaddr_in *)(sas[RTAX_DST]))->sin_addr.s_addr 288 | != host->sin_addr.s_addr) 289 | continue; 290 | 291 | /* Do we have a link-layer address? */ 292 | if (sas[RTAX_GATEWAY] == NULL) 293 | continue; 294 | if (sas[RTAX_GATEWAY]->sa_family != AF_LINK) 295 | continue; 296 | 297 | /* Copy the address out. */ 298 | memcpy(mac, LLADDR((struct sockaddr_dl *)sas[RTAX_GATEWAY]), 6); 299 | break; 300 | } 301 | 302 | /* Success! */ 303 | return (0); 304 | 305 | err1: 306 | free(buf); 307 | err0: 308 | /* Failure! */ 309 | return (-1); 310 | } 311 | -------------------------------------------------------------------------------- /imds-filterd/packets.c: -------------------------------------------------------------------------------- 1 | #define __BSD_VISIBLE 1 /* Needed for net/ headers. */ 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "asprintf.h" 19 | #include "events.h" 20 | #include "warnp.h" 21 | 22 | #include "imds-filterd.h" 23 | 24 | /* Maximum length of an IPv4 packet. */ 25 | #define MAXPACKET 65535 26 | 27 | /* State for outward packet path handling. */ 28 | struct outpath_state { 29 | int rdtun; 30 | int wrtun; 31 | int extif; 32 | in_addr_t dstaddr; 33 | uint16_t dstport; 34 | uint8_t etherframe[14 + MAXPACKET]; 35 | }; 36 | 37 | static int 38 | outpkt(void * cookie) 39 | { 40 | struct outpath_state * os = cookie; 41 | struct ip * pkt_ip; 42 | struct tcphdr * pkt_tcp; 43 | ssize_t rlen, wlen; 44 | in_addr_t srcaddr, dstaddr; 45 | uint16_t srcport, dstport; 46 | 47 | /* Read a packet. */ 48 | rlen = read(os->rdtun, &os->etherframe[14], MAXPACKET); 49 | if (rlen == -1) { 50 | warnp("Error reading packet from tunnel device"); 51 | goto err0; 52 | } 53 | if (rlen == 0) { 54 | warn0("Unexpected EOF from tunnel device"); 55 | goto err0; 56 | } 57 | 58 | /* Check that we have an IPv4 packet. */ 59 | if ((size_t)rlen < sizeof(struct ip)) 60 | goto readmore; 61 | pkt_ip = (struct ip *)(&os->etherframe[14]); 62 | if (pkt_ip->ip_v != 4) 63 | goto readmore; 64 | 65 | /* Make sure that this is a TCP packet. */ 66 | if (pkt_ip->ip_p != IPPROTO_TCP) 67 | goto readmore; 68 | if ((size_t)rlen < pkt_ip->ip_hl * 4 + sizeof(struct tcphdr)) 69 | goto readmore; 70 | pkt_tcp = (struct tcphdr *)(&os->etherframe[14 + pkt_ip->ip_hl * 4]); 71 | 72 | /* Extract source and destination IP addresses and port numbers. */ 73 | srcaddr = ntohl(pkt_ip->ip_src.s_addr); 74 | dstaddr = ntohl(pkt_ip->ip_dst.s_addr); 75 | srcport = ntohs(pkt_tcp->th_sport); 76 | dstport = ntohs(pkt_tcp->th_dport); 77 | 78 | /* 79 | * If the source and destination match one of our own connections, 80 | * let it through to the external interface; otherwise, redirect the 81 | * IP packet through a tunnel into the jail. 82 | */ 83 | if (conns_isours(srcaddr, srcport) && 84 | (dstaddr == os->dstaddr) && (dstport == os->dstport)) { 85 | /* Write the ethernet frame over the external interface. */ 86 | wlen = rlen + 14; 87 | if (write(os->extif, os->etherframe, (size_t)wlen) != wlen) { 88 | warnp("Error writing ethernet frame"); 89 | goto err0; 90 | } 91 | } else { 92 | /* Write the IPv4 packet into the other tunnel. */ 93 | if (write(os->wrtun, &os->etherframe[14], (size_t)rlen) 94 | != rlen) { 95 | warnp("Error writing packet into tunnel"); 96 | goto err0; 97 | } 98 | } 99 | 100 | readmore: 101 | /* Wait for the next packet to arrive. */ 102 | if (events_network_register(outpkt, os, os->rdtun, 103 | EVENTS_NETWORK_OP_READ)) { 104 | warnp("Cannot register packet read callback"); 105 | goto err0; 106 | } 107 | 108 | /* Success! */ 109 | return (0); 110 | 111 | err0: 112 | /* Fatal error! */ 113 | return (-1); 114 | } 115 | 116 | /** 117 | * outpath(tunin, tunout, dstaddr, ifname, srcmac, gwmac): 118 | * Read packets from ${tunin} and either write them to ${tunout} or wrap them 119 | * into ethernet frames and send them via ${ifname}. 120 | */ 121 | int 122 | outpath(int tunin, int tunout, struct sockaddr_in * dstaddr, 123 | const char * ifname, uint8_t srcmac[6], uint8_t gwmac[6]) 124 | { 125 | struct outpath_state * os; 126 | struct ifreq ifr; 127 | 128 | /* Allocate state structure. */ 129 | if ((os = malloc(sizeof(struct outpath_state))) == NULL) 130 | goto err0; 131 | os->rdtun = tunin; 132 | os->wrtun = tunout; 133 | os->dstaddr = ntohl(dstaddr->sin_addr.s_addr); 134 | os->dstport = ntohs(dstaddr->sin_port); 135 | 136 | /* Open BPF. */ 137 | if ((os->extif = open("/dev/bpf", O_WRONLY)) == -1) { 138 | warnp("open(/dev/bpf)"); 139 | goto err1; 140 | } 141 | 142 | /* Bind to the external network interface. */ 143 | memset(&ifr, 0, sizeof(struct ifreq)); 144 | strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name)); 145 | if (ioctl(os->extif, BIOCSETIF, &ifr)) { 146 | warnp("ioctl(BIOCSETIF)"); 147 | goto err2; 148 | } 149 | 150 | /* Assemble ethernet frame header. */ 151 | memcpy(&os->etherframe[0], gwmac, 6); 152 | memcpy(&os->etherframe[6], srcmac, 6); 153 | os->etherframe[12] = 0x08; 154 | os->etherframe[13] = 0x00; 155 | 156 | /* Get a callback when a packet arrives over tunin. */ 157 | if (events_network_register(outpkt, os, os->rdtun, 158 | EVENTS_NETWORK_OP_READ)) { 159 | warnp("Cannot register packet read callback"); 160 | goto err2; 161 | } 162 | 163 | /* Success! */ 164 | return (0); 165 | 166 | err2: 167 | close(os->extif); 168 | err1: 169 | free(os); 170 | err0: 171 | /* Failure! */ 172 | return (-1); 173 | } 174 | 175 | /* State for inward packet path handling. */ 176 | struct inpath_state { 177 | int rdtun; 178 | int wrtun; 179 | uint8_t buf[MAXPACKET]; 180 | }; 181 | 182 | static int 183 | inpkt(void * cookie) 184 | { 185 | struct inpath_state * is = cookie; 186 | ssize_t rlen; 187 | 188 | /* Read a packet. */ 189 | rlen = read(is->rdtun, is->buf, MAXPACKET); 190 | if (rlen == -1) { 191 | warnp("Error reading packet from tunnel device"); 192 | goto err0; 193 | } 194 | if (rlen == 0) { 195 | warn0("Unexpected EOF from tunnel device"); 196 | goto err0; 197 | } 198 | 199 | /* Write the IPv4 packet into the other tunnel. */ 200 | if (write(is->wrtun, is->buf, (size_t)rlen) != rlen) { 201 | warnp("Error writing packet into tunnel"); 202 | goto err0; 203 | } 204 | 205 | /* Wait for the next packet to arrive. */ 206 | if (events_network_register(inpkt, is, is->rdtun, 207 | EVENTS_NETWORK_OP_READ)) { 208 | warnp("Cannot register packet read callback"); 209 | goto err0; 210 | } 211 | 212 | /* Success! */ 213 | return (0); 214 | 215 | err0: 216 | /* Fatal error! */ 217 | return (-1); 218 | } 219 | 220 | /** 221 | * inpath(tunin, tunout): 222 | * Read packets from ${tunout} and write them to ${tunin}. 223 | */ 224 | int 225 | inpath(int tunin, int tunout) 226 | { 227 | struct inpath_state * is; 228 | 229 | /* Allocate state structure. */ 230 | if ((is = malloc(sizeof(struct inpath_state))) == NULL) 231 | goto err0; 232 | is->rdtun = tunout; 233 | is->wrtun = tunin; 234 | 235 | /* Get a callback when a packet arrives over tunout. */ 236 | if (events_network_register(inpkt, is, is->rdtun, 237 | EVENTS_NETWORK_OP_READ)) { 238 | warnp("Cannot register packet read callback"); 239 | goto err1; 240 | } 241 | 242 | /* Success! */ 243 | return (0); 244 | 245 | err1: 246 | free(is); 247 | err0: 248 | /* Failure! */ 249 | return (-1); 250 | } 251 | -------------------------------------------------------------------------------- /imds-proxy/Makefile: -------------------------------------------------------------------------------- 1 | .POSIX: 2 | # AUTOGENERATED FILE, DO NOT EDIT 3 | PROG=imds-proxy 4 | SRCS=main.c http.c ident.c request.c uri2path.c conf.c elasticarray.c asprintf.c daemonize.c getopt.c hexify.c noeintr.c setuidgid.c sock.c warnp.c 5 | IDIRS=-I ../libcperciva/datastruct -I ../libcperciva/util 6 | LDADD_REQ=-lpthread 7 | SUBDIR_DEPTH=.. 8 | RELATIVE_DIR=imds-proxy 9 | 10 | all: 11 | if [ -z "$${HAVE_BUILD_FLAGS}" ]; then \ 12 | cd ${SUBDIR_DEPTH}; \ 13 | ${MAKE} BUILD_SUBDIR=${RELATIVE_DIR} \ 14 | BUILD_TARGET=${PROG} buildsubdir; \ 15 | else \ 16 | ${MAKE} ${PROG}; \ 17 | fi 18 | 19 | install:${PROG} 20 | mkdir -p ${BINDIR} 21 | cp ${PROG} ${BINDIR}/_inst.${PROG}.$$$$_ && \ 22 | strip ${BINDIR}/_inst.${PROG}.$$$$_ && \ 23 | chmod 0555 ${BINDIR}/_inst.${PROG}.$$$$_ && \ 24 | mv -f ${BINDIR}/_inst.${PROG}.$$$$_ ${BINDIR}/${PROG} 25 | if ! [ -z "${MAN1DIR}" ]; then \ 26 | mkdir -p ${MAN1DIR}; \ 27 | for MPAGE in ${MAN1}; do \ 28 | cp $$MPAGE ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ 29 | chmod 0444 ${MAN1DIR}/_inst.$$MPAGE.$$$$_ && \ 30 | mv -f ${MAN1DIR}/_inst.$$MPAGE.$$$$_ ${MAN1DIR}/$$MPAGE; \ 31 | done; \ 32 | fi 33 | 34 | clean: 35 | rm -f ${PROG} ${SRCS:.c=.o} 36 | 37 | ${PROG}:${SRCS:.c=.o} 38 | ${CC} -o ${PROG} ${SRCS:.c=.o} ${LDFLAGS} ${LDADD_EXTRA} ${LDADD_REQ} ${LDADD_POSIX} 39 | 40 | main.o: main.c ../libcperciva/util/daemonize.h ../libcperciva/util/getopt.h ../libcperciva/util/setuidgid.h ../libcperciva/util/sock.h ../libcperciva/util/warnp.h imds-proxy.h 41 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c main.c -o main.o 42 | http.o: http.c ../libcperciva/util/sock.h ../libcperciva/util/warnp.h imds-proxy.h 43 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c http.c -o http.o 44 | ident.o: ident.c ../libcperciva/datastruct/elasticarray.h ../libcperciva/util/sock.h ../libcperciva/util/warnp.h imds-proxy.h 45 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ident.c -o ident.o 46 | request.o: request.c ../libcperciva/util/asprintf.h ../libcperciva/util/hexify.h ../libcperciva/util/warnp.h imds-proxy.h 47 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c request.c -o request.o 48 | uri2path.o: uri2path.c ../libcperciva/util/hexify.h ../libcperciva/util/warnp.h imds-proxy.h 49 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c uri2path.c -o uri2path.o 50 | conf.o: conf.c ../libcperciva/datastruct/elasticarray.h ../libcperciva/util/warnp.h imds-proxy.h 51 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c conf.c -o conf.o 52 | elasticarray.o: ../libcperciva/datastruct/elasticarray.c ../libcperciva/datastruct/elasticarray.h 53 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/datastruct/elasticarray.c -o elasticarray.o 54 | asprintf.o: ../libcperciva/util/asprintf.c ../libcperciva/util/asprintf.h 55 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/asprintf.c -o asprintf.o 56 | daemonize.o: ../libcperciva/util/daemonize.c ../libcperciva/util/noeintr.h ../libcperciva/util/warnp.h ../libcperciva/util/daemonize.h 57 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/daemonize.c -o daemonize.o 58 | getopt.o: ../libcperciva/util/getopt.c ../libcperciva/util/getopt.h 59 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/getopt.c -o getopt.o 60 | hexify.o: ../libcperciva/util/hexify.c ../libcperciva/util/hexify.h 61 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/hexify.c -o hexify.o 62 | noeintr.o: ../libcperciva/util/noeintr.c ../libcperciva/util/noeintr.h 63 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/noeintr.c -o noeintr.o 64 | setuidgid.o: ../libcperciva/util/setuidgid.c ../libcperciva/util/parsenum.h ../libcperciva/util/warnp.h ../libcperciva/util/setuidgid.h 65 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/setuidgid.c -o setuidgid.o 66 | sock.o: ../libcperciva/util/sock.c ../libcperciva/util/imalloc.h ../libcperciva/util/parsenum.h ../libcperciva/util/warnp.h ../libcperciva/util/sock.h ../libcperciva/util/sock_internal.h 67 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/sock.c -o sock.o 68 | warnp.o: ../libcperciva/util/warnp.c ../libcperciva/util/warnp.h 69 | ${CC} ${CFLAGS_POSIX} -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700 -DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\" -I.. ${IDIRS} ${CPPFLAGS} ${CFLAGS} -c ../libcperciva/util/warnp.c -o warnp.o 70 | -------------------------------------------------------------------------------- /imds-proxy/Makefile.BSD: -------------------------------------------------------------------------------- 1 | PROG= imds-proxy 2 | MAN1= 3 | 4 | # Library code required 5 | LDADD_REQ= -lpthread 6 | 7 | # Useful relative directory 8 | LIBCPERCIVA_DIR = ../libcperciva 9 | 10 | # imds-proxy code 11 | SRCS = main.c 12 | SRCS += http.c 13 | SRCS += ident.c 14 | SRCS += request.c 15 | SRCS += uri2path.c 16 | SRCS += conf.c 17 | 18 | # Data structures 19 | .PATH.c : ${LIBCPERCIVA_DIR}/datastruct 20 | SRCS += elasticarray.c 21 | IDIRS += -I ${LIBCPERCIVA_DIR}/datastruct 22 | 23 | # Utility functions 24 | .PATH.c : ${LIBCPERCIVA_DIR}/util 25 | SRCS += asprintf.c 26 | SRCS += daemonize.c 27 | SRCS += getopt.c 28 | SRCS += hexify.c 29 | SRCS += noeintr.c 30 | SRCS += setuidgid.c 31 | SRCS += sock.c 32 | SRCS += warnp.c 33 | IDIRS += -I ${LIBCPERCIVA_DIR}/util 34 | 35 | .include 36 | -------------------------------------------------------------------------------- /imds-proxy/conf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "elasticarray.h" 10 | #include "warnp.h" 11 | 12 | #include "imds-proxy.h" 13 | 14 | /* A single rule. */ 15 | struct rule { 16 | int rtype; 17 | #define RTYPE_ANY 0 18 | #define RTYPE_UID 1 19 | #define RTYPE_GID 2 20 | id_t id; 21 | char * prefix; 22 | int allow; 23 | }; 24 | 25 | /* IMDS access rules. */ 26 | struct imds_conf { 27 | struct rule * rs; 28 | size_t nrs; 29 | }; 30 | 31 | ELASTICARRAY_DECL(RULELIST, rulelist, struct rule); 32 | 33 | /* Buffer length for getpwnam_r and getgrnam_r. */ 34 | #define PWBUFLEN 4096 35 | 36 | /* Parse a user name to a uid. */ 37 | static int 38 | parseuid(char * p, size_t len, uid_t * u) 39 | { 40 | struct passwd pwd; 41 | struct passwd * res; 42 | char buf[PWBUFLEN]; 43 | char * s; 44 | 45 | /* Create a NUL-terminated string with the name. */ 46 | if ((s = malloc(len + 1)) == NULL) 47 | goto err0; 48 | memcpy(s, p, len); 49 | s[len] = '\0'; 50 | 51 | /* Look up the user. */ 52 | if (getpwnam_r(s, &pwd, buf, PWBUFLEN, &res)) { 53 | warnp("getpwnam_r(%s)", s); 54 | goto err1; 55 | } 56 | 57 | /* Does the user exist? */ 58 | if (res == NULL) { 59 | warn0("User not found: %s", s); 60 | goto err1; 61 | } 62 | 63 | /* Free the duplicated name. */ 64 | free(s); 65 | 66 | /* Return the user ID. */ 67 | *u = res->pw_uid; 68 | 69 | /* Success! */ 70 | return (0); 71 | 72 | err1: 73 | free(s); 74 | err0: 75 | /* Failure! */ 76 | return (-1); 77 | } 78 | 79 | /* Parse a group name to a gid. */ 80 | static int 81 | parsegid(char * p, size_t len, gid_t * g) 82 | { 83 | struct group grp; 84 | struct group * res; 85 | char buf[PWBUFLEN]; 86 | char * s; 87 | 88 | /* Create a NUL-terminated string with the name. */ 89 | if ((s = malloc(len + 1)) == NULL) 90 | goto err0; 91 | memcpy(s, p, len); 92 | s[len] = '\0'; 93 | 94 | /* Look up the group. */ 95 | if (getgrnam_r(s, &grp, buf, PWBUFLEN, &res)) { 96 | warnp("getgrnam_r(%s)", s); 97 | goto err1; 98 | } 99 | 100 | /* Does the group exist? */ 101 | if (res == NULL) { 102 | warn0("Group not found: %s", s); 103 | goto err1; 104 | } 105 | 106 | /* Free the duplicated name. */ 107 | free(s); 108 | 109 | /* Return the group ID. */ 110 | *g = res->gr_gid; 111 | 112 | /* Success! */ 113 | return (0); 114 | 115 | err1: 116 | free(s); 117 | err0: 118 | /* Failure! */ 119 | return (-1); 120 | } 121 | 122 | /** 123 | * conf_read(path): 124 | * Read the imds-proxy configuration file ${path} and return a state which 125 | * can be passed to conf_check or conf_free. 126 | */ 127 | struct imds_conf * 128 | conf_read(const char * path) 129 | { 130 | struct imds_conf * imdsc; 131 | RULELIST rs; 132 | struct rule r; 133 | FILE * f; 134 | char * line = NULL; 135 | size_t linecap = 0; 136 | ssize_t linelen; 137 | size_t i; 138 | char * p; 139 | char * sp; 140 | uid_t u; 141 | gid_t g; 142 | 143 | /* Open the configuration file. */ 144 | if ((f = fopen(path, "r")) == NULL) { 145 | warnp("fopen(%s)", path); 146 | goto err0; 147 | } 148 | 149 | /* Create an elastic array of rules. */ 150 | if ((rs = rulelist_init(0)) == NULL) 151 | goto err1; 152 | 153 | /* Read lines and construct rules. */ 154 | while ((linelen = getline(&line, &linecap, f)) > 0) { 155 | /* Strip trailing EOL characters. */ 156 | while ((linelen > 0) && 157 | ((line[linelen - 1] == '\n') || 158 | (line[linelen - 1] == '\r'))) { 159 | line[--linelen] = '\0'; 160 | } 161 | 162 | /* Skip comments and empty lines. */ 163 | if ((line[0] == '#') || (line[0] == '\0')) 164 | continue; 165 | 166 | /* Allow or Deny? */ 167 | if (strncmp(line, "Deny ", 5) == 0) { 168 | p = &line[5]; 169 | r.allow = 0; 170 | } else if (strncmp(line, "Allow ", 6) == 0) { 171 | p = &line[6]; 172 | r.allow = 1; 173 | } else { 174 | goto invalid; 175 | } 176 | 177 | /* Is there a user/group restriction? */ 178 | if (strncmp(p, "user ", 5) == 0) { 179 | p = &p[5]; 180 | r.rtype = RTYPE_UID; 181 | if ((sp = strchr(p, ' ')) == NULL) 182 | goto invalid; 183 | if (parseuid(p, (size_t)(sp - p), &u)) 184 | goto err2; 185 | p = &sp[1]; 186 | r.id = u; 187 | } else if (strncmp(p, "group ", 6) == 0) { 188 | p = &p[6]; 189 | r.rtype = RTYPE_GID; 190 | if ((sp = strchr(p, ' ')) == NULL) 191 | goto invalid; 192 | if (parsegid(p, (size_t)(sp - p), &g)) 193 | goto err2; 194 | p = &sp[1]; 195 | r.id = g; 196 | } else { 197 | r.rtype = RTYPE_ANY; 198 | } 199 | 200 | /* We should have a quoted string. */ 201 | if ((p[0] != '"') || 202 | (strchr(&p[1], '"') != &line[linelen - 1])) 203 | goto invalid; 204 | 205 | /* Make sure that there aren't any bogus wildcards. */ 206 | for (i = 0; p[i]; i++) { 207 | if (p[i] == '*') { 208 | /* Must follow a '/' character. */ 209 | if (p[i - 1] != '/') 210 | goto invalid; 211 | 212 | /* 213 | * Must precede a '/' character or be at the 214 | * end of the string (which is nonetheless 215 | * pointless, since we match prefixes). 216 | */ 217 | if ((p[i + 1] != '/') && 218 | (p[i + 1] != '"')) 219 | goto invalid; 220 | } 221 | } 222 | 223 | /* Record the prefix string, without the endquote char. */ 224 | if ((r.prefix = strdup(&p[1])) == NULL) 225 | goto err2; 226 | r.prefix[strlen(r.prefix) - 1] = '\0'; 227 | 228 | /* Add this rule to our ruleset. */ 229 | if (rulelist_append(rs, &r, 1)) { 230 | free(r.prefix); 231 | goto err2; 232 | } 233 | 234 | /* Move onto the next line. */ 235 | continue; 236 | 237 | invalid: 238 | warn0("Invalid configuration rule: %s", line); 239 | goto err2; 240 | 241 | } 242 | 243 | /* We should have reached EOF. */ 244 | if (!feof(f)) { 245 | warnp("Error reading configuration file: %s", path); 246 | goto err2; 247 | } 248 | 249 | /* Create a state structure and export the list. */ 250 | if ((imdsc = malloc(sizeof(struct imds_conf))) == NULL) 251 | goto err2; 252 | if (rulelist_export(rs, &imdsc->rs, &imdsc->nrs)) 253 | goto err3; 254 | 255 | /* Success! */ 256 | return (imdsc); 257 | 258 | err3: 259 | free(imdsc); 260 | err2: 261 | free(line); 262 | for (i = 0; i < rulelist_getsize(rs); i++) 263 | free(rulelist_get(rs, i)->prefix); 264 | rulelist_free(rs); 265 | err1: 266 | fclose(f); 267 | err0: 268 | /* Failure! */ 269 | return (NULL); 270 | } 271 | 272 | /* Check whether the path matches. */ 273 | static int 274 | pathmatch(const char * path, const char * prefix) 275 | { 276 | 277 | /* Scan through the prefix one character at a time. */ 278 | for (; *prefix; prefix++) { 279 | /* A '*' matches until the next '/' or the end. */ 280 | if (*prefix == '*') { 281 | while ((*path != '/') && (*path != '\0')) 282 | path++; 283 | continue; 284 | } 285 | 286 | /* Anything else only matches itself. */ 287 | if (*prefix != *path++) 288 | return (0); 289 | } 290 | 291 | /* The entire prefix matches the provided path. */ 292 | return (1); 293 | } 294 | 295 | /** 296 | * conf_check(imdsc, path, uid, gids, ngid): 297 | * Check whether the specified uid/gids is allowed to make this request; 298 | * return nonzero if the request is allowed. 299 | */ 300 | int 301 | conf_check(const struct imds_conf * imdsc, const char * path, 302 | uid_t uid, gid_t * gids, size_t ngid) 303 | { 304 | size_t rnum, i; 305 | int allow = 0; 306 | 307 | /* Scan through the rules looking for any which match. */ 308 | for (rnum = 0; rnum < imdsc->nrs; rnum++) { 309 | /* Does the id match (if relevant)? */ 310 | if (imdsc->rs[rnum].rtype == RTYPE_UID) { 311 | if (imdsc->rs[rnum].id != uid) 312 | continue; 313 | // warn0("XXX UID match rule %zu", rnum); 314 | } else if (imdsc->rs[rnum].rtype == RTYPE_GID) { 315 | for (i = 0; i < ngid; i++) { 316 | if (imdsc->rs[rnum].id == gids[i]) 317 | break; 318 | } 319 | if (i == ngid) 320 | continue; 321 | // warn0("XXX GID match rule %zu", rnum); 322 | } 323 | 324 | /* Does the path match? */ 325 | if (!pathmatch(path, imdsc->rs[rnum].prefix)) 326 | continue; 327 | 328 | // warn0("XXX path match rule %zu", rnum); 329 | 330 | /* Do what this rule says. */ 331 | allow = imdsc->rs[rnum].allow; 332 | } 333 | 334 | /* Return status from the last matched rule. */ 335 | return (allow); 336 | } 337 | 338 | /** 339 | * conf_free(imdsc): 340 | * Free the configuration state ${imdsc}. 341 | */ 342 | void 343 | conf_free(struct imds_conf * imdsc) 344 | { 345 | size_t rnum; 346 | 347 | /* Free the prefix strings. */ 348 | for (rnum = 0; rnum < imdsc->nrs; rnum++) 349 | free(imdsc->rs[rnum].prefix); 350 | 351 | /* Free the array of rules. */ 352 | free(imdsc->rs); 353 | 354 | /* Free the structure. */ 355 | free(imdsc); 356 | } 357 | -------------------------------------------------------------------------------- /imds-proxy/http.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "sock.h" 9 | #include "warnp.h" 10 | 11 | #include "imds-proxy.h" 12 | 13 | #define BUFLEN 1024 14 | 15 | /** 16 | * http_proxy(s, dst, id, imdsc): 17 | * Read an HTTP request from the socket ${s} and forward it to address ${dst}, 18 | * after querying ${id} about the owner of the incoming connection and 19 | * checking against the ruleset ${imdsc}. 20 | */ 21 | void 22 | http_proxy(int s, struct sock_addr * const * dst, 23 | struct sock_addr * const * id, const struct imds_conf * imdsc) 24 | { 25 | char buf[BUFLEN]; 26 | uid_t uid; 27 | gid_t * gids; 28 | size_t ngid; 29 | FILE * client; 30 | char * request; 31 | char * path; 32 | int s_imds; 33 | FILE * f_imds; 34 | size_t len; 35 | int allowed; 36 | 37 | /* Look up the owner of this connect. */ 38 | if (ident(s, id, &uid, &gids, &ngid)) { 39 | /* Drop the connection. */ 40 | goto done0; 41 | } 42 | 43 | // warn0("XXX uid = %d", (int)uid); 44 | // warn0("XXX ngid = %zu", ngid); 45 | // for (size_t i = 0; i < ngid; i++) 46 | // warn0("XXX gid[%zu] = %d", i, gids[i]); 47 | 48 | /* Convert the file descriptor into a buffered file. */ 49 | if ((client = fdopen(s, "r+")) == NULL) { 50 | warnp("fdopen"); 51 | goto done1; 52 | } 53 | 54 | /* Read and parse the request. */ 55 | if (request_read(client, &request, &path)) { 56 | warnp("HTTP request read failed"); 57 | goto done2; 58 | } 59 | 60 | // warn0("XXX HTTP path: ===>%s<===", path); 61 | // warn0("XXX HTTP request:\n======\n%s\n=====\n", request); 62 | 63 | /* Check whether this process is allowed to make this request. */ 64 | allowed = conf_check(imdsc, path, uid, gids, ngid); 65 | 66 | /* Log request. */ 67 | syslog(LOG_INFO, "imds-proxy: %s uid %zu %s", 68 | allowed ? "ALLOW" : "DENY", (size_t)uid, path); 69 | 70 | /* Drop disallowed requests. */ 71 | if (!allowed) { 72 | fprintf(client, "HTTP/1.0 403 Forbidden\r\n\r\n"); 73 | goto done3; 74 | } 75 | 76 | /* Open a connection to the IMDS and wrap it into a FILE. */ 77 | if ((s_imds = sock_connect_blocking(dst)) == -1) { 78 | warnp("sock_connect_blocking"); 79 | goto done3; 80 | } 81 | if ((f_imds = fdopen(s_imds, "r+")) == NULL) { 82 | warnp("fdopen"); 83 | close(s_imds); 84 | goto done3; 85 | } 86 | 87 | /* Send the request. */ 88 | if (fwrite(request, strlen(request), 1, f_imds) != 1) { 89 | warnp("fwrite"); 90 | goto done4; 91 | } 92 | 93 | /* Forward the server's response back. */ 94 | do { 95 | if ((len = fread(buf, 1, BUFLEN, f_imds)) == 0) 96 | break; 97 | if (fwrite(buf, len, 1, client) != 1) 98 | break; 99 | } while (1); 100 | 101 | /* No point checking ferror; we don't handle errors anyway. */ 102 | 103 | done4: 104 | fclose(f_imds); 105 | done3: 106 | free(request); 107 | free(path); 108 | done2: 109 | fclose(client); 110 | s = -1; 111 | done1: 112 | /* Free the list of gids. */ 113 | free(gids); 114 | done0: 115 | if (s != -1) 116 | close(s); 117 | } 118 | -------------------------------------------------------------------------------- /imds-proxy/ident.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "elasticarray.h" 10 | #include "sock.h" 11 | #include "warnp.h" 12 | 13 | #include "imds-proxy.h" 14 | 15 | /* Elastic array of gids. */ 16 | ELASTICARRAY_DECL(GIDLIST, gidlist, gid_t); 17 | 18 | /** 19 | * ident(s, id, uid, gids, ngid): 20 | * Query ${id} about the ownership of the process holding the other end of 21 | * the socket ${s}; return the user ID via ${uid}, a malloced array of group 22 | * IDs via ${gids}, and the number of group IDs via ${ngid}. 23 | */ 24 | int 25 | ident(int s, struct sock_addr * const * id, 26 | uid_t * uid, gid_t ** gids, size_t * ngid) 27 | { 28 | struct sockaddr_in al; 29 | struct sockaddr_in ar; 30 | socklen_t alen; 31 | uint8_t idreq[12]; 32 | FILE * f_id; 33 | int s_id; 34 | intmax_t i; 35 | GIDLIST gs; 36 | gid_t g; 37 | 38 | /* Look up the local and remote addresses of this connection. */ 39 | alen = sizeof(struct sockaddr_in); 40 | if (getsockname(s, (struct sockaddr *)&al, &alen)) { 41 | warnp("getsockname"); 42 | goto err0; 43 | } 44 | alen = sizeof(struct sockaddr_in); 45 | if (getpeername(s, (struct sockaddr *)&ar, &alen)) { 46 | warnp("getpeername"); 47 | goto err0; 48 | } 49 | 50 | /* Make sure that we got AF_INET addresses. */ 51 | if ((al.sin_family != AF_INET) || (ar.sin_family != AF_INET)) { 52 | warn0("HTTP connection is not IPv4!"); 53 | goto err0; 54 | } 55 | 56 | /* 57 | * Construct the ident query. Note that we send the *remote* address 58 | * and port first because what we see as remote is seen by the filter 59 | * daemon as local and vice versa. 60 | */ 61 | memcpy(&idreq[0], &ar.sin_addr, 4); 62 | memcpy(&idreq[4], &ar.sin_port, 2); 63 | memcpy(&idreq[6], &al.sin_addr, 4); 64 | memcpy(&idreq[10], &al.sin_port, 2); 65 | 66 | /* Connect to the ident service and wrap into a FILE. */ 67 | if ((s_id = sock_connect_blocking(id)) == -1) { 68 | warnp("sock_connect_blocking"); 69 | goto err0; 70 | } 71 | if ((f_id = fdopen(s_id, "r+")) == NULL) { 72 | warnp("fdopen"); 73 | close(s_id); 74 | goto err0; 75 | } 76 | 77 | /* Write the query. */ 78 | if (fwrite(idreq, 12, 1, f_id) != 1) { 79 | warnp("fwrite"); 80 | goto err1; 81 | } 82 | 83 | /* 84 | * Read the user ID into an intmax_t; we don't know how large a uid_t 85 | * is so we can't ask fscanf to parse directly into there. 86 | */ 87 | if (fscanf(f_id, "%jd\n", &i) != 1) { 88 | warn0("Could not parse uid from ident daemon!"); 89 | goto err1; 90 | } 91 | *uid = (uid_t)i; 92 | 93 | /* Allocate an elastic array of gids. */ 94 | if ((gs = gidlist_init(0)) == NULL) 95 | goto err1; 96 | 97 | /* Read the group IDs. */ 98 | while (fscanf(f_id, "%jd,", &i) == 1) { 99 | g = (gid_t)i; 100 | if (gidlist_append(gs, &g, 1)) 101 | goto err2; 102 | } 103 | 104 | /* We should have read at least one gid. */ 105 | if (gidlist_getsize(gs) == 0) { 106 | warn0("Did not read any gids from ident daemon!"); 107 | goto err2; 108 | } 109 | 110 | /* Export the array. */ 111 | gidlist_export(gs, gids, ngid); 112 | 113 | /* Close the connection to the ident service. */ 114 | fclose(f_id); 115 | 116 | /* Success! */ 117 | return (0); 118 | 119 | err2: 120 | gidlist_free(gs); 121 | err1: 122 | fclose(f_id); 123 | err0: 124 | /* Failure! */ 125 | return (-1); 126 | } 127 | -------------------------------------------------------------------------------- /imds-proxy/imds-proxy.h: -------------------------------------------------------------------------------- 1 | #ifndef IMDS_PROXY_H 2 | #define IMDS_PROXY_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /* Opaque type. */ 9 | struct sock_addr; 10 | struct imds_conf; 11 | 12 | /** 13 | * http_proxy(s, dst, id, imdsc): 14 | * Read an HTTP request from the socket ${s} and forward it to address ${dst}, 15 | * after querying ${id} about the owner of the incoming connection and 16 | * checking against the ruleset ${imdsc}. 17 | */ 18 | void http_proxy(int, struct sock_addr * const *, struct sock_addr * const *, 19 | const struct imds_conf *); 20 | 21 | /** 22 | * request_read(f, req, path): 23 | * Read an HTTP request from ${f}. Store an HTTP/1.0 request (which may be 24 | * identical or may be reconstructed with the same semantic meaning) in 25 | * ${req}, and a normalized IMDS request path in ${path}. 26 | */ 27 | int request_read(FILE *, char **, char **); 28 | 29 | /** 30 | * uri2path(uri, path): 31 | * Extract the path from the HTTP Request-URI ${uri}, normalize it, and 32 | * return it via ${path}. 33 | */ 34 | int uri2path(const char *, char **); 35 | 36 | /** 37 | * ident(s, id, uid, gids, ngid): 38 | * Query ${id} about the ownership of the process holding the other end of 39 | * the socket ${s}; return the user ID via ${uid}, a malloced array of group 40 | * IDs via ${gids}, and the number of group IDs via ${ngid}. 41 | */ 42 | int ident(int, struct sock_addr * const *, uid_t *, gid_t **, size_t *); 43 | 44 | /** 45 | * conf_read(path): 46 | * Read the imds-proxy configuration file ${path} and return a state which 47 | * can be passed to conf_check or conf_free. 48 | */ 49 | struct imds_conf * conf_read(const char *); 50 | 51 | /** 52 | * conf_check(imdsc, path, uid, gids, ngid): 53 | * Check whether the specified uid/gids is allowed to make this request; 54 | * return nonzero if the request is allowed. 55 | */ 56 | int conf_check(const struct imds_conf *, const char *, 57 | uid_t, gid_t *, size_t); 58 | 59 | /** 60 | * conf_free(imdsc): 61 | * Free the configuration state ${imdsc}. 62 | */ 63 | void conf_free(struct imds_conf *); 64 | 65 | #endif /* !IMDS_PROXY_H */ 66 | -------------------------------------------------------------------------------- /imds-proxy/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "daemonize.h" 13 | #include "getopt.h" 14 | #include "setuidgid.h" 15 | #include "sock.h" 16 | #include "warnp.h" 17 | 18 | #include "imds-proxy.h" 19 | 20 | /* Connection parameters. */ 21 | struct cstate { 22 | int s; 23 | struct sock_addr ** sas_t; 24 | struct sock_addr ** sas_id; 25 | struct imds_conf * imdsc; 26 | }; 27 | 28 | /* Handle the connection. */ 29 | static void * 30 | handleconn(void * cookie) 31 | { 32 | struct cstate * cs = cookie; 33 | 34 | /* Do the work for this connection. */ 35 | http_proxy(cs->s, cs->sas_t, cs->sas_id, cs->imdsc); 36 | 37 | /* Free the state structure. */ 38 | free(cs); 39 | 40 | /* We're done. */ 41 | return (NULL); 42 | } 43 | 44 | static void 45 | usage(void) 46 | { 47 | 48 | fprintf(stderr, "usage: imds-proxy [-f ] [-p ]\n" 49 | " [-u | <:group> | ]\n"); 50 | exit(1); 51 | } 52 | 53 | int 54 | main(int argc, char * argv[]) 55 | { 56 | struct sock_addr ** sas_t; 57 | struct sock_addr ** sas_id; 58 | struct imds_conf * imdsc; 59 | struct cstate * cs; 60 | struct sockaddr_in sin; 61 | const char * ch; 62 | const char * opt_f = NULL; 63 | const char * opt_p = NULL; 64 | const char * opt_u = NULL; 65 | pthread_t thr; 66 | int s; 67 | int rc; 68 | int one = 1; 69 | 70 | WARNP_INIT; 71 | 72 | /* Parse command line. */ 73 | while ((ch = GETOPT(argc, argv)) != NULL) { 74 | GETOPT_SWITCH(ch) { 75 | GETOPT_OPTARG("-f"): 76 | GETOPT_OPTARG("--conffile"): 77 | if (opt_f) 78 | usage(); 79 | opt_f = optarg; 80 | break; 81 | GETOPT_OPTARG("-p"): 82 | GETOPT_OPTARG("--pidfile"): 83 | if (opt_p) 84 | usage(); 85 | opt_p = optarg; 86 | break; 87 | GETOPT_OPTARG("-u"): 88 | GETOPT_OPTARG("--uidgid"): 89 | if (opt_u) 90 | usage(); 91 | opt_u = optarg; 92 | break; 93 | GETOPT_MISSING_ARG: 94 | warn0("Missing argument to %s", ch); 95 | usage(); 96 | GETOPT_DEFAULT: 97 | warn0("illegal option -- %s", ch); 98 | usage(); 99 | } 100 | } 101 | 102 | /* Check for unused arguments. */ 103 | if (argc > optind) 104 | usage(); 105 | 106 | /* Default configuration file. */ 107 | if (opt_f == NULL) 108 | opt_f = "/usr/local/etc/imds.conf"; 109 | 110 | /* Default pidfile. */ 111 | if (opt_p == NULL) 112 | opt_p = "/var/run/imds-proxy.pid"; 113 | 114 | /* Target address. */ 115 | if ((sas_t = sock_resolve("/var/run/imds.sock")) == NULL) { 116 | warnp("sock_resolve"); 117 | goto err0; 118 | } 119 | 120 | /* Ident socket. */ 121 | if ((sas_id = sock_resolve("/var/run/imds-ident.sock")) == NULL) { 122 | warnp("sock_resolve"); 123 | goto err1; 124 | } 125 | 126 | /* Read the configuration file. */ 127 | if ((imdsc = conf_read(opt_f)) == NULL) { 128 | warnp("Could not read configuration file: %s", opt_f); 129 | goto err2; 130 | } 131 | 132 | /* Bind to 0.0.0.0:80 and accept connections. */ 133 | memset(&sin, 0, sizeof(struct sockaddr_in)); 134 | sin.sin_len = sizeof(struct sockaddr_in); 135 | sin.sin_family = AF_INET; 136 | sin.sin_addr.s_addr = INADDR_ANY; 137 | sin.sin_port = htons(80); 138 | if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 139 | warnp("socket"); 140 | goto err3; 141 | } 142 | /* Set SO_REUSEADDR. */ 143 | if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one))) { 144 | warnp("setsockopt(SO_REUSEADDR)"); 145 | goto err4; 146 | } 147 | if (bind(s, (struct sockaddr *)&sin, sin.sin_len)) { 148 | warnp("bind"); 149 | goto err4; 150 | } 151 | if (listen(s, 10)) { 152 | warnp("listen"); 153 | goto err4; 154 | } 155 | 156 | /* Daemonize. */ 157 | if (daemonize(opt_p)) { 158 | warnp("daemonize"); 159 | goto err4; 160 | } 161 | 162 | /* Drop privileges (if applicable). */ 163 | if (opt_u && setuidgid(opt_u, SETUIDGID_SGROUP_LEAVE_WARN)) { 164 | warnp("Failed to drop privileges"); 165 | goto err4; 166 | } 167 | 168 | /* Accept connections until an error occurs. */ 169 | do { 170 | if ((cs = malloc(sizeof(struct cstate))) == NULL) { 171 | warnp("malloc"); 172 | goto die; 173 | } 174 | while ((cs->s = accept(s, NULL, NULL)) == -1) { 175 | if (errno == EINTR) 176 | continue; 177 | warnp("accept"); 178 | goto die; 179 | } 180 | cs->sas_t = sas_t; 181 | cs->sas_id = sas_id; 182 | cs->imdsc = imdsc; 183 | if ((rc = pthread_create(&thr, NULL, handleconn, cs)) != 0) { 184 | warn0("pthread_create: %s", strerror(rc)); 185 | goto die; 186 | } 187 | } while (1); 188 | 189 | /* NOTREACHED */ 190 | 191 | die: 192 | /*- 193 | * Theoretically we might want to run 194 | * close(cs->s); 195 | * free(cs); 196 | * and then continue with other cleanup; but at this point it's 197 | * possible that threads we spawned are still running, and we need 198 | * to avoid freeing memory out from underneath them -- so instead 199 | * we just exit without worrying about cleaning up. 200 | */ 201 | exit(1); 202 | err4: 203 | close(s); 204 | err3: 205 | conf_free(imdsc); 206 | err2: 207 | sock_addr_freelist(sas_id); 208 | err1: 209 | sock_addr_freelist(sas_t); 210 | err0: 211 | exit(1); 212 | } 213 | -------------------------------------------------------------------------------- /imds-proxy/request.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "asprintf.h" 9 | #include "hexify.h" 10 | #include "warnp.h" 11 | 12 | #include "imds-proxy.h" 13 | 14 | /** 15 | * We have two goals here: 16 | * 1. Valid HTTP requests get the right response. 17 | * 2. Requests, even if not valid HTTP, cannot bypass the filtering. 18 | * In particular we need to worry about things like "request smuggling" 19 | * attacks where an invalid request is parsed differently by a filter vs 20 | * the end host; and we need to normalize requests so that filtering works 21 | * (e.g., to make sure that "/safe/path/../../dangerous/stuff" doesn't match 22 | * "/safe/path/"). 23 | * 24 | * We currently handle this by 25 | * (a) parsing the request, 26 | * (b) normalizing it, and 27 | * (c) constructing a *new* request from what we parsed, 28 | * in order to guaranteed that an invalid request can't do anything which a 29 | * valid request couldn't do. 30 | * 31 | * It would be ideal if we could the exact same request-parsing code which 32 | * the EC2 Instance Metadata Service uses, in order to guarantee bug-for-bug 33 | * compatibility; but of course that code is not publicly available. 34 | */ 35 | 36 | /* Percent-encode a request path. */ 37 | static char * 38 | urlencode(char * path) 39 | { 40 | size_t len; 41 | char * ep; 42 | char * p; 43 | char c; 44 | 45 | /* Compute an upper bound on the allocation length needed. */ 46 | len = strlen(path); 47 | if (len > (SIZE_MAX - 1) / 3) { 48 | errno = ENOMEM; 49 | goto err0; 50 | } 51 | len = 3 * len + 1; 52 | 53 | /* Allocate a buffer. */ 54 | if ((ep = malloc(len)) == NULL) 55 | goto err0; 56 | 57 | /* Fill it, one byte at a time. */ 58 | for (p = ep; *path; path++) { 59 | c = *path; 60 | if ((('a' <= c) && (c <= 'z')) || 61 | (('A' <= c) && (c <= 'Z')) || 62 | (('0' <= c) && (c <= '9')) || 63 | (c == '$') || (c == '-') || (c == '_') || 64 | (c == '.') || (c == '+') || (c == '/')) { 65 | *p++ = c; 66 | } else { 67 | *p++ = '%'; 68 | hexify((uint8_t *)&c, p, 1); 69 | p += 2; 70 | } 71 | } 72 | 73 | /* NUL-terminate. */ 74 | *p = '\0'; 75 | 76 | /* Return the encoded string. */ 77 | return (ep); 78 | 79 | err0: 80 | /* Failure! */ 81 | return (NULL); 82 | } 83 | 84 | /** 85 | * request_read(f, req, path): 86 | * Read an HTTP request from ${f}. Store an HTTP/1.0 request (which may be 87 | * identical or may be reconstructed with the same semantic meaning) in 88 | * ${req}, and a normalized IMDS request path in ${path}. 89 | */ 90 | int 91 | request_read(FILE * f, char ** req, char ** path) 92 | { 93 | char * line = NULL; 94 | size_t linecap = 0; 95 | ssize_t linelen; 96 | char * p; 97 | char * s; 98 | char * method; 99 | char * uri; 100 | char * val; 101 | char * hdr_forwarded = NULL; 102 | char * hdr_xforwardedfor = NULL; 103 | char * hdr_token = NULL; 104 | char * hdr_token_ttl = NULL; 105 | char * encpath; 106 | int hasbody; 107 | 108 | /* 109 | * Read and parse the Request-Line into " HTTP/.*". We 110 | * don't bother checking the HTTP version or verifying that there is 111 | * no trailing junk. 112 | */ 113 | if ((linelen = getline(&line, &linecap, f)) <= 0) { 114 | warnp("Could not read Request-Line"); 115 | goto err0; 116 | } 117 | (void)linelen; /* This linelen is not used beyond this point. */ 118 | if ((p = strchr(line, ' ')) == NULL) { 119 | warn0("Invalid Request-Line read"); 120 | goto err1; 121 | } else { 122 | *p = '\0'; 123 | if ((method = strdup(line)) == NULL) { 124 | warnp("strdup"); 125 | goto err1; 126 | } 127 | s = &p[1]; 128 | } 129 | if ((p = strchr(s, ' ')) == NULL) { 130 | warn0("Invalid Request-Line read"); 131 | goto err2; 132 | } else { 133 | *p = '\0'; 134 | if ((uri = strdup(s)) == NULL) { 135 | warnp("strdup"); 136 | goto err2; 137 | } 138 | s = &p[1]; 139 | } 140 | if (strncmp(s, "HTTP/", 5)) { 141 | warn0("Invalid Request-Line read"); 142 | goto err3; 143 | } 144 | 145 | /* PUT/POST have bodies; GET/HEAD don't. */ 146 | if ((strcmp(method, "PUT") == 0) || 147 | (strcmp(method, "POST") == 0)) 148 | hasbody = 1; 149 | else if ((strcmp(method, "GET") == 0) || 150 | (strcmp(method, "HEAD") == 0)) 151 | hasbody = 0; 152 | else { 153 | /* We don't understand this request; drop it. */ 154 | goto err3; 155 | } 156 | 157 | /* Extract a normalized path from the uri. */ 158 | if (uri2path(uri, path)) 159 | goto err3; 160 | 161 | /* Read headers. */ 162 | while ((linelen = getline(&line, &linecap, f)) >= 0) { 163 | /* EOF? */ 164 | if (linelen == 0) { 165 | warn0("Unexpected end of HTTP request"); 166 | goto err4; 167 | } 168 | 169 | /* Strip trailing \r\n. */ 170 | while (linelen) { 171 | if ((line[linelen - 1] != '\r') && 172 | (line[linelen - 1] != '\n')) 173 | break; 174 | line[--linelen] = '\0'; 175 | } 176 | 177 | /* End of request? */ 178 | if (linelen == 0) 179 | break; 180 | 181 | /* Split into field-name and field-value. */ 182 | if ((p = strchr(line, ':')) == NULL) { 183 | warn0("Invalid HTTP header line read"); 184 | goto err4; 185 | } 186 | *p = '\0'; 187 | val = &p[1]; 188 | 189 | /* Strip whitespace before and after the separator. */ 190 | while ((*val == ' ') || (*val == '\t')) 191 | val++; 192 | while ((p > line) && 193 | ((p[-1] == ' ') || (p[-1] == '\t'))) 194 | *p-- = '\0'; 195 | 196 | /* Make sure nobody is trying to smuggle an EOL character. */ 197 | if (strchr(p, '\r') != NULL) { 198 | warn0("HTTP header contains \\r"); 199 | goto err4; 200 | } 201 | 202 | /* Is this a header we care about? */ 203 | #define GETHDR(name, var) do { \ 204 | if (strcasecmp(line, name) == 0) { \ 205 | free(var); \ 206 | var = strdup(val); \ 207 | if (var == NULL) \ 208 | goto err4; \ 209 | } \ 210 | } while (0) 211 | GETHDR("Forwarded", hdr_forwarded); 212 | GETHDR("X-Forwarded-for", hdr_xforwardedfor); 213 | GETHDR("X-aws-ec2-metadata-token", hdr_token); 214 | GETHDR("X-aws-ec2-metadata-token-ttl-seconds", hdr_token_ttl); 215 | #undef GETHDR 216 | } 217 | 218 | /* Percent-encode the request path. */ 219 | if ((encpath = urlencode(*path)) == NULL) 220 | goto err4; 221 | 222 | /* Construct an HTTP/1.0 request. */ 223 | if (asprintf(req, 224 | "%s %s HTTP/1.0" 225 | "%s%s" 226 | "%s%s" 227 | "%s%s" 228 | "%s%s" 229 | "%s" 230 | "\r\nConnection: Close\r\n\r\n", 231 | method, encpath, 232 | #define DOHDR(name, var) var ? "\r\n" name ":" : "", var ? var : "" 233 | DOHDR("Forwarded", hdr_forwarded), 234 | DOHDR("X-Forwarded-for", hdr_xforwardedfor), 235 | DOHDR("X-aws-ec2-metadata-token", hdr_token), 236 | DOHDR("X-aws-ec2-metadata-token-ttl-seconds", hdr_token_ttl), 237 | #undef DOHDR 238 | hasbody ? "\r\nContent-Length:0" : "") == -1) 239 | goto err5; 240 | 241 | /* Clean up temporary allocated strings. */ 242 | free(encpath); 243 | free(hdr_forwarded); 244 | free(hdr_xforwardedfor); 245 | free(hdr_token); 246 | free(hdr_token_ttl); 247 | free(uri); 248 | free(method); 249 | free(line); 250 | 251 | /* Success! */ 252 | return (0); 253 | 254 | err5: 255 | free(encpath); 256 | err4: 257 | free(hdr_forwarded); 258 | free(hdr_xforwardedfor); 259 | free(hdr_token); 260 | free(hdr_token_ttl); 261 | free(*path); 262 | err3: 263 | free(uri); 264 | err2: 265 | free(method); 266 | err1: 267 | free(line); 268 | err0: 269 | /* Failure! */ 270 | return (-1); 271 | } 272 | -------------------------------------------------------------------------------- /imds-proxy/uri2path.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hexify.h" 5 | #include "warnp.h" 6 | 7 | #include "imds-proxy.h" 8 | 9 | /** 10 | * uri2path(uri, path): 11 | * Extract the path from the HTTP Request-URI ${uri}, normalize it, and 12 | * return it via ${path}. 13 | */ 14 | int 15 | uri2path(const char * uri, char ** path) 16 | { 17 | char * s; 18 | size_t pos, opos; 19 | char c; 20 | 21 | /* 22 | * Allocate a working buffer. We need up to 2 extra bytes due to 23 | * the path normalization process. 24 | */ 25 | if ((s = malloc(strlen(uri) + 3)) == NULL) 26 | goto err0; 27 | 28 | /* Start with a '/' in the path; and at the start of the uri. */ 29 | s[0] = '/'; 30 | opos = 1; 31 | pos = 0; 32 | 33 | /* Advance past a scheme if present. */ 34 | while ((c = uri[pos]) != '\0') { 35 | if ((c == ':') || (c == '/') || (c == '?') || (c == '#')) 36 | break; 37 | pos++; 38 | } 39 | if (c != ':') { 40 | /* No scheme here; go back to the start. */ 41 | pos = 0; 42 | } 43 | 44 | /* Advance past a host if present. */ 45 | if ((uri[pos] == '/') && (uri[pos + 1] == '/')) { 46 | pos += 2; 47 | while ((c = uri[pos]) != '\0') { 48 | if ((c == '/') || (c == '?') || (c == '#')) 49 | break; 50 | pos++; 51 | } 52 | } 53 | 54 | /* Copy until we hit a query string or fragment. */ 55 | while ((c = uri[pos]) != '\0') { 56 | if ((c == '?') || (c == '#')) 57 | break; 58 | s[opos++] = c; 59 | pos++; 60 | } 61 | 62 | /* 63 | * Append a '/' to the path; we'll strip it later but this makes 64 | * handling '.' and '..' path segments easier. NUL-terimate the 65 | * string. 66 | */ 67 | s[opos++] = '/'; 68 | s[opos] = '\0'; 69 | 70 | /* Scan through the path, undoing any percent-encoding. */ 71 | for (opos = pos = 0; (c = s[pos]) != '\0'; pos++) { 72 | if (c == '%') { 73 | if ((s[pos + 1] == '\0') || (s[pos + 2] == '\0') || 74 | unhexify(&s[pos + 1], (uint8_t *)&c, 1)) { 75 | /* Invalid percent-encoding. */ 76 | warn0("Invalid URI"); 77 | goto err1; 78 | } 79 | pos += 2; 80 | } 81 | s[opos] = c; 82 | } 83 | 84 | /* 85 | * Collapse empty, dot, and dotdot path segments. Each time through 86 | * the loop, pos and opos point to the character *after* the last '/' 87 | * seen. 88 | */ 89 | opos = pos = 1; 90 | while (s[pos] != '\0') { 91 | /* "//" -> "/". */ 92 | if (s[pos] == '/') { 93 | pos += 1; 94 | continue; 95 | } 96 | 97 | /* "/./" -> "/". */ 98 | if ((s[pos] == '.') && (s[pos + 1] == '/')) { 99 | pos += 2; 100 | continue; 101 | } 102 | 103 | /* If we have "/../", remove the last segment, if any. */ 104 | if ((s[pos] == '.') && (s[pos + 1] == '.') && 105 | (s[pos + 2] == '/')) { 106 | pos += 3; 107 | if (opos == 1) 108 | continue; 109 | do { 110 | opos--; 111 | } while ((opos > 1) && (s[opos - 1] != '/')); 112 | continue; 113 | } 114 | 115 | /* Copy the next segment up to and including '/'. */ 116 | do { 117 | s[opos++] = s[pos++]; 118 | } while (s[pos - 1] != '/'); 119 | } 120 | s[opos] = '\0'; 121 | 122 | /* 123 | * Remove any trailing '/' character, unless it's the entire string. 124 | * This may be one we added above, or it may be one which was part of 125 | * the original request; there can't be more than one since a pair 126 | * of consecutive '/' characters would have been collapsed above. 127 | */ 128 | if (opos > 1) 129 | s[--opos] = '\0'; 130 | 131 | /* Return the string. */ 132 | *path = s; 133 | 134 | /* Success! */ 135 | return (0); 136 | 137 | err1: 138 | free(s); 139 | err0: 140 | /* Failure! */ 141 | return (-1); 142 | } 143 | -------------------------------------------------------------------------------- /imds.conf: -------------------------------------------------------------------------------- 1 | # imds-filterd sample configuration file 2 | # ====================================== 3 | 4 | # Lines starting with '#' are comments which are ignored. 5 | 6 | # Blank lines are also ignored. 7 | 8 | # Directives are of the form 9 | # (Allow|Deny) [user name|group name] "/path/to/stuff" 10 | # and the last matching rule applies. If no rule matches, access is denied. 11 | 12 | # The path string must be quoted, and is a prefix; e.g. "/path/to/stuff" 13 | # matches a request for "/path/to/stuff/which/I/need" but not a request 14 | # for "/evil/path/to/stuff". A wildcard "*" matches any single path segment, 15 | # e.g. "/*/foo" matches "/bar/foo" but does not match "/bar/baz/foo", and may 16 | # not match a partial segment, i.e. "/a*" is a syntax error. 17 | 18 | # Start by allowing access to anything 19 | Allow "/" 20 | 21 | # Deny access to IAM Roles and Amazon's mysterious "internal use only" 22 | # credentials. 23 | Deny "/*/meta-data/iam/security-credentials/" 24 | Deny "/*/meta-data/identity-credentials/ec2/security-credentials/" 25 | 26 | # Root gets to access everything anyway. 27 | Allow user root "/" 28 | 29 | # Examples 30 | # ======== 31 | 32 | # Give a daemon access to an IAM Role: 33 | # Allow user mydaemon "/*/iam/security-credentials/myrole" 34 | 35 | # Give the "wheel" group access to everything: 36 | # Allow group wheel "/" 37 | 38 | # Blocking all access to the IMDS from a web proxy: 39 | # Deny user www "/" 40 | -------------------------------------------------------------------------------- /libcperciva/datastruct/elasticarray.h: -------------------------------------------------------------------------------- 1 | #ifndef _ELASTICARRAY_H_ 2 | #define _ELASTICARRAY_H_ 3 | 4 | #include 5 | 6 | /** 7 | * Elastic Arrays are dynamically resizing arrays which remain within a 8 | * factor of 4 of the optimal size for the data they contain and have (within 9 | * a constant factor) amortized optimal running time providing that all of 10 | * the allocated space is accessed at some point. Functions return NULL or 11 | * (int)(-1) on error and set errno; other return types indicate that failure 12 | * is not possible. On error, the array will be unmodified. 13 | * 14 | * The ELASTICARRAY_DECL(type, prefix, rectype) macro can be used to create a 15 | * more friendly interface, at the expense of restricting the array to only 16 | * holding a single data type. 17 | */ 18 | 19 | /* Opaque elastic array type. */ 20 | struct elasticarray; 21 | 22 | /** 23 | * elasticarray_init(nrec, reclen): 24 | * Create and return an elastic array holding ${nrec} (uninitialized) records 25 | * of length ${reclen}. Takes O(nrec * reclen) time. The value ${reclen} 26 | * must be positive. 27 | */ 28 | struct elasticarray * elasticarray_init(size_t, size_t); 29 | 30 | /** 31 | * elasticarray_resize(EA, nrec, reclen): 32 | * Resize the elastic array pointed to by ${EA} to hold ${nrec} records of 33 | * length ${reclen}. If ${nrec} exceeds the number of records previously 34 | * held by the array, the additional records will be uninitialized. Takes 35 | * O(nrec * reclen) time. The value ${reclen} must be positive. 36 | */ 37 | int elasticarray_resize(struct elasticarray *, size_t, size_t); 38 | 39 | /** 40 | * elasticarray_getsize(EA, reclen): 41 | * Return the number of length-${reclen} records in the array, rounding down 42 | * if there is a partial record (which can only occur if elasticarray_* 43 | * functions have been called with different values of reclen). The value 44 | * ${reclen} must be positive. 45 | */ 46 | size_t elasticarray_getsize(struct elasticarray *, size_t); 47 | 48 | /** 49 | * elasticarray_append(EA, buf, nrec, reclen): 50 | * Append to the elastic array ${EA} the ${nrec} records of length ${reclen} 51 | * stored in ${buf}. Takes O(nrec * reclen) amortized time. The value 52 | * ${reclen} must be positive. 53 | */ 54 | int elasticarray_append(struct elasticarray *, const void *, size_t, size_t); 55 | 56 | /** 57 | * elasticarray_shrink(EA, nrec, reclen): 58 | * Delete the final ${nrec} records of length ${reclen} from the elastic 59 | * array ${EA}. If there are fewer than ${nrec} records, all records 60 | * present will be deleted. The value ${reclen} must be positive. 61 | * 62 | * As an exception to the normal rule, an elastic array may occupy more than 63 | * 4 times the optimal storage immediately following an elasticarray_shrink 64 | * call; but only if realloc(3) failed to shrink a memory allocation. 65 | */ 66 | void elasticarray_shrink(struct elasticarray *, size_t, size_t); 67 | 68 | /** 69 | * elasticarray_truncate(EA): 70 | * Release any spare space in the elastic array ${EA}. 71 | */ 72 | int elasticarray_truncate(struct elasticarray *); 73 | 74 | /** 75 | * elasticarray_get(EA, pos, reclen): 76 | * Return a pointer to record number ${pos} of length ${reclen} in the 77 | * elastic array ${EA}. Takes O(1) time. 78 | */ 79 | void * elasticarray_get(struct elasticarray *, size_t, size_t); 80 | 81 | /** 82 | * elasticarray_iter(EA, reclen, fp): 83 | * Run the ${fp} function on every member of the array. The value ${reclen} 84 | * must be positive. 85 | */ 86 | void elasticarray_iter(struct elasticarray *, size_t, void(*)(void *)); 87 | 88 | /** 89 | * elasticarray_free(EA): 90 | * Free the elastic array ${EA}. Takes O(1) time. 91 | */ 92 | void elasticarray_free(struct elasticarray *); 93 | 94 | /** 95 | * elasticarray_export(EA, buf, nrec, reclen): 96 | * Return the data in the elastic array ${EA} as a buffer ${buf} containing 97 | * ${nrec} records of length ${reclen}. Free the elastic array ${EA}. 98 | * The value ${reclen} must be positive. 99 | */ 100 | int elasticarray_export(struct elasticarray *, void **, size_t *, size_t); 101 | 102 | /** 103 | * elasticarray_exportdup(EA, buf, nrec, reclen): 104 | * Duplicate the data in the elastic array ${EA} into a buffer ${buf} 105 | * containing ${nrec} records of length ${reclen}. (Same as _export, except 106 | * that the elastic array remains intact.) The value ${reclen} must be 107 | * positive. 108 | */ 109 | int elasticarray_exportdup(struct elasticarray *, void **, size_t *, size_t); 110 | 111 | /** 112 | * ELASTICARRAY_DECL(type, prefix, rectype): 113 | * Declare the type ${type} and the following functions: 114 | * ${type} ${prefix}_init(size_t nrec); 115 | * int ${prefix}_resize(${type} EA, size_t nrec); 116 | * size_t ${prefix}_getsize(${type} EA); 117 | * int ${prefix}_append(${type} EA, const void * buf, size_t nrec); 118 | * void ${prefix}_shrink(${type} EA, size_t nrec); 119 | * int ${prefix}_truncate(${type} EA); 120 | * ${rectype} * ${prefix}_get(${type} EA, size_t pos); 121 | * void ${prefix}_iter(${type} EA, void(*)(void *)); 122 | * void ${prefix}_free(${type} EA); 123 | */ 124 | #define ELASTICARRAY_DECL(type, prefix, rectype) \ 125 | static inline struct prefix##_struct * \ 126 | prefix##_init(size_t nrec) \ 127 | { \ 128 | struct elasticarray * EA; \ 129 | \ 130 | EA = elasticarray_init(nrec, sizeof(rectype)); \ 131 | return ((struct prefix##_struct *)EA); \ 132 | } \ 133 | static inline int \ 134 | prefix##_resize(struct prefix##_struct * EA, size_t nrec) \ 135 | { \ 136 | return (elasticarray_resize((struct elasticarray *)EA, \ 137 | nrec, sizeof(rectype))); \ 138 | } \ 139 | static inline size_t \ 140 | prefix##_getsize(struct prefix##_struct * EA) \ 141 | { \ 142 | return (elasticarray_getsize((struct elasticarray *)EA, \ 143 | sizeof(rectype))); \ 144 | } \ 145 | static inline int \ 146 | prefix##_append(struct prefix##_struct * EA, \ 147 | rectype const * buf, size_t nrec) \ 148 | { \ 149 | return (elasticarray_append((struct elasticarray *)EA, \ 150 | buf, nrec, sizeof(rectype))); \ 151 | } \ 152 | static inline void \ 153 | prefix##_shrink(struct prefix##_struct * EA, size_t nrec) \ 154 | { \ 155 | elasticarray_shrink((struct elasticarray *)EA, \ 156 | nrec, sizeof(rectype)); \ 157 | } \ 158 | static inline int \ 159 | prefix##_truncate(struct prefix##_struct * EA) \ 160 | { \ 161 | return (elasticarray_truncate( \ 162 | (struct elasticarray *)EA)); \ 163 | } \ 164 | static inline rectype * \ 165 | prefix##_get(struct prefix##_struct * EA, size_t pos) \ 166 | { \ 167 | rectype * rec; \ 168 | \ 169 | rec = elasticarray_get((struct elasticarray *)EA, \ 170 | pos, sizeof(rectype)); \ 171 | return (rec); \ 172 | } \ 173 | static inline void \ 174 | prefix##_iter(struct prefix##_struct * EA, \ 175 | void(* free_fp)(rectype *)) \ 176 | { \ 177 | elasticarray_iter((struct elasticarray *)EA, \ 178 | sizeof(rectype), (void (*)(void *))free_fp); \ 179 | } \ 180 | static inline void \ 181 | prefix##_free(struct prefix##_struct * EA) \ 182 | { \ 183 | elasticarray_free((struct elasticarray *)EA); \ 184 | } \ 185 | static inline int \ 186 | prefix##_export(struct prefix##_struct * EA, rectype ** buf, \ 187 | size_t * nrec) \ 188 | { \ 189 | return (elasticarray_export((struct elasticarray *)EA, \ 190 | (void **)buf, nrec, sizeof(rectype))); \ 191 | } \ 192 | static inline int \ 193 | prefix##_exportdup(struct prefix##_struct * EA, rectype ** buf, \ 194 | size_t * nrec) \ 195 | { \ 196 | return ( \ 197 | elasticarray_exportdup((struct elasticarray *)EA, \ 198 | (void **)buf, nrec, sizeof(rectype))); \ 199 | } \ 200 | static void (* prefix##_dummyptr)(void); \ 201 | static inline void \ 202 | prefix##_dummy(void) \ 203 | { \ 204 | \ 205 | (void)prefix##_init; \ 206 | (void)prefix##_resize; \ 207 | (void)prefix##_getsize; \ 208 | (void)prefix##_append; \ 209 | (void)prefix##_shrink; \ 210 | (void)prefix##_truncate; \ 211 | (void)prefix##_get; \ 212 | (void)prefix##_iter; \ 213 | (void)prefix##_free; \ 214 | (void)prefix##_export; \ 215 | (void)prefix##_exportdup; \ 216 | (void)prefix##_dummyptr; \ 217 | } \ 218 | static void (* prefix##_dummyptr)(void) = prefix##_dummy; \ 219 | typedef struct prefix##_struct * type 220 | 221 | #endif /* !_ELASTICARRAY_H_ */ 222 | -------------------------------------------------------------------------------- /libcperciva/datastruct/mpool.h: -------------------------------------------------------------------------------- 1 | #ifndef _MPOOL_H_ 2 | #define _MPOOL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * Memory allocator cache. Memory allocations can be returned to the pool 10 | * and reused by a subsequent allocation without returning all the way to 11 | * free/malloc. In effect, this is an optimization for the case where we 12 | * know we will want another allocation of the same size soon, at the expense 13 | * of keeping memory allocated (and thus preventing any other code from 14 | * allocating the same memory). 15 | */ 16 | 17 | /* Internal data. */ 18 | struct mpool { 19 | size_t stacklen; 20 | size_t allocsize; 21 | void ** allocs; 22 | uint64_t nallocs; 23 | uint64_t nempties; 24 | int state; 25 | void ** allocs_static; 26 | void (*atexitfunc)(void); 27 | }; 28 | 29 | static inline void 30 | mpool_atexit(struct mpool * M) 31 | { 32 | 33 | while (M->stacklen) 34 | free(M->allocs[--M->stacklen]); 35 | if (M->allocs != M->allocs_static) 36 | free(M->allocs); 37 | } 38 | 39 | static inline void * 40 | mpool_malloc(struct mpool * M, size_t len) 41 | { 42 | 43 | M->nallocs++; 44 | if (M->stacklen) 45 | return (M->allocs[--(M->stacklen)]); 46 | M->nempties++; 47 | if (M->state == 0) { 48 | atexit(M->atexitfunc); 49 | M->state = 1; 50 | } 51 | return (malloc(len)); 52 | } 53 | 54 | static inline void 55 | mpool_free(struct mpool * M, void * p) 56 | { 57 | void ** allocs_new; 58 | 59 | if (p == NULL) 60 | return; 61 | 62 | if (M->stacklen < M->allocsize) { 63 | M->allocs[M->stacklen++] = p; 64 | return; 65 | } 66 | 67 | if (M->nempties > (M->nallocs >> 8)) { 68 | allocs_new = (void **)malloc(M->allocsize * 2 * sizeof(void *)); 69 | if (allocs_new) { 70 | memcpy(allocs_new, M->allocs, 71 | M->allocsize * sizeof(void *)); 72 | if (M->allocs != M->allocs_static) 73 | free(M->allocs); 74 | M->allocs = allocs_new; 75 | M->allocsize = M->allocsize * 2; 76 | M->allocs[M->stacklen++] = p; 77 | } else 78 | free(p); 79 | } else 80 | free(p); 81 | M->nempties = 0; 82 | M->nallocs = 0; 83 | } 84 | 85 | /** 86 | * MPOOL(name, type, size): 87 | * Define the functions 88 | * 89 | * ${type} * mpool_${name}_malloc(void); 90 | * void mpool_${name}_free(${type} *); 91 | * 92 | * which allocate and free structures of type ${type}. A minimum of ${size} 93 | * such structures are kept cached after _free is called in order to allow 94 | * future _malloc calls to be rapidly serviced; this limit will be autotuned 95 | * upwards depending on the allocation/free pattern. 96 | * 97 | * Cached structures will be freed at program exit time in order to aid 98 | * in the detection of memory leaks. 99 | */ 100 | #define MPOOL(name, type, size) \ 101 | static void mpool_##name##_atexit(void); \ 102 | static void * mpool_##name##_static[size]; \ 103 | static struct mpool mpool_##name##_rec = \ 104 | {0, size, mpool_##name##_static, 0, 0, 0, \ 105 | mpool_##name##_static, mpool_##name##_atexit}; \ 106 | \ 107 | static void \ 108 | mpool_##name##_atexit(void) \ 109 | { \ 110 | \ 111 | mpool_atexit(&mpool_##name##_rec); \ 112 | } \ 113 | \ 114 | static inline type * \ 115 | mpool_##name##_malloc(void) \ 116 | { \ 117 | \ 118 | return (mpool_malloc(&mpool_##name##_rec, sizeof(type))); \ 119 | } \ 120 | \ 121 | static inline void \ 122 | mpool_##name##_free(type * p) \ 123 | { \ 124 | \ 125 | mpool_free(&mpool_##name##_rec, p); \ 126 | } \ 127 | \ 128 | struct mpool_##name##_dummy 129 | 130 | #endif /* !_MPOOL_H_ */ 131 | -------------------------------------------------------------------------------- /libcperciva/datastruct/ptrheap.h: -------------------------------------------------------------------------------- 1 | #ifndef _PTRHEAP_H_ 2 | #define _PTRHEAP_H_ 3 | 4 | #include 5 | 6 | /** 7 | * Pointer-heap data structure. Arbitrary pointers can be inserted and are 8 | * compared using a provided callback; the usual heapy getmin / increasemin / 9 | * deletemin algorithms are supported. To use three additional functions, 10 | * ptrheap_{delete, increase, decrease}, a setreccookie callback needs to be 11 | * provided. These functions require a record cookie to identify the element 12 | * to operate upon; each time a record's record cookie changes, the 13 | * setreccookie callback will be called. Functions return NULL or (int)(-1) 14 | * on error and set errno; other return types indicate that failure is not 15 | * possible. On error, the heap will be unmodified. 16 | */ 17 | 18 | /* Opaque pointer-heap type. */ 19 | struct ptrheap; 20 | 21 | /** 22 | * ptrheap_init(compar, setreccookie, cookie): 23 | * Create and return an empty heap. The function ${compar}(${cookie}, x, y) 24 | * should return less than, equal to, or greater than 0 depending on whether 25 | * x is less than, equal to, or greater than y; and if ${setreccookie} is 26 | * non-zero it will be called as ${setreccookie}(${cookie}, ${ptr}, ${rc}) to 27 | * indicate that the value ${rc} is the current record cookie for the pointer 28 | * ${ptr}. The function ${setreccookie} may not make any ptrheap_* calls. 29 | */ 30 | struct ptrheap * ptrheap_init(int (*)(void *, const void *, const void *), 31 | void (*)(void *, void *, size_t), void *); 32 | 33 | /** 34 | * ptrheap_create(compar, setreccookie, cookie, N, ptrs): 35 | * Create and return a heap, as in ptrheap_init, but with the ${N} pointers 36 | * in ${ptrs} as heap elements. This is faster than creating an empty heap 37 | * and adding the elements individually. 38 | */ 39 | struct ptrheap * ptrheap_create(int (*)(void *, const void *, const void *), 40 | void (*)(void *, void *, size_t), void *, size_t, void **); 41 | 42 | /** 43 | * ptrheap_add(H, ptr): 44 | * Add the pointer ${ptr} to the heap ${H}. 45 | */ 46 | int ptrheap_add(struct ptrheap *, void *); 47 | 48 | /** 49 | * ptrheap_getmin(H): 50 | * Return the minimum pointer in the heap ${H}. If the heap is empty, NULL 51 | * is returned. 52 | */ 53 | void * ptrheap_getmin(struct ptrheap *); 54 | 55 | /** 56 | * ptrheap_delete(H, rc): 57 | * Delete from the heap ${H} the element ptr for which the function call 58 | * setreccookie(cookie, ptr, ${rc}) was most recently made. 59 | */ 60 | void ptrheap_delete(struct ptrheap *, size_t); 61 | 62 | /** 63 | * ptrheap_deletemin(H): 64 | * Delete the minimum element in the heap ${H}. The heap must not be empty. 65 | */ 66 | void ptrheap_deletemin(struct ptrheap *); 67 | 68 | /** 69 | * ptrheap_decrease(H, rc): 70 | * Adjust the heap ${H} to account for the fact that the element ptr for 71 | * which the function call setreccookie(cookie, ptr, ${rc}) was most recently 72 | * made has decreased. 73 | */ 74 | void ptrheap_decrease(struct ptrheap *, size_t); 75 | 76 | /** 77 | * ptrheap_increase(H, rc): 78 | * Adjust the heap ${H} to account for the fact that the element ptr for 79 | * which the function call setreccookie(cookie, ptr, ${rc}) was most recently 80 | * made has increased. 81 | */ 82 | void ptrheap_increase(struct ptrheap *, size_t); 83 | 84 | /** 85 | * ptrheap_increasemin(H): 86 | * Adjust the heap ${H} to account for the fact that the (formerly) minimum 87 | * element has increased. 88 | */ 89 | void ptrheap_increasemin(struct ptrheap *); 90 | 91 | /** 92 | * ptrheap_free(H): 93 | * Free the pointer heap ${H}. 94 | */ 95 | void ptrheap_free(struct ptrheap *); 96 | 97 | #endif /* !_PTRHEAP_H_ */ 98 | -------------------------------------------------------------------------------- /libcperciva/datastruct/timerqueue.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "ptrheap.h" 7 | 8 | #include "timerqueue.h" 9 | 10 | struct timerqueue { 11 | struct ptrheap * H; 12 | }; 13 | 14 | struct timerrec { 15 | struct timeval tv; 16 | size_t rc; 17 | void * ptr; 18 | }; 19 | 20 | /* Compare two timevals. */ 21 | static int 22 | tvcmp(const struct timeval * x, const struct timeval * y) 23 | { 24 | 25 | /* Does one have more seconds? */ 26 | if (x->tv_sec > y->tv_sec) 27 | return (1); 28 | if (x->tv_sec < y->tv_sec) 29 | return (-1); 30 | 31 | /* Does one have more microseconds? */ 32 | if (x->tv_usec > y->tv_usec) 33 | return (1); 34 | if (x->tv_usec < y->tv_usec) 35 | return (-1); 36 | 37 | /* They must be equal. */ 38 | return (0); 39 | } 40 | 41 | /* Record-comparison callback from ptrheap. */ 42 | static int 43 | compar(void * cookie, const void * x, const void * y) 44 | { 45 | const struct timerrec * _x = x; 46 | const struct timerrec * _y = y; 47 | 48 | (void)cookie; /* UNUSED */ 49 | 50 | return (tvcmp(&_x->tv, &_y->tv)); 51 | } 52 | 53 | /* Cookie-recording callback from ptrheap. */ 54 | static void 55 | setreccookie(void * cookie, void * ptr, size_t rc) 56 | { 57 | struct timerrec * rec = ptr; 58 | 59 | (void)cookie; /* UNUSED */ 60 | 61 | rec->rc = rc; 62 | } 63 | 64 | /** 65 | * timerqueue_init(void): 66 | * Create and return an empty timer priority queue. 67 | */ 68 | struct timerqueue * 69 | timerqueue_init(void) 70 | { 71 | struct timerqueue * Q; 72 | 73 | /* Allocate structure. */ 74 | if ((Q = malloc(sizeof(struct timerqueue))) == NULL) 75 | goto err0; 76 | 77 | /* Allocate heap. */ 78 | if ((Q->H = ptrheap_init(compar, setreccookie, Q)) == NULL) 79 | goto err1; 80 | 81 | /* Success! */ 82 | return (Q); 83 | 84 | err1: 85 | free(Q); 86 | err0: 87 | /* Failure! */ 88 | return (NULL); 89 | } 90 | 91 | /** 92 | * timerqueue_add(Q, tv, ptr): 93 | * Add the pair (${tv}, ${ptr}) to the priority queue ${Q}. Return a cookie 94 | * which can be passed to timerqueue_delete or timerqueue_increase. 95 | */ 96 | void * 97 | timerqueue_add(struct timerqueue * Q, const struct timeval * tv, void * ptr) 98 | { 99 | struct timerrec * r; 100 | 101 | /* Allocate (timeval, ptr) pair record. */ 102 | if ((r = malloc(sizeof(struct timerrec))) == NULL) 103 | goto err0; 104 | 105 | /* Fill in values. */ 106 | memcpy(&r->tv, tv, sizeof(struct timeval)); 107 | r->ptr = ptr; 108 | 109 | /* 110 | * Add the record to the heap. The value r->rc will be filled in 111 | * by setreccookie which will be called by ptrheap_add. 112 | */ 113 | if (ptrheap_add(Q->H, r)) 114 | goto err1; 115 | 116 | /* Success! */ 117 | return (r); 118 | 119 | err1: 120 | free(r); 121 | err0: 122 | /* Failure! */ 123 | return (NULL); 124 | } 125 | 126 | /** 127 | * timerqueue_delete(Q, cookie): 128 | * Delete the (timeval, ptr) pair associated with the cookie ${cookie} from 129 | * the priority queue ${Q}. 130 | */ 131 | void 132 | timerqueue_delete(struct timerqueue * Q, void * cookie) 133 | { 134 | struct timerrec * r = cookie; 135 | 136 | /* Remove the record from the heap. */ 137 | ptrheap_delete(Q->H, r->rc); 138 | 139 | /* Free the record. */ 140 | free(r); 141 | } 142 | 143 | /** 144 | * timerqueue_increase(Q, cookie, tv): 145 | * Increase the timer associated with the cookie ${cookie} in the priority 146 | * queue ${Q} to ${tv}. 147 | */ 148 | void 149 | timerqueue_increase(struct timerqueue * Q, void * cookie, 150 | const struct timeval * tv) 151 | { 152 | struct timerrec * r = cookie; 153 | 154 | /* Adjust timer value. */ 155 | memcpy(&r->tv, tv, sizeof(struct timeval)); 156 | 157 | /* Inform the heap that the record value has increased. */ 158 | ptrheap_increase(Q->H, r->rc); 159 | } 160 | 161 | /** 162 | * timerqueue_getmin(Q): 163 | * Return a pointer to the least timeval in ${Q}, or NULL if the priority 164 | * queue is empty. The pointer will remain valid until the next call to a 165 | * timerqueue_* function. This function cannot fail. 166 | */ 167 | const struct timeval * 168 | timerqueue_getmin(struct timerqueue * Q) 169 | { 170 | struct timerrec * r; 171 | 172 | /* Get the minimum element from the heap. */ 173 | r = ptrheap_getmin(Q->H); 174 | 175 | /* If we have an element, return its timeval; otherwise, NULL. */ 176 | if (r != NULL) 177 | return (&r->tv); 178 | else 179 | return (NULL); 180 | } 181 | 182 | /** 183 | * timerqueue_getptr(Q, tv): 184 | * If the least timeval in ${Q} is less than or equal to ${tv}, return the 185 | * associated pointer and remove the pair from the priority queue. If not, 186 | * return NULL. This function cannot fail. 187 | */ 188 | void * 189 | timerqueue_getptr(struct timerqueue * Q, const struct timeval * tv) 190 | { 191 | struct timerrec * r; 192 | void * ptr; 193 | 194 | /* 195 | * Get the minimum element from the heap. Return NULL if the heap 196 | * has no minimum element (i.e., is empty). 197 | */ 198 | if ((r = ptrheap_getmin(Q->H)) == NULL) 199 | return (NULL); 200 | 201 | /* If the minimum timeval is greater than ${tv}, return NULL. */ 202 | if (tvcmp(&r->tv, tv) > 0) 203 | return (NULL); 204 | 205 | /* Remove this record from the heap. */ 206 | ptrheap_deletemin(Q->H); 207 | 208 | /* Extract its pointer. */ 209 | ptr = r->ptr; 210 | 211 | /* Free the record. */ 212 | free(r); 213 | 214 | /* 215 | * And finally return the pointer which was associated with the 216 | * (formerly) minimum timeval in the heap. 217 | */ 218 | return (ptr); 219 | } 220 | 221 | /** 222 | * timerqueue_free(Q): 223 | * Free the timer priority queue ${Q}. 224 | */ 225 | void 226 | timerqueue_free(struct timerqueue * Q) 227 | { 228 | struct timerrec * r; 229 | 230 | /* Behave consistently with free(NULL). */ 231 | if (Q == NULL) 232 | return; 233 | 234 | /* Extract elements from the heap and free them one by one. */ 235 | while ((r = ptrheap_getmin(Q->H)) != NULL) { 236 | free(r); 237 | ptrheap_deletemin(Q->H); 238 | } 239 | 240 | /* Free the heap. */ 241 | ptrheap_free(Q->H); 242 | 243 | /* Free the timer priority queue structure. */ 244 | free(Q); 245 | } 246 | -------------------------------------------------------------------------------- /libcperciva/datastruct/timerqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _TIMERQUEUE_H_ 2 | #define _TIMERQUEUE_H_ 3 | 4 | #include 5 | 6 | /* Timer priority queue. Contains (timeval, ptr) pairs. */ 7 | 8 | /* Opaque timer priority queue type. */ 9 | struct timerqueue; 10 | 11 | /** 12 | * timerqueue_init(void): 13 | * Create and return an empty timer priority queue. 14 | */ 15 | struct timerqueue * timerqueue_init(void); 16 | 17 | /** 18 | * timerqueue_add(Q, tv, ptr): 19 | * Add the pair (${tv}, ${ptr}) to the priority queue ${Q}. Return a cookie 20 | * which can be passed to timerqueue_delete or timerqueue_increase. 21 | */ 22 | void * timerqueue_add(struct timerqueue *, const struct timeval *, void *); 23 | 24 | /** 25 | * timerqueue_delete(Q, cookie): 26 | * Delete the (timeval, ptr) pair associated with the cookie ${cookie} from 27 | * the priority queue ${Q}. 28 | */ 29 | void timerqueue_delete(struct timerqueue *, void *); 30 | 31 | /** 32 | * timerqueue_increase(Q, cookie, tv): 33 | * Increase the timer associated with the cookie ${cookie} in the priority 34 | * queue ${Q} to ${tv}. 35 | */ 36 | void timerqueue_increase(struct timerqueue *, void *, const struct timeval *); 37 | 38 | /** 39 | * timerqueue_getmin(Q): 40 | * Return a pointer to the least timeval in ${Q}, or NULL if the priority 41 | * queue is empty. The pointer will remain valid until the next call to a 42 | * timerqueue_* function. This function cannot fail. 43 | */ 44 | const struct timeval * timerqueue_getmin(struct timerqueue *); 45 | 46 | /** 47 | * timerqueue_getptr(Q, tv): 48 | * If the least timeval in ${Q} is less than or equal to ${tv}, return the 49 | * associated pointer and remove the pair from the priority queue. If not, 50 | * return NULL. This function cannot fail. 51 | */ 52 | void * timerqueue_getptr(struct timerqueue *, const struct timeval *); 53 | 54 | /** 55 | * timerqueue_free(Q): 56 | * Free the timer priority queue ${Q}. 57 | */ 58 | void timerqueue_free(struct timerqueue *); 59 | 60 | #endif /* !_TIMERQUEUE_H_ */ 61 | -------------------------------------------------------------------------------- /libcperciva/events/events.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mpool.h" 8 | 9 | #include "events.h" 10 | #include "events_internal.h" 11 | 12 | /* Event structure. */ 13 | struct eventrec { 14 | int (*func)(void *); 15 | void * cookie; 16 | }; 17 | 18 | MPOOL(eventrec, struct eventrec, 4096); 19 | 20 | /* Zero timeval, for use with non-blocking event runs. */ 21 | static const struct timeval tv_zero = {0, 0}; 22 | 23 | /* We want to interrupt a running event loop. */ 24 | static volatile sig_atomic_t interrupt_requested = 0; 25 | 26 | /** 27 | * events_mkrec(func, cookie): 28 | * Package ${func}, ${cookie} into a struct eventrec. 29 | */ 30 | struct eventrec * 31 | events_mkrec(int (*func)(void *), void * cookie) 32 | { 33 | struct eventrec * r; 34 | 35 | /* Allocate structure. */ 36 | if ((r = mpool_eventrec_malloc()) == NULL) 37 | goto err0; 38 | 39 | /* Initialize. */ 40 | r->func = func; 41 | r->cookie = cookie; 42 | 43 | /* Success! */ 44 | return (r); 45 | 46 | err0: 47 | /* Failure! */ 48 | return (NULL); 49 | } 50 | 51 | /** 52 | * events_freerec(r): 53 | * Free the eventrec ${r}. 54 | */ 55 | void 56 | events_freerec(struct eventrec * r) 57 | { 58 | 59 | mpool_eventrec_free(r); 60 | } 61 | 62 | /* Do an event. This makes events_run cleaner. */ 63 | static inline int 64 | doevent(struct eventrec * r) 65 | { 66 | int rc; 67 | 68 | /* Invoke the callback. */ 69 | rc = (r->func)(r->cookie); 70 | 71 | /* Free the event record. */ 72 | mpool_eventrec_free(r); 73 | 74 | /* Return the status code from the callback. */ 75 | return (rc); 76 | } 77 | 78 | /** 79 | * events_run(void): 80 | * Run events. Events registered via events_immediate_register will be run 81 | * first, in order of increasing ${prio} values; then events associated with 82 | * ready sockets registered via events_network_register; finally, events 83 | * associated with expired timers registered via events_timer_register will 84 | * be run. If any event function returns a non-zero result, no further 85 | * events will be run and said non-zero result will be returned; on error, 86 | * -1 will be returned. May be interrupted by events_interrupt, in which case 87 | * 0 will be returned. If there are runnable events, events_run is guaranteed 88 | * to run at least one; but it may return while there are still more runnable 89 | * events. 90 | */ 91 | static int 92 | _events_run(void) 93 | { 94 | struct eventrec * r; 95 | struct timeval * tv; 96 | struct timeval tv2; 97 | int rc = 0; 98 | 99 | /* If we have any immediate events, process them and return. */ 100 | if ((r = events_immediate_get()) != NULL) { 101 | while (r != NULL) { 102 | /* Process the event. */ 103 | if ((rc = doevent(r)) != 0) 104 | goto done; 105 | 106 | /* Interrupt loop if requested. */ 107 | if (interrupt_requested) 108 | goto done; 109 | 110 | /* Get the next event. */ 111 | r = events_immediate_get(); 112 | } 113 | 114 | /* We've processed at least one event; time to return. */ 115 | goto done; 116 | } 117 | 118 | /* 119 | * Figure out the maximum duration to block, and wait up to that 120 | * duration for network events to become available. 121 | */ 122 | if (events_timer_min(&tv)) 123 | goto err0; 124 | if (events_network_select(tv, &interrupt_requested)) 125 | goto err1; 126 | free(tv); 127 | 128 | /* 129 | * Check for available immediate events, network events, and timer 130 | * events, in that order of priority; exit only when no more events 131 | * are available or when interrupted. 132 | */ 133 | do { 134 | /* Interrupt loop if requested. */ 135 | if (interrupt_requested) 136 | goto done; 137 | 138 | /* Run an immediate event, if one is available. */ 139 | if ((r = events_immediate_get()) != NULL) { 140 | if ((rc = doevent(r)) != 0) 141 | goto done; 142 | continue; 143 | } 144 | 145 | /* Run a network event, if one is available. */ 146 | if ((r = events_network_get()) != NULL) { 147 | if ((rc = doevent(r)) != 0) 148 | goto done; 149 | continue; 150 | } 151 | 152 | /* Check if any new network events are available. */ 153 | memcpy(&tv2, &tv_zero, sizeof(struct timeval)); 154 | if (events_network_select(&tv2, &interrupt_requested)) 155 | goto err0; 156 | if ((r = events_network_get()) != NULL) { 157 | if ((rc = doevent(r)) != 0) 158 | goto done; 159 | continue; 160 | } 161 | 162 | /* Run a timer event, if one is available. */ 163 | if (events_timer_get(&r)) 164 | goto err0; 165 | if (r != NULL) { 166 | if ((rc = doevent(r)) != 0) 167 | goto done; 168 | continue; 169 | } 170 | 171 | /* No events available. */ 172 | break; 173 | } while (1); 174 | 175 | done: 176 | /* Success! */ 177 | return (rc); 178 | 179 | err1: 180 | free(tv); 181 | err0: 182 | /* Failure! */ 183 | return (-1); 184 | } 185 | 186 | /* Wrapper function for events_run to reset interrupt_requested. */ 187 | int 188 | events_run(void) 189 | { 190 | int rc; 191 | 192 | /* Call the real function. */ 193 | rc = _events_run(); 194 | 195 | /* Reset interrupt_requested after quitting the loop. */ 196 | interrupt_requested = 0; 197 | 198 | /* Return status. */ 199 | return (rc); 200 | } 201 | 202 | /** 203 | * events_spin(done): 204 | * Call events_run until ${done} is non-zero (and return 0), an error occurs (and 205 | * return -1), or a callback returns a non-zero status (and return the status 206 | * code from the callback). May be interrupted by events_interrupt (and return 207 | * 0). 208 | */ 209 | int 210 | events_spin(int * done) 211 | { 212 | int rc = 0; 213 | 214 | /* Loop until we're done or have a non-zero status. */ 215 | while ((done[0] == 0) && (rc == 0) && (interrupt_requested == 0)) { 216 | /* Run events. */ 217 | rc = _events_run(); 218 | } 219 | 220 | /* Reset interrupt_requested after quitting the loop. */ 221 | interrupt_requested = 0; 222 | 223 | /* Return status code. */ 224 | return (rc); 225 | } 226 | 227 | /** 228 | * events_interrupt(void): 229 | * Halt the event loop after finishing the current event. This function can 230 | * be safely called from within a signal handler. 231 | */ 232 | void 233 | events_interrupt(void) 234 | { 235 | 236 | /* Interrupt the event loop. */ 237 | interrupt_requested = 1; 238 | } 239 | 240 | /** 241 | * events_shutdown(void): 242 | * Deprecated function; does nothing. 243 | */ 244 | void 245 | events_shutdown(void) 246 | { 247 | } 248 | -------------------------------------------------------------------------------- /libcperciva/events/events.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVENTS_H_ 2 | #define _EVENTS_H_ 3 | 4 | #include 5 | 6 | /** 7 | * events_immediate_register(func, cookie, prio): 8 | * Register ${func}(${cookie}) to be run the next time events_run is invoked, 9 | * after immediate events with smaller ${prio} values and before events with 10 | * larger ${prio} values. The value ${prio} must be in the range [0, 31]. 11 | * Return a cookie which can be passed to events_immediate_cancel. 12 | */ 13 | void * events_immediate_register(int (*)(void *), void *, int); 14 | 15 | /** 16 | * events_immediate_cancel(cookie): 17 | * Cancel the immediate event for which the cookie ${cookie} was returned by 18 | * events_immediate_register. 19 | */ 20 | void events_immediate_cancel(void *); 21 | 22 | /* "op" parameter to events_network_register. */ 23 | #define EVENTS_NETWORK_OP_READ 0 24 | #define EVENTS_NETWORK_OP_WRITE 1 25 | 26 | /** 27 | * events_network_register(func, cookie, s, op): 28 | * Register ${func}(${cookie}) to be run when socket ${s} is ready for 29 | * reading or writing depending on whether ${op} is EVENTS_NETWORK_OP_READ or 30 | * EVENTS_NETWORK_OP_WRITE. If there is already an event registration for 31 | * this ${s}/${op} pair, errno will be set to EEXIST and the function will 32 | * fail. 33 | */ 34 | int events_network_register(int (*)(void *), void *, int, int); 35 | 36 | /** 37 | * events_network_cancel(s, op): 38 | * Cancel the event registered for the socket/operation pair ${s}/${op}. If 39 | * there is no such registration, errno will be set to ENOENT and the 40 | * function will fail. 41 | */ 42 | int events_network_cancel(int, int); 43 | 44 | /** 45 | * events_network_selectstats(N, mu, va, max): 46 | * Return statistics on the inter-select durations since the last time this 47 | * function was called. 48 | */ 49 | void events_network_selectstats(double *, double *, double *, double *); 50 | 51 | /** 52 | * events_timer_register(func, cookie, timeo): 53 | * Register ${func}(${cookie}) to be run ${timeo} in the future. Return a 54 | * cookie which can be passed to events_timer_cancel or events_timer_reset. 55 | */ 56 | void * events_timer_register(int (*)(void *), void *, const struct timeval *); 57 | 58 | /** 59 | * events_timer_register_double(func, cookie, timeo): 60 | * As events_timer_register, but ${timeo} is a double-precision floating point 61 | * value specifying a number of seconds. 62 | */ 63 | void * events_timer_register_double(int (*)(void *), void *, double); 64 | 65 | /** 66 | * events_timer_cancel(cookie): 67 | * Cancel the timer for which the cookie ${cookie} was returned by 68 | * events_timer_register. 69 | */ 70 | void events_timer_cancel(void *); 71 | 72 | /** 73 | * events_timer_reset(cookie): 74 | * Reset the timer for which the cookie ${cookie} was returned by 75 | * events_timer_register to its initial value. 76 | */ 77 | int events_timer_reset(void *); 78 | 79 | /** 80 | * events_run(void): 81 | * Run events. Events registered via events_immediate_register will be run 82 | * first, in order of increasing ${prio} values; then events associated with 83 | * ready sockets registered via events_network_register; finally, events 84 | * associated with expired timers registered via events_timer_register will 85 | * be run. If any event function returns a non-zero result, no further 86 | * events will be run and said non-zero result will be returned; on error, 87 | * -1 will be returned. May be interrupted by events_interrupt, in which case 88 | * 0 will be returned. If there are runnable events, events_run is guaranteed 89 | * to run at least one; but it may return while there are still more runnable 90 | * events. 91 | */ 92 | int events_run(void); 93 | 94 | /** 95 | * events_spin(done): 96 | * Call events_run until ${done} is non-zero (and return 0), an error occurs (and 97 | * return -1), or a callback returns a non-zero status (and return the status 98 | * code from the callback). May be interrupted by events_interrupt (and return 99 | * 0). 100 | */ 101 | int events_spin(int *); 102 | 103 | /** 104 | * events_interrupt(void): 105 | * Halt the event loop after finishing the current event. This function can 106 | * be safely called from within a signal handler. 107 | */ 108 | void events_interrupt(void); 109 | 110 | /** 111 | * events_shutdown(void): 112 | * Deprecated function; does nothing. 113 | */ 114 | void events_shutdown(void); 115 | 116 | #endif /* !_EVENTS_H_ */ 117 | -------------------------------------------------------------------------------- /libcperciva/events/events_immediate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "mpool.h" 5 | 6 | #include "events.h" 7 | #include "events_internal.h" 8 | 9 | struct eventq { 10 | struct eventrec * r; 11 | struct eventq * next; 12 | struct eventq * prev; 13 | int prio; 14 | }; 15 | 16 | MPOOL(eventq, struct eventq, 4096); 17 | 18 | /* First nodes in the linked lists. */ 19 | static struct eventq * heads[32] = { 20 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 21 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 22 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 23 | NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL 24 | }; 25 | 26 | /* For non-NULL heads[i], tails[i] is the last node in the list. */ 27 | static struct eventq * tails[32]; 28 | 29 | /* For i < minq, heads[i] == NULL. */ 30 | static int minq = 32; 31 | 32 | /** 33 | * events_immediate_register(func, cookie, prio): 34 | * Register ${func}(${cookie}) to be run the next time events_run is invoked, 35 | * after immediate events with smaller ${prio} values and before events with 36 | * larger ${prio} values. The value ${prio} must be in the range [0, 31]. 37 | * Return a cookie which can be passed to events_immediate_cancel. 38 | */ 39 | void * 40 | events_immediate_register(int (*func)(void *), void * cookie, int prio) 41 | { 42 | struct eventrec * r; 43 | struct eventq * q; 44 | 45 | /* Sanity check. */ 46 | assert((prio >= 0) && (prio < 32)); 47 | 48 | /* Bundle into an eventrec record. */ 49 | if ((r = events_mkrec(func, cookie)) == NULL) 50 | goto err0; 51 | 52 | /* Create a linked list node. */ 53 | if ((q = mpool_eventq_malloc()) == NULL) 54 | goto err1; 55 | q->r = r; 56 | q->next = NULL; 57 | q->prev = NULL; 58 | q->prio = prio; 59 | 60 | /* Add to the queue. */ 61 | if (heads[prio] == NULL) { 62 | heads[prio] = q; 63 | if (prio < minq) 64 | minq = prio; 65 | } else { 66 | tails[prio]->next = q; 67 | q->prev = tails[prio]; 68 | } 69 | tails[prio] = q; 70 | 71 | /* Success! */ 72 | return (q); 73 | 74 | err1: 75 | events_freerec(r); 76 | err0: 77 | /* Failure! */ 78 | return (NULL); 79 | } 80 | 81 | /** 82 | * events_immediate_cancel(cookie): 83 | * Cancel the immediate event for which the cookie ${cookie} was returned by 84 | * events_immediate_register. 85 | */ 86 | void 87 | events_immediate_cancel(void * cookie) 88 | { 89 | struct eventq * q = cookie; 90 | int prio = q->prio; 91 | 92 | /* If we have a predecessor, point it at our successor. */ 93 | if (q->prev != NULL) 94 | q->prev->next = q->next; 95 | else 96 | heads[prio] = q->next; 97 | 98 | /* If we have a successor, point it at our predecessor. */ 99 | if (q->next != NULL) 100 | q->next->prev = q->prev; 101 | else 102 | tails[prio] = q->prev; 103 | 104 | /* Free the eventrec. */ 105 | events_freerec(q->r); 106 | 107 | /* Return the node to the malloc pool. */ 108 | mpool_eventq_free(q); 109 | } 110 | 111 | /** 112 | * events_immediate_get(void): 113 | * Remove and return an eventrec structure from the immediate event queue, 114 | * or return NULL if there are no such events. The caller is responsible for 115 | * freeing the returned memory. 116 | */ 117 | struct eventrec * 118 | events_immediate_get(void) 119 | { 120 | struct eventq * q; 121 | struct eventrec * r; 122 | 123 | /* Advance past priorities which have no events. */ 124 | while ((minq < 32) && (heads[minq] == NULL)) 125 | minq++; 126 | 127 | /* Are there any events? */ 128 | if (minq == 32) 129 | return (NULL); 130 | 131 | /* 132 | * Remove the first node from the highest priority non-empty linked 133 | * list. 134 | */ 135 | q = heads[minq]; 136 | heads[minq] = q->next; 137 | if (heads[minq] != NULL) 138 | heads[minq]->prev = NULL; 139 | 140 | /* Extract the eventrec. */ 141 | r = q->r; 142 | 143 | /* Return the node to the malloc pool. */ 144 | mpool_eventq_free(q); 145 | 146 | /* Return the eventrec. */ 147 | return (r); 148 | } 149 | -------------------------------------------------------------------------------- /libcperciva/events/events_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef _EVENTS_INTERNAL_H_ 2 | #define _EVENTS_INTERNAL_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | /* Opaque event structure. */ 9 | struct eventrec; 10 | 11 | /** 12 | * events_mkrec(func, cookie): 13 | * Package ${func}, ${cookie} into a struct eventrec. 14 | */ 15 | struct eventrec * events_mkrec(int (*)(void *), void *); 16 | 17 | /** 18 | * events_freerec(r): 19 | * Free the eventrec ${r}. 20 | */ 21 | void events_freerec(struct eventrec *); 22 | 23 | /** 24 | * events_immediate_get(void): 25 | * Remove and return an eventrec structure from the immediate event queue, 26 | * or return NULL if there are no such events. The caller is responsible for 27 | * freeing the returned memory. 28 | */ 29 | struct eventrec * events_immediate_get(void); 30 | 31 | /** 32 | * events_network_select(tv, interrupt_requested): 33 | * Check for socket readiness events, waiting up to ${tv} time if there are 34 | * no sockets immediately ready, or indefinitely if ${tv} is NULL. The value 35 | * stored in ${tv} may be modified. If ${*interrupt_requested} is non-zero 36 | * and a signal is received, exit. 37 | */ 38 | int events_network_select(struct timeval *, volatile sig_atomic_t *); 39 | 40 | /** 41 | * events_network_selectstats_startclock(void): 42 | * Start the inter-select duration clock: There is a selectable event. 43 | */ 44 | void events_network_selectstats_startclock(void); 45 | 46 | /** 47 | * events_network_selectstats_stopclock(void): 48 | * Stop the inter-select duration clock: There are no selectable events. 49 | */ 50 | void events_network_selectstats_stopclock(void); 51 | 52 | /** 53 | * events_network_selectstats_select(void): 54 | * Update inter-select duration statistics in relation to an upcoming 55 | * select(2) call. 56 | */ 57 | void events_network_selectstats_select(void); 58 | 59 | /** 60 | * events_network_get(void): 61 | * Find a socket readiness event which was identified by a previous call to 62 | * events_network_select, and return it as an eventrec structure; or return 63 | * NULL if there are no such events available. The caller is responsible for 64 | * freeing the returned memory. 65 | */ 66 | struct eventrec * events_network_get(void); 67 | 68 | /** 69 | * events_timer_min(timeo): 70 | * Return via ${timeo} a pointer to the minimum time which must be waited 71 | * before a timer will expire; or to NULL if there are no timers. The caller 72 | * is responsible for freeing the returned pointer. 73 | */ 74 | int events_timer_min(struct timeval **); 75 | 76 | /** 77 | * events_timer_get(r): 78 | * Return via ${r} a pointer to an eventrec structure corresponding to an 79 | * expired timer, and delete said timer; or to NULL if there are no expired 80 | * timers. The caller is responsible for freeing the returned pointer. 81 | */ 82 | int events_timer_get(struct eventrec **); 83 | 84 | #endif /* !_EVENTS_INTERNAL_H_ */ 85 | -------------------------------------------------------------------------------- /libcperciva/events/events_network_selectstats.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "monoclock.h" 4 | 5 | #include "events.h" 6 | #include "events_internal.h" 7 | 8 | /* Time when inter-select duration clock started. */ 9 | static struct timeval st; 10 | static int running = 0; 11 | 12 | /* Statistics on inter-select durations. */ 13 | static double N = 0.0; 14 | static double mu = 0.0; 15 | static double M2 = 0.0; 16 | static double max = 0.0; 17 | 18 | /** 19 | * events_network_selectstats_startclock(void): 20 | * Start the inter-select duration clock: There is a selectable event. 21 | */ 22 | void 23 | events_network_selectstats_startclock(void) 24 | { 25 | 26 | /* If the clock is already running, return silently. */ 27 | if (running) 28 | return; 29 | 30 | /* Get the current time; return silently on error. */ 31 | if (monoclock_get(&st)) 32 | return; 33 | 34 | /* The clock is now running. */ 35 | running = 1; 36 | } 37 | 38 | /** 39 | * events_network_selectstats_stopclock(void): 40 | * Stop the inter-select duration clock: There are no selectable events. 41 | */ 42 | void 43 | events_network_selectstats_stopclock(void) 44 | { 45 | 46 | /* The clock is no longer running. */ 47 | running = 0; 48 | } 49 | 50 | /** 51 | * events_network_selectstats_select(void): 52 | * Update inter-select duration statistics in relation to an upcoming 53 | * select(2) call. 54 | */ 55 | void 56 | events_network_selectstats_select(void) 57 | { 58 | struct timeval tnow; 59 | double t, d; 60 | 61 | /* If the clock is not running, return silently. */ 62 | if (!running) 63 | return; 64 | 65 | /* If we can't get the current time, fail silently. */ 66 | if (monoclock_get(&tnow)) 67 | goto done; 68 | 69 | /* Compute inter-select duration in seconds. */ 70 | t = timeval_diff(st, tnow); 71 | 72 | /* Adjust statistics. We track running mean, variance * N, and max. */ 73 | N += 1.0; 74 | d = t - mu; 75 | mu += d / N; 76 | M2 += d * (t - mu); 77 | if (max < t) 78 | max = t; 79 | 80 | done: 81 | /* The clock is no longer running. */ 82 | running = 0; 83 | } 84 | 85 | /** 86 | * events_network_selectstats(N, mu, va, max): 87 | * Return statistics on the inter-select durations since the last time this 88 | * function was called. 89 | */ 90 | void 91 | events_network_selectstats(double * _N, double * _mu, double * _va, 92 | double * _max) 93 | { 94 | 95 | /* Copy statistics out. */ 96 | *_N = N; 97 | *_mu = mu; 98 | if (N > 1.0) 99 | *_va = M2 / (N - 1.0); 100 | else 101 | *_va = 0.0; 102 | *_max = max; 103 | 104 | /* Zero statistics. */ 105 | N = mu = M2 = max = 0.0; 106 | } 107 | -------------------------------------------------------------------------------- /libcperciva/events/events_timer.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "monoclock.h" 7 | #include "timerqueue.h" 8 | 9 | #include "events.h" 10 | #include "events_internal.h" 11 | 12 | struct timerrec { 13 | struct eventrec * r; 14 | void * cookie; 15 | struct timeval tv_orig; 16 | }; 17 | 18 | /* This also tracks whether we've initialized the atexit function. */ 19 | static struct timerqueue * Q = NULL; 20 | 21 | static void events_timer_shutdown(void); 22 | 23 | /* Set tv := + tdelta. */ 24 | static int 25 | gettimeout(struct timeval * tv, struct timeval * tdelta) 26 | { 27 | 28 | if (monoclock_get(tv)) 29 | goto err0; 30 | tv->tv_sec += tdelta->tv_sec; 31 | if ((tv->tv_usec += tdelta->tv_usec) >= 1000000) { 32 | tv->tv_usec -= 1000000; 33 | tv->tv_sec += 1; 34 | } 35 | 36 | /* Success! */ 37 | return (0); 38 | 39 | err0: 40 | /* Failure! */ 41 | return (-1); 42 | } 43 | 44 | /** 45 | * events_timer_register(func, cookie, timeo): 46 | * Register ${func}(${cookie}) to be run ${timeo} in the future. Return a 47 | * cookie which can be passed to events_timer_cancel or events_timer_reset. 48 | */ 49 | void * 50 | events_timer_register(int (*func)(void *), void * cookie, 51 | const struct timeval * timeo) 52 | { 53 | struct eventrec * r; 54 | struct timerrec * t; 55 | struct timeval tv; 56 | 57 | /* Create the timer queue if it doesn't exist yet. */ 58 | if (Q == NULL) { 59 | if ((Q = timerqueue_init()) == NULL) 60 | goto err0; 61 | 62 | /* Clean up the timer queue at exit. */ 63 | if (atexit(events_timer_shutdown)) 64 | goto err0; 65 | } 66 | 67 | /* Bundle into an eventrec record. */ 68 | if ((r = events_mkrec(func, cookie)) == NULL) 69 | goto err0; 70 | 71 | /* Create a timer record. */ 72 | if ((t = malloc(sizeof(struct timerrec))) == NULL) 73 | goto err1; 74 | t->r = r; 75 | memcpy(&t->tv_orig, timeo, sizeof(struct timeval)); 76 | 77 | /* Compute the absolute timeout. */ 78 | if (gettimeout(&tv, &t->tv_orig)) 79 | goto err2; 80 | 81 | /* Add this to the timer queue. */ 82 | if ((t->cookie = timerqueue_add(Q, &tv, t)) == NULL) 83 | goto err2; 84 | 85 | /* Success! */ 86 | return (t); 87 | 88 | err2: 89 | free(t); 90 | err1: 91 | events_freerec(r); 92 | err0: 93 | /* Failure! */ 94 | return (NULL); 95 | } 96 | 97 | /** 98 | * events_timer_register_double(func, cookie, timeo): 99 | * As events_timer_register, but ${timeo} is a double-precision floating point 100 | * value specifying a number of seconds. 101 | */ 102 | void * 103 | events_timer_register_double(int (*func)(void *), void * cookie, 104 | double timeo) 105 | { 106 | struct timeval tv; 107 | 108 | /* Convert timeo to a struct timeval. */ 109 | tv.tv_sec = (time_t)timeo; 110 | tv.tv_usec = (suseconds_t)((timeo - (double)tv.tv_sec) * 1000000.0); 111 | 112 | /* Schedule the timeout. */ 113 | return (events_timer_register(func, cookie, &tv)); 114 | } 115 | 116 | /** 117 | * events_timer_cancel(cookie): 118 | * Cancel the timer for which the cookie ${cookie} was returned by 119 | * events_timer_register. 120 | */ 121 | void 122 | events_timer_cancel(void * cookie) 123 | { 124 | struct timerrec * t = cookie; 125 | 126 | /* Remove from the timer queue. */ 127 | timerqueue_delete(Q, t->cookie); 128 | 129 | /* Free the eventrec and timer records. */ 130 | events_freerec(t->r); 131 | free(t); 132 | } 133 | 134 | /** 135 | * events_timer_reset(cookie): 136 | * Reset the timer for which the cookie ${cookie} was returned by 137 | * events_timer_register to its initial value. 138 | */ 139 | int 140 | events_timer_reset(void * cookie) 141 | { 142 | struct timerrec * t = cookie; 143 | struct timeval tv; 144 | 145 | /* Compute the new timeout. */ 146 | if (gettimeout(&tv, &t->tv_orig)) 147 | goto err0; 148 | 149 | /* Adjust the timer. */ 150 | timerqueue_increase(Q, t->cookie, &tv); 151 | 152 | /* Success! */ 153 | return (0); 154 | 155 | err0: 156 | /* Failure! */ 157 | return (-1); 158 | } 159 | 160 | /** 161 | * events_timer_min(timeo): 162 | * Return via ${timeo} a pointer to the minimum time which must be waited 163 | * before a timer will expire; or to NULL if there are no timers. The caller 164 | * is responsible for freeing the returned pointer. 165 | */ 166 | int 167 | events_timer_min(struct timeval ** timeo) 168 | { 169 | struct timeval tnow; 170 | const struct timeval * tv; 171 | 172 | /* If we have no queue, we have no timers; return NULL. */ 173 | if (Q == NULL) { 174 | *timeo = NULL; 175 | goto done; 176 | } 177 | 178 | /* Get the minimum timer from the queue. */ 179 | tv = timerqueue_getmin(Q); 180 | 181 | /* If there are no timers, return NULL. */ 182 | if (tv == NULL) { 183 | *timeo = NULL; 184 | goto done; 185 | } 186 | 187 | /* Allocate space for holding the returned timeval. */ 188 | if ((*timeo = malloc(sizeof(struct timeval))) == NULL) 189 | goto err0; 190 | 191 | /* Get the current time... */ 192 | if (monoclock_get(&tnow)) 193 | goto err1; 194 | 195 | /* ... and compare it to the minimum timer. */ 196 | if ((tnow.tv_sec > tv->tv_sec) || 197 | ((tnow.tv_sec == tv->tv_sec) && (tnow.tv_usec > tv->tv_usec))) { 198 | /* The timer has already expired, so return zero. */ 199 | (*timeo)->tv_sec = 0; 200 | (*timeo)->tv_usec = 0; 201 | } else { 202 | /* Compute the difference. */ 203 | (*timeo)->tv_sec = tv->tv_sec - tnow.tv_sec; 204 | (*timeo)->tv_usec = tv->tv_usec - tnow.tv_usec; 205 | if (tv->tv_usec < tnow.tv_usec) { 206 | (*timeo)->tv_usec += 1000000; 207 | (*timeo)->tv_sec -= 1; 208 | } 209 | } 210 | 211 | done: 212 | /* Success! */ 213 | return (0); 214 | 215 | err1: 216 | free(*timeo); 217 | err0: 218 | /* Failure! */ 219 | return (-1); 220 | } 221 | 222 | /** 223 | * events_timer_get(r): 224 | * Return via ${r} a pointer to an eventrec structure corresponding to an 225 | * expired timer, and delete said timer; or to NULL if there are no expired 226 | * timers. The caller is responsible for freeing the returned pointer. 227 | */ 228 | int 229 | events_timer_get(struct eventrec ** r) 230 | { 231 | struct timeval tnow; 232 | struct timerrec * t; 233 | 234 | /* If we have no queue, we have no timers; return NULL. */ 235 | if (Q == NULL) { 236 | *r = NULL; 237 | goto done; 238 | } 239 | 240 | /* Get current time. */ 241 | if (monoclock_get(&tnow)) 242 | goto err0; 243 | 244 | /* Get an expired timer, if there is one. */ 245 | t = timerqueue_getptr(Q, &tnow); 246 | 247 | /* If there is an expired timer... */ 248 | if (t != NULL) { 249 | /* ... pass back the eventrec and free the timer. */ 250 | *r = t->r; 251 | free(t); 252 | } else { 253 | /* Otherwise, return NULL. */ 254 | *r = NULL; 255 | } 256 | 257 | done: 258 | /* Success! */ 259 | return (0); 260 | 261 | err0: 262 | /* Failure! */ 263 | return (-1); 264 | } 265 | 266 | /** 267 | * events_timer_shutdown(void): 268 | * Clean up and free memory. This should run automatically via atexit. 269 | */ 270 | static void 271 | events_timer_shutdown(void) 272 | { 273 | 274 | /* If we have a queue and it is empty, free it. */ 275 | if ((Q != NULL) && (timerqueue_getmin(Q) == NULL)) { 276 | timerqueue_free(Q); 277 | Q = NULL; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /libcperciva/network/network.h: -------------------------------------------------------------------------------- 1 | #ifndef _NETWORK_H_ 2 | #define _NETWORK_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | /* Opaque address structure. */ 11 | struct sock_addr; 12 | 13 | /** 14 | * network_accept(fd, callback, cookie): 15 | * Asynchronously accept a connection on the socket ${fd}, which must be 16 | * already marked as listening and non-blocking. When a connection has been 17 | * accepted or an error occurs, invoke ${callback}(${cookie}, s) where s is 18 | * the accepted connection or -1 on error. Return a cookie which can be 19 | * passed to network_accept_cancel in order to cancel the accept. 20 | */ 21 | void * network_accept(int, int (*)(void *, int), void *); 22 | 23 | /** 24 | * network_accept_cancel(cookie): 25 | * Cancel the connection accept for which the cookie ${cookie} was returned 26 | * by network_accept. Do not invoke the callback associated with the accept. 27 | */ 28 | void network_accept_cancel(void *); 29 | 30 | /** 31 | * network_connect(sas, callback, cookie): 32 | * Iterate through the addresses in ${sas}, attempting to create and connect 33 | * a non-blocking socket. Once connected, invoke ${callback}(${cookie}, s) 34 | * where s is the connected socket; upon fatal error or if there are no 35 | * addresses remaining to attempt, invoke ${callback}(${cookie}, -1). Return 36 | * a cookie which can be passed to network_connect_cancel in order to cancel 37 | * the connection attempt. 38 | */ 39 | void * network_connect(struct sock_addr * const *, 40 | int (*)(void *, int), void *); 41 | 42 | /** 43 | * network_connect_timeo(sas, timeo, callback, cookie): 44 | * Behave as network_connect, but wait a duration of at most ${timeo} for 45 | * each address which is being attempted. 46 | */ 47 | void * network_connect_timeo(struct sock_addr * const *, const struct timeval *, 48 | int (*)(void *, int), void *); 49 | 50 | /** 51 | * network_connect_cancel(cookie): 52 | * Cancel the connection attempt for which ${cookie} was returned by 53 | * network_connect. Do not invoke the associated callback. 54 | */ 55 | void network_connect_cancel(void *); 56 | 57 | /** 58 | * network_read(fd, buf, buflen, minread, callback, cookie): 59 | * Asynchronously read up to ${buflen} bytes of data from ${fd} into ${buf}. 60 | * When at least ${minread} bytes have been read or on error, invoke 61 | * ${callback}(${cookie}, lenread), where lenread is 0 on EOF or -1 on error, 62 | * and the number of bytes read (between ${minread} and ${buflen} inclusive) 63 | * otherwise. Return a cookie which can be passed to network_read_cancel in 64 | * order to cancel the read. 65 | */ 66 | void * network_read(int, uint8_t *, size_t, size_t, 67 | int (*)(void *, ssize_t), void *); 68 | 69 | /** 70 | * network_read_cancel(cookie): 71 | * Cancel the buffer read for which the cookie ${cookie} was returned by 72 | * network_read. Do not invoke the callback associated with the read. 73 | */ 74 | void network_read_cancel(void *); 75 | 76 | /** 77 | * network_write(fd, buf, buflen, minwrite, callback, cookie): 78 | * Asynchronously write up to ${buflen} bytes of data from ${buf} to ${fd}. 79 | * When at least ${minwrite} bytes have been written or on error, invoke 80 | * ${callback}(${cookie}, lenwrit), where lenwrit is -1 on error and the 81 | * number of bytes written (between ${minwrite} and ${buflen} inclusive) 82 | * otherwise. Return a cookie which can be passed to network_write_cancel in 83 | * order to cancel the write. 84 | */ 85 | void * network_write(int, const uint8_t *, size_t, size_t, 86 | int (*)(void *, ssize_t), void *); 87 | 88 | /** 89 | * network_write_cancel(cookie): 90 | * Cancel the buffer write for which the cookie ${cookie} was returned by 91 | * network_write. Do not invoke the callback associated with the write. 92 | */ 93 | void network_write_cancel(void *); 94 | 95 | #endif /* !_NETWORK_H_ */ 96 | -------------------------------------------------------------------------------- /libcperciva/network/network_accept.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "events.h" 7 | 8 | #include "network.h" 9 | 10 | struct accept_cookie { 11 | int (* callback)(void *, int); 12 | void * cookie; 13 | int fd; 14 | }; 15 | 16 | /* Accept the connection and invoke the callback. */ 17 | static int 18 | callback_accept(void * cookie) 19 | { 20 | struct accept_cookie * C = cookie; 21 | int s; 22 | int rc; 23 | 24 | /* Attempt to accept a new connection. */ 25 | if ((s = accept(C->fd, NULL, NULL)) == -1) { 26 | /* If a connection isn't available, reset the callback. */ 27 | if ((errno == EAGAIN) || 28 | (errno == EWOULDBLOCK) || 29 | (errno == ECONNABORTED) || 30 | (errno == EINTR)) 31 | goto tryagain; 32 | } 33 | 34 | /* Call the upstream callback. */ 35 | rc = (C->callback)(C->cookie, s); 36 | 37 | /* Free the cookie. */ 38 | free(C); 39 | 40 | /* Return status from upstream callback. */ 41 | return (rc); 42 | 43 | tryagain: 44 | /* Reset the callback. */ 45 | return (events_network_register(callback_accept, C, C->fd, 46 | EVENTS_NETWORK_OP_READ)); 47 | } 48 | 49 | /** 50 | * network_accept(fd, callback, cookie): 51 | * Asynchronously accept a connection on the socket ${fd}, which must be 52 | * already marked as listening and non-blocking. When a connection has been 53 | * accepted or an error occurs, invoke ${callback}(${cookie}, s) where s is 54 | * the accepted connection or -1 on error. Return a cookie which can be 55 | * passed to network_accept_cancel in order to cancel the accept. 56 | */ 57 | void * 58 | network_accept(int fd, int (* callback)(void *, int), void * cookie) 59 | { 60 | struct accept_cookie * C; 61 | 62 | /* Bake a cookie. */ 63 | if ((C = malloc(sizeof(struct accept_cookie))) == NULL) 64 | goto err0; 65 | C->callback = callback; 66 | C->cookie = cookie; 67 | C->fd = fd; 68 | 69 | /* 70 | * Register a network event. A connection arriving on a listening 71 | * socket is treated by select(2) as the socket becoming readable. 72 | */ 73 | if (events_network_register(callback_accept, C, C->fd, 74 | EVENTS_NETWORK_OP_READ)) 75 | goto err1; 76 | 77 | /* Success! */ 78 | return (C); 79 | 80 | err1: 81 | free(C); 82 | err0: 83 | /* Failure! */ 84 | return (NULL); 85 | } 86 | 87 | /** 88 | * network_accept_cancel(cookie): 89 | * Cancel the connection accept for which the cookie ${cookie} was returned 90 | * by network_accept. Do not invoke the callback associated with the accept. 91 | */ 92 | void 93 | network_accept_cancel(void * cookie) 94 | { 95 | struct accept_cookie * C = cookie; 96 | 97 | /* Cancel the network event. */ 98 | events_network_cancel(C->fd, EVENTS_NETWORK_OP_READ); 99 | 100 | /* Free the cookie. */ 101 | free(C); 102 | } 103 | -------------------------------------------------------------------------------- /libcperciva/network/network_read.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "events.h" 11 | #include "mpool.h" 12 | 13 | #include "network.h" 14 | 15 | struct network_read_cookie { 16 | int (*callback)(void *, ssize_t); 17 | void * cookie; 18 | int fd; 19 | uint8_t * buf; 20 | size_t buflen; 21 | size_t minlen; 22 | size_t bufpos; 23 | }; 24 | 25 | MPOOL(network_read_cookie, struct network_read_cookie, 16); 26 | 27 | /* Invoke the callback, clean up, and return the callback's status. */ 28 | static int 29 | docallback(struct network_read_cookie * C, ssize_t nbytes) 30 | { 31 | int rc; 32 | 33 | /* Invoke the callback. */ 34 | rc = (C->callback)(C->cookie, nbytes); 35 | 36 | /* Clean up. */ 37 | mpool_network_read_cookie_free(C); 38 | 39 | /* Return the callback's status. */ 40 | return (rc); 41 | } 42 | 43 | /* The socket is ready for reading/writing. */ 44 | static int 45 | callback_buf(void * cookie) 46 | { 47 | struct network_read_cookie * C = cookie; 48 | size_t oplen; 49 | ssize_t len; 50 | 51 | /* Attempt to read/write data to/from the buffer. */ 52 | oplen = C->buflen - C->bufpos; 53 | len = recv(C->fd, C->buf + C->bufpos, oplen, 0); 54 | 55 | /* Failure? */ 56 | if (len == -1) { 57 | /* Was it really an error, or just a try-again? */ 58 | if ((errno == EAGAIN) || 59 | (errno == EWOULDBLOCK) || 60 | (errno == EINTR)) 61 | goto tryagain; 62 | 63 | /* Something went wrong. */ 64 | goto failed; 65 | } else if (len == 0) { 66 | /* The socket was shut down by the remote host. */ 67 | goto eof; 68 | } 69 | 70 | /* We processed some data. */ 71 | C->bufpos += (size_t)len; 72 | 73 | /* Do we need to keep going? */ 74 | if (C->bufpos < C->minlen) 75 | goto tryagain; 76 | 77 | /* Sanity-check: buffer position must fit into a ssize_t. */ 78 | assert(C->bufpos <= SSIZE_MAX); 79 | 80 | /* Invoke the callback and return. */ 81 | return (docallback(C, (ssize_t)C->bufpos)); 82 | 83 | tryagain: 84 | /* Reset the event. */ 85 | if (events_network_register(callback_buf, C, C->fd, 86 | EVENTS_NETWORK_OP_READ)) 87 | goto failed; 88 | 89 | /* Callback was reset. */ 90 | return (0); 91 | 92 | eof: 93 | /* Invoke the callback with an EOF status and return. */ 94 | return (docallback(C, 0)); 95 | 96 | failed: 97 | /* Invoke the callback with a failure status and return. */ 98 | return (docallback(C, -1)); 99 | } 100 | 101 | /** 102 | * network_read(fd, buf, buflen, minread, callback, cookie): 103 | * Asynchronously read up to ${buflen} bytes of data from ${fd} into ${buf}. 104 | * When at least ${minread} bytes have been read or on error, invoke 105 | * ${callback}(${cookie}, lenread), where lenread is 0 on EOF or -1 on error, 106 | * and the number of bytes read (between ${minread} and ${buflen} inclusive) 107 | * otherwise. Return a cookie which can be passed to network_read_cancel in 108 | * order to cancel the read. 109 | */ 110 | void * 111 | network_read(int fd, uint8_t * buf, size_t buflen, size_t minread, 112 | int (* callback)(void *, ssize_t), void * cookie) 113 | { 114 | struct network_read_cookie * C; 115 | 116 | /* Make sure buflen is non-zero. */ 117 | assert(buflen != 0); 118 | 119 | /* Sanity-check: # bytes must fit into a ssize_t. */ 120 | assert(buflen <= SSIZE_MAX); 121 | 122 | /* Bake a cookie. */ 123 | if ((C = mpool_network_read_cookie_malloc()) == NULL) 124 | goto err0; 125 | C->callback = callback; 126 | C->cookie = cookie; 127 | C->fd = fd; 128 | C->buf = buf; 129 | C->buflen = buflen; 130 | C->minlen = minread; 131 | C->bufpos = 0; 132 | 133 | /* Register a callback for network readiness. */ 134 | if (events_network_register(callback_buf, C, C->fd, 135 | EVENTS_NETWORK_OP_READ)) 136 | goto err1; 137 | 138 | /* Success! */ 139 | return (C); 140 | 141 | err1: 142 | mpool_network_read_cookie_free(C); 143 | err0: 144 | /* Failure! */ 145 | return (NULL); 146 | } 147 | 148 | /** 149 | * network_read_cancel(cookie): 150 | * Cancel the buffer read for which the cookie ${cookie} was returned by 151 | * network_read. Do not invoke the callback associated with the read. 152 | */ 153 | void 154 | network_read_cancel(void * cookie) 155 | { 156 | struct network_read_cookie * C = cookie; 157 | 158 | /* Kill the network event. */ 159 | events_network_cancel(C->fd, EVENTS_NETWORK_OP_READ); 160 | 161 | /* Free the cookie. */ 162 | mpool_network_read_cookie_free(C); 163 | } 164 | -------------------------------------------------------------------------------- /libcperciva/network/network_write.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "events.h" 12 | #include "mpool.h" 13 | #include "warnp.h" 14 | 15 | #include "network.h" 16 | 17 | /** 18 | * POSIX.1-2008 requires that MSG_NOSIGNAL be defined as a flag for send(2) 19 | * which has the effect of preventing SIGPIPE from being raised when writing 20 | * to a descriptor which has been shut down. Unfortunately there are some 21 | * platforms which are not POSIX.1-2008 compliant; we provide a workaround 22 | * (-DPOSIXFAIL_MSG_NOSIGNAL) which instead blocks the SIGPIPE signal on such 23 | * platforms. 24 | * 25 | * (This workaround could be used automatically, but requiring that it be 26 | * explicitly enabled helps to get platforms fixed.) 27 | */ 28 | #ifdef POSIXFAIL_MSG_NOSIGNAL 29 | #ifndef MSG_NOSIGNAL 30 | #define MSG_NOSIGNAL 0 31 | #endif 32 | #endif 33 | 34 | struct network_write_cookie { 35 | int (*callback)(void *, ssize_t); 36 | void * cookie; 37 | int fd; 38 | const uint8_t * buf; 39 | size_t buflen; 40 | size_t minlen; 41 | size_t bufpos; 42 | }; 43 | 44 | MPOOL(network_write_cookie, struct network_write_cookie, 16); 45 | 46 | /* Invoke the callback, clean up, and return the callback's status. */ 47 | static int 48 | docallback(struct network_write_cookie * C, ssize_t nbytes) 49 | { 50 | int rc; 51 | 52 | /* Invoke the callback. */ 53 | rc = (C->callback)(C->cookie, nbytes); 54 | 55 | /* Clean up. */ 56 | mpool_network_write_cookie_free(C); 57 | 58 | /* Return the callback's status. */ 59 | return (rc); 60 | } 61 | 62 | /* The socket is ready for reading/writing. */ 63 | static int 64 | callback_buf(void * cookie) 65 | { 66 | struct network_write_cookie * C = cookie; 67 | size_t oplen; 68 | ssize_t len; 69 | #ifdef POSIXFAIL_MSG_NOSIGNAL 70 | void (*oldsig)(int); 71 | #endif 72 | 73 | /* If we don't have MSG_NOSIGNAL, catch SIGPIPE. */ 74 | #ifdef POSIXFAIL_MSG_NOSIGNAL 75 | if ((oldsig = signal(SIGPIPE, SIG_IGN)) == SIG_ERR) { 76 | warnp("signal(SIGPIPE)"); 77 | goto failed; 78 | } 79 | #endif 80 | 81 | /* Attempt to read/write data to/from the buffer. */ 82 | oplen = C->buflen - C->bufpos; 83 | len = send(C->fd, C->buf + C->bufpos, oplen, MSG_NOSIGNAL); 84 | 85 | /* We should never see a send length of zero. */ 86 | assert(len != 0); 87 | 88 | /* If we set a SIGPIPE handler, restore the old one. */ 89 | #ifdef POSIXFAIL_MSG_NOSIGNAL 90 | if (signal(SIGPIPE, oldsig) == SIG_ERR) { 91 | warnp("signal(SIGPIPE)"); 92 | goto failed; 93 | } 94 | #endif 95 | 96 | /* Failure? */ 97 | if (len == -1) { 98 | /* Was it really an error, or just a try-again? */ 99 | if ((errno == EAGAIN) || 100 | (errno == EWOULDBLOCK) || 101 | (errno == EINTR)) 102 | goto tryagain; 103 | 104 | /* Something went wrong. */ 105 | goto failed; 106 | } 107 | 108 | /* We processed some data. */ 109 | C->bufpos += (size_t)len; 110 | 111 | /* Do we need to keep going? */ 112 | if (C->bufpos < C->minlen) 113 | goto tryagain; 114 | 115 | /* Sanity-check: buffer position must fit into a ssize_t. */ 116 | assert(C->bufpos <= SSIZE_MAX); 117 | 118 | /* Invoke the callback and return. */ 119 | return (docallback(C, (ssize_t)C->bufpos)); 120 | 121 | tryagain: 122 | /* Reset the event. */ 123 | if (events_network_register(callback_buf, C, C->fd, 124 | EVENTS_NETWORK_OP_WRITE)) 125 | goto failed; 126 | 127 | /* Callback was reset. */ 128 | return (0); 129 | 130 | failed: 131 | /* Invoke the callback with a failure status and return. */ 132 | return (docallback(C, -1)); 133 | } 134 | 135 | /** 136 | * network_write(fd, buf, buflen, minwrite, callback, cookie): 137 | * Asynchronously write up to ${buflen} bytes of data from ${buf} to ${fd}. 138 | * When at least ${minwrite} bytes have been written or on error, invoke 139 | * ${callback}(${cookie}, lenwrit), where lenwrit is -1 on error and the 140 | * number of bytes written (between ${minwrite} and ${buflen} inclusive) 141 | * otherwise. Return a cookie which can be passed to network_write_cancel in 142 | * order to cancel the write. 143 | */ 144 | void * 145 | network_write(int fd, const uint8_t * buf, size_t buflen, size_t minwrite, 146 | int (* callback)(void *, ssize_t), void * cookie) 147 | { 148 | struct network_write_cookie * C; 149 | 150 | /* Make sure buflen is non-zero. */ 151 | assert(buflen != 0); 152 | 153 | /* Sanity-check: # bytes must fit into a ssize_t. */ 154 | assert(buflen <= SSIZE_MAX); 155 | 156 | /* Bake a cookie. */ 157 | if ((C = mpool_network_write_cookie_malloc()) == NULL) 158 | goto err0; 159 | C->callback = callback; 160 | C->cookie = cookie; 161 | C->fd = fd; 162 | C->buf = buf; 163 | C->buflen = buflen; 164 | C->minlen = minwrite; 165 | C->bufpos = 0; 166 | 167 | /* Register a callback for network readiness. */ 168 | if (events_network_register(callback_buf, C, C->fd, 169 | EVENTS_NETWORK_OP_WRITE)) 170 | goto err1; 171 | 172 | /* Success! */ 173 | return (C); 174 | 175 | err1: 176 | mpool_network_write_cookie_free(C); 177 | err0: 178 | /* Failure! */ 179 | return (NULL); 180 | } 181 | 182 | /** 183 | * network_write_cancel(cookie): 184 | * Cancel the buffer write for which the cookie ${cookie} was returned by 185 | * network_write. Do not invoke the callback associated with the write. 186 | */ 187 | void 188 | network_write_cancel(void * cookie) 189 | { 190 | struct network_write_cookie * C = cookie; 191 | 192 | /* Kill the network event. */ 193 | events_network_cancel(C->fd, EVENTS_NETWORK_OP_WRITE); 194 | 195 | /* Free the cookie. */ 196 | mpool_network_write_cookie_free(C); 197 | } 198 | -------------------------------------------------------------------------------- /libcperciva/util/asprintf.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "asprintf.h" 6 | 7 | /** 8 | * asprintf(ret, format, ...): 9 | * Do asprintf(3) like GNU and BSD do. 10 | */ 11 | int 12 | asprintf(char ** ret, const char * format, ...) 13 | { 14 | va_list ap; 15 | int len; 16 | size_t buflen; 17 | 18 | /* Figure out how long the string needs to be. */ 19 | va_start(ap, format); 20 | len = vsnprintf(NULL, 0, format, ap); 21 | va_end(ap); 22 | 23 | /* Did we fail? */ 24 | if (len < 0) 25 | goto err0; 26 | buflen = (size_t)(len) + 1; 27 | 28 | /* Allocate memory. */ 29 | if ((*ret = malloc(buflen)) == NULL) 30 | goto err0; 31 | 32 | /* Actually generate the string. */ 33 | va_start(ap, format); 34 | len = vsnprintf(*ret, buflen, format, ap); 35 | va_end(ap); 36 | 37 | /* Did we fail? */ 38 | if (len < 0) 39 | goto err1; 40 | 41 | /* Success! */ 42 | return (len); 43 | 44 | err1: 45 | free(*ret); 46 | err0: 47 | /* Failure! */ 48 | return (-1); 49 | } 50 | -------------------------------------------------------------------------------- /libcperciva/util/asprintf.h: -------------------------------------------------------------------------------- 1 | #ifndef _ASPRINTF_H_ 2 | #define _ASPRINTF_H_ 3 | 4 | /* Avoid namespace collisions with BSD/GNU asprintf. */ 5 | #ifdef asprintf 6 | #undef asprintf 7 | #endif 8 | #define asprintf libcperciva_asprintf 9 | 10 | /** 11 | * asprintf(ret, format, ...): 12 | * Do asprintf(3) like GNU and BSD do. 13 | */ 14 | int asprintf(char **, const char *, ...); 15 | 16 | #endif /* !_ASPRINTF_H_ */ 17 | -------------------------------------------------------------------------------- /libcperciva/util/ctassert.h: -------------------------------------------------------------------------------- 1 | #ifndef _CTASSERT_H_ 2 | #define _CTASSERT_H_ 3 | 4 | /* 5 | * CTASSERT(foo) will produce a compile-time error if "foo" is not a constant 6 | * expression which evaluates to a non-zero value. 7 | */ 8 | 9 | /* Kill any existing definition, just in case it's different. */ 10 | #ifdef CTASSERT 11 | #undef CTASSERT 12 | #endif 13 | 14 | /* Define using libcperciva namespace to avoid collisions. */ 15 | #define CTASSERT(x) libcperciva_CTASSERT(x, __LINE__) 16 | #define libcperciva_CTASSERT(x, y) libcperciva__CTASSERT(x, y) 17 | #define libcperciva__CTASSERT(x, y) extern char libcperciva__assert ## y[(x) ? 1 : -1] 18 | 19 | #endif /* !_CTASSERT_H_ */ 20 | -------------------------------------------------------------------------------- /libcperciva/util/daemonize.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "noeintr.h" 6 | #include "warnp.h" 7 | 8 | #include "daemonize.h" 9 | 10 | /** 11 | * daemonize(spid): 12 | * Daemonize and write the process ID in decimal to a file named ${spid}. 13 | * The parent process will exit only after the child has written its pid. 14 | * On success, the child will return 0; on failure, the parent will return 15 | * -1. 16 | */ 17 | int 18 | daemonize(const char * spid) 19 | { 20 | FILE * f; 21 | int fd[2]; 22 | char dummy = 0; 23 | 24 | /* 25 | * Create a pipe for the child to notify the parent when it has 26 | * finished daemonizing. 27 | */ 28 | if (pipe(fd)) { 29 | warnp("pipe"); 30 | goto err0; 31 | } 32 | 33 | /* 34 | * Fork into the parent process (which waits for a poke and exits) 35 | * and the child process (which keeps going). 36 | */ 37 | switch (fork()) { 38 | case -1: 39 | /* Fork failed. */ 40 | warnp("fork"); 41 | goto err2; 42 | case 0: 43 | /* In child process. */ 44 | break; 45 | default: 46 | /* 47 | * In parent process. Close write end of pipe so that if the 48 | * client dies we will notice the pipe being reset. 49 | */ 50 | while (close(fd[1])) { 51 | if (errno == EINTR) 52 | continue; 53 | warnp("close"); 54 | goto err1; 55 | } 56 | do { 57 | switch (read(fd[0], &dummy, 1)) { 58 | case -1: 59 | /* Error in read. */ 60 | break; 61 | case 0: 62 | /* EOF -- the child died without poking us. */ 63 | goto err1; 64 | case 1: 65 | /* We have been poked by the child. Exit. */ 66 | _exit(0); 67 | } 68 | 69 | /* Anything other than EINTR is bad. */ 70 | if (errno != EINTR) { 71 | warnp("read"); 72 | goto err1; 73 | } 74 | } while (1); 75 | } 76 | 77 | /* Set ourselves to be a session leader. */ 78 | if (setsid() == -1) { 79 | warnp("setsid"); 80 | goto die; 81 | } 82 | 83 | /* Write out our pid file. */ 84 | if ((f = fopen(spid, "w")) == NULL) { 85 | warnp("fopen(%s)", spid); 86 | goto die; 87 | } 88 | if (fprintf(f, "%d", getpid()) < 0) { 89 | warnp("fprintf"); 90 | goto die; 91 | } 92 | if (fclose(f)) { 93 | warnp("fclose"); 94 | goto die; 95 | } 96 | 97 | /* Tell the parent to suicide. */ 98 | if (noeintr_write(fd[1], &dummy, 1) == -1) { 99 | warnp("write"); 100 | goto die; 101 | } 102 | 103 | /* Close the pipe. */ 104 | while (close(fd[0])) { 105 | if (errno == EINTR) 106 | continue; 107 | warnp("close"); 108 | goto die; 109 | } 110 | while (close(fd[1])) { 111 | if (errno == EINTR) 112 | continue; 113 | warnp("close"); 114 | goto die; 115 | } 116 | 117 | /* Success! */ 118 | return (0); 119 | 120 | err2: 121 | close(fd[1]); 122 | err1: 123 | close(fd[0]); 124 | err0: 125 | /* Failure! */ 126 | return (-1); 127 | 128 | die: 129 | /* 130 | * We're in the child and something bad happened; the parent will be 131 | * notified when we die thanks to the pipe being closed. 132 | */ 133 | _exit(0); 134 | } 135 | -------------------------------------------------------------------------------- /libcperciva/util/daemonize.h: -------------------------------------------------------------------------------- 1 | #ifndef _DAEMONIZE_H_ 2 | #define _DAEMONIZE_H_ 3 | 4 | /** 5 | * daemonize(spid): 6 | * Daemonize and write the process ID in decimal to a file named ${spid}. 7 | * The parent process will exit only after the child has written its pid. 8 | * On success, the child will return 0; on failure, the parent will return 9 | * -1. 10 | */ 11 | int daemonize(const char *); 12 | 13 | #endif /* !_DAEMONIZE_H_ */ 14 | -------------------------------------------------------------------------------- /libcperciva/util/getopt.h: -------------------------------------------------------------------------------- 1 | #ifndef _GETOPT_H_ 2 | #define _GETOPT_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * This getopt implementation parses options of the following forms: 10 | * -a -b -c foo (single-character options) 11 | * -abc foo (packed single-character options) 12 | * -abcfoo (packed single-character options and an argument) 13 | * --foo bar (long option) 14 | * --foo=bar (long option and argument separated by '=') 15 | * 16 | * It does not support abbreviated options (e.g., interpreting --foo as 17 | * --foobar when there are no other --foo* options) since that misfeature 18 | * results in breakage when new options are added. It also does not support 19 | * options appearing after non-options (e.g., "cp foo bar -R") since that is 20 | * a horrible GNU perversion. 21 | * 22 | * Upon encountering '--', it consumes that argument (by incrementing optind) 23 | * and returns NULL to signal the end of option processing. Upon encountering 24 | * a bare '-' argument or any argument not starting with '-' it returns NULL 25 | * to signal the end of option processing (without consuming the argument). 26 | * Note that these behaviours do not apply when such strings are encountered 27 | * as arguments to options; e.g., if "--foo" takes an argument, then the 28 | * command line arguments "--foo -- --bar" is interpreted as having two 29 | * options ("--foo --" and "--bar") and no left-over arguments. 30 | */ 31 | 32 | /* Work around LLVM bug. */ 33 | #ifdef __clang__ 34 | #warning Working around bug in LLVM optimizer 35 | #warning For more details see https://bugs.llvm.org/show_bug.cgi?id=27190 36 | #define DO_SETJMP _DO_SETJMP(__LINE__) 37 | #define _DO_SETJMP(x) __DO_SETJMP(x) 38 | #define __DO_SETJMP(x) \ 39 | void * getopt_initloop = && getopt_initloop_ ## x; \ 40 | getopt_initloop_ ## x: 41 | #define DO_LONGJMP \ 42 | goto *getopt_initloop 43 | #else 44 | #define DO_SETJMP \ 45 | sigjmp_buf getopt_initloop; \ 46 | if (!getopt_initialized) \ 47 | sigsetjmp(getopt_initloop, 0) 48 | #define DO_LONGJMP \ 49 | siglongjmp(getopt_initloop, 1) 50 | #endif 51 | 52 | /* Avoid namespace collisions with libc getopt. */ 53 | #define getopt libcperciva_getopt 54 | #define optarg libcperciva_optarg 55 | #define optind libcperciva_optind 56 | #define opterr libcperciva_opterr 57 | #define optreset libcperciva_optreset 58 | 59 | /* Standard getopt global variables. */ 60 | extern const char * optarg; 61 | extern int optind, opterr, optreset; 62 | 63 | /* Dummy option string, equal to "(dummy)". */ 64 | #define GETOPT_DUMMY getopt_dummy 65 | 66 | /** 67 | * GETOPT(argc, argv): 68 | * When called for the first time (or the first time after optreset is set to 69 | * a nonzero value), return GETOPT_DUMMY, aka. "(dummy)". Thereafter, return 70 | * the next option string and set optarg / optind appropriately; abort if not 71 | * properly initialized when not being called for the first time. 72 | */ 73 | #define GETOPT(argc, argv) getopt(argc, argv) 74 | 75 | /** 76 | * GETOPT_SWITCH(ch): 77 | * Jump to the appropriate GETOPT_OPT, GETOPT_OPTARG, GETOPT_MISSING_ARG, or 78 | * GETOPT_DEFAULT based on the option string ${ch}. When called for the first 79 | * time, perform magic to index the options. 80 | * 81 | * GETOPT_SWITCH(ch) is equivalent to "switch (ch)" in a standard getopt loop. 82 | */ 83 | #define GETOPT_SWITCH(ch) \ 84 | volatile size_t getopt_ln_min = __LINE__; \ 85 | volatile size_t getopt_ln = getopt_ln_min - 1; \ 86 | volatile int getopt_default_missing = 0; \ 87 | DO_SETJMP; \ 88 | switch (getopt_initialized ? getopt_lookup(ch) + getopt_ln_min : getopt_ln++) 89 | 90 | /** 91 | * GETOPT_OPT(os): 92 | * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH. 93 | * 94 | * GETOPT_OPT("-x") is equivalent to "case 'x'" in a standard getopt loop 95 | * which has an optstring containing "x". 96 | */ 97 | #define GETOPT_OPT(os) _GETOPT_OPT(os, __LINE__) 98 | #define _GETOPT_OPT(os, ln) __GETOPT_OPT(os, ln) 99 | #define __GETOPT_OPT(os, ln) \ 100 | case ln: \ 101 | if (getopt_initialized) \ 102 | goto getopt_skip_ ## ln; \ 103 | getopt_register_opt(os, ln - getopt_ln_min, 0); \ 104 | DO_LONGJMP; \ 105 | getopt_skip_ ## ln 106 | 107 | /** 108 | * GETOPT_OPTARG(os): 109 | * Jump to this point when the option string ${os} is passed to GETOPT_SWITCH, 110 | * unless no argument is available, in which case jump to GETOPT_MISSING_ARG 111 | * (if present) or GETOPT_DEFAULT (if not). 112 | * 113 | * GETOPT_OPTARG("-x") is equivalent to "case 'x'" in a standard getopt loop 114 | * which has an optstring containing "x:". 115 | */ 116 | #define GETOPT_OPTARG(os) _GETOPT_OPTARG(os, __LINE__) 117 | #define _GETOPT_OPTARG(os, ln) __GETOPT_OPTARG(os, ln) 118 | #define __GETOPT_OPTARG(os, ln) \ 119 | case ln: \ 120 | if (getopt_initialized) { \ 121 | assert(optarg != NULL); \ 122 | goto getopt_skip_ ## ln; \ 123 | } \ 124 | getopt_register_opt(os, ln - getopt_ln_min, 1); \ 125 | DO_LONGJMP; \ 126 | getopt_skip_ ## ln 127 | 128 | /** 129 | * GETOPT_MISSING_ARG: 130 | * Jump to this point if an option string specified in GETOPT_OPTARG is seen 131 | * but no argument is available. 132 | * 133 | * GETOPT_MISSING_ARG is equivalent to "case ':'" in a standard getopt loop 134 | * which has an optstring starting with ":". As such, it also has the effect 135 | * of disabling warnings about invalid options, as if opterr had been zeroed. 136 | */ 137 | #define GETOPT_MISSING_ARG _GETOPT_MISSING_ARG(__LINE__) 138 | #define _GETOPT_MISSING_ARG(ln) __GETOPT_MISSING_ARG(ln) 139 | #define __GETOPT_MISSING_ARG(ln) \ 140 | case ln: \ 141 | if (getopt_initialized) \ 142 | goto getopt_skip_ ## ln; \ 143 | getopt_register_missing(ln - getopt_ln_min); \ 144 | DO_LONGJMP; \ 145 | getopt_skip_ ## ln 146 | 147 | /** 148 | * GETOPT_DEFAULT: 149 | * Jump to this point if an unrecognized option is seen or if an option 150 | * specified in GETOPT_OPTARG is seen, no argument is available, and there is 151 | * no GETOPT_MISSING_ARG label. 152 | * 153 | * GETOPT_DEFAULT is equivalent to "case '?'" in a standard getopt loop. 154 | * 155 | * NOTE: This MUST be present in the GETOPT_SWITCH statement, and MUST occur 156 | * after all other GETOPT_* labels. 157 | */ 158 | #define GETOPT_DEFAULT _GETOPT_DEFAULT(__LINE__) 159 | #define _GETOPT_DEFAULT(ln) __GETOPT_DEFAULT(ln) 160 | #define __GETOPT_DEFAULT(ln) \ 161 | goto getopt_skip_ ## ln; \ 162 | case ln: \ 163 | getopt_initialized = 1; \ 164 | break; \ 165 | default: \ 166 | if (getopt_initialized) \ 167 | goto getopt_skip_ ## ln; \ 168 | if (!getopt_default_missing) { \ 169 | getopt_setrange(ln - getopt_ln_min); \ 170 | getopt_default_missing = 1; \ 171 | } \ 172 | DO_LONGJMP; \ 173 | getopt_skip_ ## ln 174 | 175 | /* 176 | * The back-end implementation. These should be considered internal 177 | * interfaces and not used directly. 178 | */ 179 | const char * getopt(int, char * const []); 180 | size_t getopt_lookup(const char *); 181 | void getopt_register_opt(const char *, size_t, int); 182 | void getopt_register_missing(size_t); 183 | void getopt_setrange(size_t); 184 | extern const char * getopt_dummy; 185 | extern int getopt_initialized; 186 | 187 | #endif /* !_GETOPT_H_ */ 188 | -------------------------------------------------------------------------------- /libcperciva/util/hexify.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "hexify.h" 5 | 6 | static char hexchars[] = "0123456789abcdef0123456789ABCDEF"; 7 | 8 | /** 9 | * hexify(in, out, len): 10 | * Convert ${len} bytes from ${in} into hexadecimal, writing the resulting 11 | * 2 * ${len} bytes to ${out}; and append a NUL byte. 12 | */ 13 | void 14 | hexify(const uint8_t * in, char * out, size_t len) 15 | { 16 | char * p = out; 17 | size_t i; 18 | 19 | for (i = 0; i < len; i++) { 20 | *p++ = hexchars[in[i] >> 4]; 21 | *p++ = hexchars[in[i] & 0x0f]; 22 | } 23 | *p = '\0'; 24 | } 25 | 26 | /** 27 | * unhexify(in, out, len): 28 | * Convert 2 * ${len} hexadecimal characters from ${in} to ${len} bytes 29 | * and write them to ${out}. This function will only fail if the input is 30 | * not a sequence of hexadecimal characters. 31 | */ 32 | int 33 | unhexify(const char * in, uint8_t * out, size_t len) 34 | { 35 | size_t i; 36 | 37 | /* Make sure we have at least 2 * ${len} hex characters. */ 38 | for (i = 0; i < 2 * len; i++) { 39 | if ((in[i] == '\0') || (strchr(hexchars, in[i]) == NULL)) 40 | goto err0; 41 | } 42 | 43 | for (i = 0; i < len; i++) { 44 | out[i] = (strchr(hexchars, in[2 * i]) - hexchars) & 0x0f; 45 | out[i] <<= 4; 46 | out[i] += (strchr(hexchars, in[2 * i + 1]) - hexchars) & 0x0f; 47 | } 48 | 49 | /* Success! */ 50 | return (0); 51 | 52 | err0: 53 | /* Bad input string. */ 54 | return (-1); 55 | } 56 | -------------------------------------------------------------------------------- /libcperciva/util/hexify.h: -------------------------------------------------------------------------------- 1 | #ifndef _HEXIFY_H_ 2 | #define _HEXIFY_H_ 3 | 4 | #include 5 | #include 6 | 7 | /** 8 | * hexify(in, out, len): 9 | * Convert ${len} bytes from ${in} into hexadecimal, writing the resulting 10 | * 2 * ${len} bytes to ${out}; and append a NUL byte. 11 | */ 12 | void hexify(const uint8_t *, char *, size_t); 13 | 14 | /** 15 | * unhexify(in, out, len): 16 | * Convert 2 * ${len} hexadecimal characters from ${in} to ${len} bytes 17 | * and write them to ${out}. This function will only fail if the input is 18 | * not a sequence of hexadecimal characters. 19 | */ 20 | int unhexify(const char *, uint8_t *, size_t); 21 | 22 | #endif /* !_HEXIFY_H_ */ 23 | -------------------------------------------------------------------------------- /libcperciva/util/imalloc.h: -------------------------------------------------------------------------------- 1 | #ifndef _IMALLOC_H_ 2 | #define _IMALLOC_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | /** 9 | * imalloc(nrec, reclen): 10 | * Allocate ${nrec} records of length ${reclen}. Check for size_t overflow. 11 | */ 12 | static inline void * 13 | imalloc(size_t nrec, size_t reclen) 14 | { 15 | 16 | if (nrec > SIZE_MAX / reclen) { 17 | errno = ENOMEM; 18 | return (NULL); 19 | } else { 20 | return (malloc(nrec * reclen)); 21 | } 22 | } 23 | 24 | /** 25 | * IMALLOC(p, nrec, type): 26 | * Allocate ${nrec} records of type ${type} and store the pointer in ${p}. 27 | * Return non-zero on failure. 28 | */ 29 | #define IMALLOC(p, nrec, type) \ 30 | ((((p) = (type *)imalloc((nrec), sizeof(type))) == NULL) && \ 31 | ((nrec) > 0)) 32 | 33 | #endif /* !_IMALLOC_H_ */ 34 | -------------------------------------------------------------------------------- /libcperciva/util/monoclock.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "warnp.h" 7 | 8 | #include "monoclock.h" 9 | 10 | /* Determine which clock(s) to use. */ 11 | #ifndef POSIXFAIL_CLOCK_GETTIME 12 | #ifdef CLOCK_MONOTONIC 13 | #define USE_MONOTONIC 14 | #endif 15 | #ifndef POSIXFAIL_CLOCK_REALTIME 16 | #define USE_REALTIME 17 | #endif 18 | #endif 19 | 20 | /** 21 | * monoclock_get(tv): 22 | * Store the current time in ${tv}. If CLOCK_MONOTONIC is available, use 23 | * that clock; if CLOCK_MONOTONIC is unavailable, use CLOCK_REALTIME (if 24 | * available) or gettimeofday(2). 25 | */ 26 | int 27 | monoclock_get(struct timeval * tv) 28 | { 29 | #if defined(USE_MONOTONIC) || defined(USE_REALTIME) 30 | struct timespec tp; 31 | #endif 32 | 33 | #ifdef USE_MONOTONIC 34 | if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0) { 35 | tv->tv_sec = tp.tv_sec; 36 | tv->tv_usec = (suseconds_t)(tp.tv_nsec / 1000); 37 | } else if ((errno != ENOSYS) && (errno != EINVAL)) { 38 | warnp("clock_gettime(CLOCK_MONOTONIC)"); 39 | goto err0; 40 | } else 41 | #endif 42 | #ifdef USE_REALTIME 43 | if (clock_gettime(CLOCK_REALTIME, &tp) == 0) { 44 | tv->tv_sec = tp.tv_sec; 45 | tv->tv_usec = (suseconds_t)(tp.tv_nsec / 1000); 46 | } else { 47 | warnp("clock_gettime(CLOCK_REALTIME)"); 48 | goto err0; 49 | } 50 | #else 51 | if (gettimeofday(tv, NULL)) { 52 | warnp("gettimeofday"); 53 | goto err0; 54 | } 55 | #endif 56 | 57 | /* Success! */ 58 | return (0); 59 | 60 | err0: 61 | /* Failure! */ 62 | return (-1); 63 | } 64 | 65 | /** 66 | * monoclock_get_cputime(tv): 67 | * Store in ${tv} the duration the process has been running if 68 | * CLOCK_PROCESS_CPUTIME_ID is available; fall back to monoclock_get() 69 | * otherwise. 70 | */ 71 | int 72 | monoclock_get_cputime(struct timeval * tv) 73 | { 74 | /* Use CLOCK_PROCESS_CPUTIME_ID if available. */ 75 | #ifdef CLOCK_PROCESS_CPUTIME_ID 76 | struct timespec tp; 77 | 78 | if (clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &tp) == 0) { 79 | tv->tv_sec = tp.tv_sec; 80 | tv->tv_usec = (suseconds_t)(tp.tv_nsec / 1000); 81 | } else if ((errno != ENOSYS) && (errno != EINVAL)) { 82 | warnp("clock_gettime(CLOCK_PROCESS_CPUTIME_ID)"); 83 | goto err0; 84 | } else 85 | #endif 86 | /* Fall back to monoclock_get(). */ 87 | if (monoclock_get(tv)) 88 | goto err0; 89 | 90 | /* Success! */ 91 | return (0); 92 | 93 | err0: 94 | /* Failure! */ 95 | return (-1); 96 | } 97 | 98 | /** 99 | * monoclock_getres(resd): 100 | * Store an upper limit on timer granularity in ${resd}. If CLOCK_MONOTONIC is 101 | * available, use that clock; if CLOCK_MONOTONIC is unavailable, use 102 | * CLOCK_REALTIME (if available) or gettimeofday(2). For this value to be 103 | * meaningful, we assume that clock_getres(x) succeeds iff clock_gettime(x) 104 | * succeeds. 105 | */ 106 | int 107 | monoclock_getres(double * resd) 108 | { 109 | #if defined(USE_MONOTONIC) || defined(USE_REALTIME) 110 | struct timespec res; 111 | #endif 112 | 113 | #ifdef USE_MONOTONIC 114 | if (clock_getres(CLOCK_MONOTONIC, &res) == 0) { 115 | /* Convert clock resolution to a double. */ 116 | *resd = (double)res.tv_sec + (double)res.tv_nsec * 0.000000001; 117 | } else if ((errno != ENOSYS) && (errno != EINVAL)) { 118 | warnp("clock_getres(CLOCK_MONOTONIC)"); 119 | goto err0; 120 | } else 121 | #endif 122 | #ifdef USE_REALTIME 123 | if (clock_getres(CLOCK_REALTIME, &res) == 0) { 124 | /* Convert clock resolution to a double. */ 125 | *resd = (double)res.tv_sec + (double)res.tv_nsec * 0.000000001; 126 | } else { 127 | warnp("clock_getres(CLOCK_REALTIME)"); 128 | goto err0; 129 | } 130 | #else 131 | /* 132 | * We'll be using gettimeofday(). There is no standard way of getting 133 | * the resolution of this clock, but it seems safe to assume that it 134 | * ticks at a minimum rate of CLOCKS_PER_SEC Hz (even though that is 135 | * defined in relation to the measurement of processor time usage, not 136 | * wallclock time); on non-broken systems we'll be relying on 137 | * clock_gettime and clock_getres anyway. 138 | */ 139 | *resd = 1.0 / CLOCKS_PER_SEC; 140 | #endif 141 | 142 | /* Success! */ 143 | return (0); 144 | 145 | #if defined(USE_MONOTONIC) || defined(USE_REALTIME) 146 | err0: 147 | /* Failure! */ 148 | return (-1); 149 | #endif 150 | } 151 | -------------------------------------------------------------------------------- /libcperciva/util/monoclock.h: -------------------------------------------------------------------------------- 1 | #ifndef _MONOCLOCK_H_ 2 | #define _MONOCLOCK_H_ 3 | 4 | #include 5 | 6 | /* Macro to simplify benchmarks. */ 7 | #define timeval_diff(x, y) ((double)(y.tv_sec - x.tv_sec) + \ 8 | (double)(y.tv_usec - x.tv_usec) * 0.000001) 9 | 10 | /** 11 | * monoclock_get(tv): 12 | * Store the current time in ${tv}. If CLOCK_MONOTONIC is available, use 13 | * that clock; if CLOCK_MONOTONIC is unavailable, use CLOCK_REALTIME (if 14 | * available) or gettimeofday(2). 15 | */ 16 | int monoclock_get(struct timeval *); 17 | 18 | /** 19 | * monoclock_get_cputime(tv): 20 | * Store in ${tv} the duration the process has been running if 21 | * CLOCK_PROCESS_CPUTIME_ID is available; fall back to monoclock_get() 22 | * otherwise. 23 | */ 24 | int monoclock_get_cputime(struct timeval *); 25 | 26 | /** 27 | * monoclock_getres(resd): 28 | * Store an upper limit on timer granularity in ${resd}. If CLOCK_MONOTONIC is 29 | * available, use that clock; if CLOCK_MONOTONIC is unavailable, use 30 | * CLOCK_REALTIME (if available) or gettimeofday(2). For this value to be 31 | * meaningful, we assume that clock_getres(x) succeeds iff clock_gettime(x) 32 | * succeeds. 33 | */ 34 | int monoclock_getres(double *); 35 | 36 | #endif /* !_MONOCLOCK_H_ */ 37 | -------------------------------------------------------------------------------- /libcperciva/util/noeintr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "noeintr.h" 8 | 9 | /** 10 | * noeintr_write(d, buf, nbytes): 11 | * Write ${nbytes} bytes of data from ${buf} to the file descriptor ${d} per 12 | * the write(2) system call, but looping until completion if interrupted by 13 | * a signal. Return ${nbytes} on success or -1 on error. 14 | */ 15 | ssize_t 16 | noeintr_write(int d, const void * buf, size_t nbytes) 17 | { 18 | const uint8_t * p = buf; 19 | size_t len = nbytes; 20 | ssize_t lenwrit; 21 | 22 | /* Implementation-defined: Don't allow oversized writes. */ 23 | assert(nbytes <= SSIZE_MAX); 24 | 25 | /* Loop until we have no data left to write. */ 26 | while (len > 0) { 27 | if ((lenwrit = write(d, p, len)) == -1) { 28 | /* EINTR is harmless. */ 29 | if (errno == EINTR) 30 | continue; 31 | 32 | /* Anything else isn't. */ 33 | goto err0; 34 | } 35 | 36 | /* Sanity check. */ 37 | assert(lenwrit >= 0); 38 | assert(lenwrit <= (ssize_t)len); 39 | 40 | /* 41 | * We might have done a partial write; advance the buffer 42 | * pointer and adjust the remaining write length. 43 | */ 44 | p += (size_t)lenwrit; 45 | len -= (size_t)lenwrit; 46 | } 47 | 48 | /* Success! */ 49 | return (ssize_t)(nbytes); 50 | 51 | err0: 52 | /* Failure! */ 53 | return (-1); 54 | } 55 | -------------------------------------------------------------------------------- /libcperciva/util/noeintr.h: -------------------------------------------------------------------------------- 1 | #ifndef _NOEINTR_H_ 2 | #define _NOEINTR_H_ 3 | 4 | #include 5 | 6 | /** 7 | * noeintr_write(d, buf, nbytes): 8 | * Write ${nbytes} bytes of data from ${buf} to the file descriptor ${d} per 9 | * the write(2) system call, but looping until completion if interrupted by 10 | * a signal. Return ${nbytes} on success or -1 on error. 11 | */ 12 | ssize_t noeintr_write(int, const void *, size_t); 13 | 14 | #endif /* !_NOEINTR_H_ */ 15 | -------------------------------------------------------------------------------- /libcperciva/util/parsenum.h: -------------------------------------------------------------------------------- 1 | #ifndef _PARSENUM_H_ 2 | #define _PARSENUM_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | /* Handle compiler warnings about implicit variable conversion in PARSENUM. */ 12 | #ifdef __clang__ 13 | 14 | /* Disable clang warnings. */ 15 | #define PARSENUM_PROLOGUE \ 16 | _Pragma("clang diagnostic push") \ 17 | _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") \ 18 | _Pragma("clang diagnostic ignored \"-Wfloat-conversion\"") \ 19 | _Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \ 20 | _Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"") \ 21 | _Pragma("clang diagnostic ignored \"-Wconversion\"") 22 | 23 | /* Enable clang warnings for code outside of PARSENUM. */ 24 | #define PARSENUM_EPILOGUE \ 25 | _Pragma("clang diagnostic pop") 26 | 27 | /* Other compilers don't need any special handling */ 28 | #else 29 | #define PARSENUM_PROLOGUE /* NOTHING */ 30 | #define PARSENUM_EPILOGUE /* NOTHING */ 31 | #endif /* !__clang__ */ 32 | 33 | /* Print a message before assert(). */ 34 | #define ASSERT_FAIL(s) \ 35 | ( \ 36 | fprintf(stderr, "Assertion failed: " s \ 37 | ", function %s, file %s, line %d\n", \ 38 | __func__, __FILE__, __LINE__), \ 39 | abort() \ 40 | ) 41 | 42 | /** 43 | * PARSENUM(x, s, min, max): 44 | * Parse the string ${s} according to the type of the unsigned integer, signed 45 | * integer, or floating-point number variable ${x}. If the string consists of 46 | * optional whitespace followed by a number (and nothing else) and the numeric 47 | * interpretation of the number is between ${min} and ${max} inclusive, store 48 | * the value into ${x}, set errno to zero, and return zero. Otherwise, return 49 | * nonzero with an unspecified value of ${x} and errno set to EINVAL or ERANGE 50 | * as appropriate. 51 | * 52 | * For floating-point and unsigned integer variables ${x}, this can also be 53 | * invoked as PARSENUM(x, s), in which case the minimum and maximum values are 54 | * set to +/- infinity or the limits of the unsigned integer type. 55 | */ 56 | #define PARSENUM2(x, s) \ 57 | PARSENUM_EX4(x, s, 0, 0, "PARSENUM") 58 | #define PARSENUM4(x, s, min, max) \ 59 | PARSENUM_EX6(x, s, min, max, 0, 0, "PARSENUM") 60 | 61 | /* Magic to select which version of PARSENUM to use. */ 62 | #define PARSENUM(...) PARSENUM_(PARSENUM_COUNT(__VA_ARGS__))(__VA_ARGS__) 63 | #define PARSENUM_(N) PARSENUM__(N) 64 | #define PARSENUM__(N) PARSENUM ## N 65 | #define PARSENUM_COUNT(...) PARSENUM_COUNT_(__VA_ARGS__, 4, 3, 2, 1) 66 | #define PARSENUM_COUNT_(_1, _2, _3, _4, N, ...) N 67 | 68 | /** 69 | * PARSENUM_EX(x, s, min, max, base, trailing): 70 | * Parse the string ${s} according to the type of the unsigned integer or 71 | * signed integer variable ${x}, in the specified ${base}. If the string 72 | * consists of optional whitespace followed by a number (and nothing else) and 73 | * the numeric interpretation of the number is between ${min} and ${max} 74 | * inclusive, store the value into ${x}, set errno to zero, and return zero. 75 | * Otherwise, return nonzero with an unspecified value of ${x} and errno set 76 | * to EINVAL or ERANGE as appropriate. The check for trailing characters (and 77 | * EINVAL if they are found) is disabled if ${trailing} is non-zero. 78 | * 79 | * For an unsigned integer variable ${x}, this can also be invoked as 80 | * PARSENUM_EX(x, s, base), in which case the minimum and maximum values are 81 | * set to the limits of the unsigned integer type. 82 | * 83 | * For a floating-point variable ${x}, the ${base} must be 0. 84 | */ 85 | #define PARSENUM_EX4(x, s, base, trailing, _define_name) \ 86 | ( \ 87 | PARSENUM_PROLOGUE \ 88 | errno = 0, \ 89 | (((*(x)) = 1, (*(x)) /= 2) > 0) ? \ 90 | (((base) == 0) ? \ 91 | ((*(x)) = parsenum_float((s), \ 92 | (double)-INFINITY, \ 93 | (double)INFINITY, (trailing))) : \ 94 | (ASSERT_FAIL(_define_name " applied to" \ 95 | " float with base != 0"), 1)) : \ 96 | (((*(x)) = -1) > 0) ? \ 97 | ((*(x)) = parsenum_unsigned((s), 0, (*(x)), \ 98 | (*(x)), (base), (trailing))) : \ 99 | (ASSERT_FAIL(_define_name " applied to signed" \ 100 | " integer without specified bounds"), 1), \ 101 | errno != 0 \ 102 | PARSENUM_EPILOGUE \ 103 | ) 104 | #define PARSENUM_EX6(x, s, min, max, base, trailing, _define_name) \ 105 | ( \ 106 | PARSENUM_PROLOGUE \ 107 | errno = 0, \ 108 | (((*(x)) = 1, (*(x)) /= 2) > 0) ? \ 109 | (((base) == 0) ? \ 110 | ((*(x)) = parsenum_float((s), \ 111 | (double)(min), (double)(max), \ 112 | (trailing))) : \ 113 | (ASSERT_FAIL(_define_name " applied to" \ 114 | " float with base != 0"), 1)) : \ 115 | (((*(x)) = -1) <= 0) ? \ 116 | ((*(x)) = parsenum_signed((s), \ 117 | (*(x) <= 0) ? (min) : 0, \ 118 | (*(x) <= 0) ? (max) : 0, (base), \ 119 | (trailing))) : \ 120 | (((*(x)) = parsenum_unsigned((s), \ 121 | (min) <= 0 ? 0 : (min), \ 122 | (uintmax_t)(max), *(x), (base), \ 123 | (trailing))), \ 124 | ((((max) < 0) && (errno == 0)) ? \ 125 | (errno = ERANGE) : 0)), \ 126 | errno != 0 \ 127 | PARSENUM_EPILOGUE \ 128 | ) 129 | 130 | /* Magic to select which version of PARSENUM_EX to use. */ 131 | #define PARSENUM_EX(...) PARSENUM_EX_(PARSENUM_EX_COUNT(__VA_ARGS__))(__VA_ARGS__, "PARSENUM_EX") 132 | #define PARSENUM_EX_(N) PARSENUM_EX__(N) 133 | #define PARSENUM_EX__(N) PARSENUM_EX ## N 134 | #define PARSENUM_EX_COUNT(...) PARSENUM_EX_COUNT_(__VA_ARGS__, 6, 5, 4, 3, 2, 1) 135 | #define PARSENUM_EX_COUNT_(_1, _2, _3, _4, _5, _6, N, ...) N 136 | 137 | /* Functions for performing the parsing and parameter checking. */ 138 | static inline double 139 | parsenum_float(const char * s, double min, double max, int trailing) 140 | { 141 | char * eptr; 142 | double val; 143 | 144 | val = strtod(s, &eptr); 145 | if (eptr == s || (!trailing && (*eptr != '\0'))) 146 | errno = EINVAL; 147 | else if ((val < min) || (val > max)) 148 | errno = ERANGE; 149 | return (val); 150 | } 151 | 152 | static inline intmax_t 153 | parsenum_signed(const char * s, intmax_t min, intmax_t max, int base, 154 | int trailing) 155 | { 156 | char * eptr; 157 | intmax_t val; 158 | 159 | val = strtoimax(s, &eptr, base); 160 | if (eptr == s || (!trailing && (*eptr != '\0'))) 161 | errno = EINVAL; 162 | else if ((val < min) || (val > max)) { 163 | errno = ERANGE; 164 | val = 0; 165 | } 166 | return (val); 167 | } 168 | 169 | static inline uintmax_t 170 | parsenum_unsigned(const char * s, uintmax_t min, uintmax_t max, 171 | uintmax_t typemax, int base, int trailing) 172 | { 173 | char * eptr; 174 | uintmax_t val; 175 | 176 | val = strtoumax(s, &eptr, base); 177 | if (eptr == s || (!trailing && (*eptr != '\0'))) 178 | errno = EINVAL; 179 | else if ((val < min) || (val > max) || (val > typemax)) 180 | errno = ERANGE; 181 | return (val); 182 | } 183 | 184 | #endif /* !_PARSENUM_H_ */ 185 | -------------------------------------------------------------------------------- /libcperciva/util/setuidgid.c: -------------------------------------------------------------------------------- 1 | /* We use non-POSIX functionality in this file. */ 2 | #undef _POSIX_C_SOURCE 3 | #undef _XOPEN_SOURCE 4 | 5 | /* 6 | * There is no setgroups() in the POSIX standard, so if we want to drop 7 | * supplementary groups, we need to use platform-specific code. This must 8 | * happen before the regular includes, as in some cases we need to define other 9 | * symbols before including the relevant header. 10 | */ 11 | #if defined(__linux__) 12 | /* setgroups() includes for Linux. */ 13 | #define _BSD_SOURCE 1 14 | #define _DEFAULT_SOURCE 1 15 | 16 | #include 17 | 18 | #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__APPLE__) 19 | /* setgroups() includes for FreeBSD, NetBSD, MacOS X. */ 20 | #include 21 | #include 22 | 23 | #elif defined(__OpenBSD__) || defined(__osf__) 24 | /* setgroups() includes for OpenBSD. */ 25 | #include 26 | #include 27 | 28 | #elif defined(__sun) || defined(__hpux) 29 | /* setgroups() includes for Solaris. */ 30 | #include 31 | 32 | #elif defined(_AIX) 33 | /* setgroups() includes for AIX. */ 34 | #include 35 | 36 | #else 37 | /* Unknown OS; we don't know how to get setgroups(). */ 38 | #define NO_SETGROUPS 39 | 40 | #endif /* end includes for setgroups() */ 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include "parsenum.h" 52 | #include "warnp.h" 53 | 54 | #include "setuidgid.h" 55 | 56 | /* Function prototypes related to supplementary groups. */ 57 | static int setgroups_none(void); 58 | static int check_supplementary_groups_none(void); 59 | 60 | /* Function prototypes related to uid and gid. */ 61 | static int set_group(const char *); 62 | static int set_user(const char *); 63 | static int string_extract_user_group(const char *, char **, char **); 64 | 65 | /** 66 | * setgroups_none(void): 67 | * Attempt to leave all supplementary groups. If we do not know how to do this 68 | * on the platform, return 0 anyway. 69 | */ 70 | static int 71 | setgroups_none(void) 72 | { 73 | 74 | #ifndef NO_SETGROUPS 75 | if (setgroups(0, NULL)) { 76 | /* We handle EPERM separately; keep it in errno. */ 77 | if (errno != EPERM) 78 | warnp("setgroups()"); 79 | goto err0; 80 | } 81 | #endif 82 | 83 | /* Success! */ 84 | return (0); 85 | 86 | err0: 87 | /* Failure! */ 88 | return (-1); 89 | } 90 | 91 | /* Check if we're in any supplementary groups. */ 92 | static int 93 | check_supplementary_groups_none(void) 94 | { 95 | gid_t grouplist[1]; 96 | int ngroups; 97 | 98 | /* Find number of groups. */ 99 | if ((ngroups = getgroups(0, NULL)) < 0) { 100 | warnp("getgroups()"); 101 | goto err0; 102 | } 103 | 104 | /* Check group membership. */ 105 | if (ngroups > 1) 106 | /* Definitely failed. */ 107 | goto err0; 108 | if (ngroups == 1) { 109 | /* 110 | * POSIX allows getgroups() to return the effective group ID, 111 | * so if we're in exactly one group, we need to check that GID. 112 | */ 113 | if (getgroups(1, grouplist) != 1) { 114 | warnp("getgroups()"); 115 | goto err0; 116 | } 117 | /* POSIX: getegid() shall not modify errno. */ 118 | if (grouplist[0] != getegid()) 119 | goto err0; 120 | } 121 | 122 | /* Success! */ 123 | return (0); 124 | 125 | err0: 126 | /* Failure! */ 127 | return (-1); 128 | } 129 | 130 | /* Set the GID to the group represented by ${groupname}. */ 131 | static int 132 | set_group(const char * groupname) 133 | { 134 | gid_t gid; 135 | struct group * group_info; 136 | 137 | /* 138 | * Attempt to convert the group name to a group ID. If the database 139 | * lookup fails with an error, return; but if it fails without an error 140 | * (indicating that it successfully found that the name does not exist) 141 | * fall back to trying to parse the name as a numeric group ID. 142 | */ 143 | errno = 0; 144 | if ((group_info = getgrnam(groupname)) != NULL) { 145 | gid = group_info->gr_gid; 146 | } else if (errno) { 147 | warnp("getgrnam(\"%s\")", groupname); 148 | goto err0; 149 | } else if (PARSENUM(&gid, groupname)) { 150 | warn0("No such group: %s", groupname); 151 | goto err0; 152 | } 153 | 154 | /* Set GID. */ 155 | if (setgid(gid)) { 156 | /* We handle EPERM separately; keep it in errno. */ 157 | if (errno != EPERM) 158 | warnp("setgid(%lu)", (unsigned long int)gid); 159 | goto err0; 160 | } 161 | 162 | /* Success! */ 163 | return (0); 164 | 165 | err0: 166 | /* Failure! */ 167 | return (-1); 168 | } 169 | 170 | /* Set the UID to the user represented by ${username}. */ 171 | static int 172 | set_user(const char * username) 173 | { 174 | uid_t uid; 175 | struct passwd * user_info; 176 | 177 | /* See similar code in set_gid(). */ 178 | errno = 0; 179 | if ((user_info = getpwnam(username)) != NULL) { 180 | uid = user_info->pw_uid; 181 | } else if (errno) { 182 | warnp("getpwnam(\"%s\")", username); 183 | goto err0; 184 | } else if (PARSENUM(&uid, username)) { 185 | warn0("No such user: %s", username); 186 | goto err0; 187 | } 188 | 189 | /* Set UID. */ 190 | if (setuid(uid)) { 191 | /* We handle EPERM separately; keep it in errno. */ 192 | if (errno != EPERM) 193 | warnp("setuid(%lu)", (unsigned long int)uid); 194 | goto err0; 195 | } 196 | 197 | /* Success! */ 198 | return (0); 199 | 200 | err0: 201 | /* Failure! */ 202 | return (-1); 203 | } 204 | 205 | /* Parses "username", ":groupname", or "username:groupname". */ 206 | static int 207 | string_extract_user_group(const char * combined, char ** username_p, 208 | char ** groupname_p) 209 | { 210 | size_t pos; 211 | size_t len; 212 | 213 | /* Sanity check. */ 214 | assert(combined != NULL); 215 | 216 | /* Search for ":" and get string length. */ 217 | pos = strcspn(combined, ":"); 218 | len = strlen(combined); 219 | 220 | /* Reject silly strings. */ 221 | if (pos == len - 1) { 222 | warn0("Empty group name: %s", combined); 223 | goto err0; 224 | } 225 | 226 | /* String ok, proceed. */ 227 | if (pos == 0) { 228 | /* Groupname only; duplicate string. */ 229 | if ((*groupname_p = strdup(&combined[1])) == NULL) { 230 | warnp("strdup()"); 231 | goto err0; 232 | } 233 | *username_p = NULL; 234 | } else if (pos != len) { 235 | /* Extract username. */ 236 | if ((*username_p = malloc(pos + 1)) == NULL) { 237 | warnp("Failed to allocate memory"); 238 | goto err0; 239 | } 240 | memcpy(*username_p, combined, pos); 241 | (*username_p)[pos] = '\0'; 242 | 243 | /* Extract groupname. */ 244 | if ((*groupname_p = strdup(&combined[pos + 1])) == NULL) { 245 | warnp("strdup()"); 246 | goto err1; 247 | } 248 | } else { 249 | /* Duplicate string. */ 250 | if ((*username_p = strdup(combined)) == NULL) { 251 | warnp("strdup()"); 252 | goto err0; 253 | } 254 | *groupname_p = NULL; 255 | } 256 | 257 | /* Success! */ 258 | return (0); 259 | 260 | err1: 261 | free(*username_p); 262 | err0: 263 | /* Failure! */ 264 | return (-1); 265 | } 266 | 267 | /** 268 | * setuidgid(user_group_string, leave_suppgrp): 269 | * Set the UID and/or GID to the names given in ${user_group_string}. If no 270 | * UID or GID can be found matching those strings, treat the values as numeric 271 | * IDs. Depending on the existence and position of a colon ":", the behaviour 272 | * is 273 | * - no ":" means that the string is a username. 274 | * - ":" in the first position means that the string is a groupname. 275 | * - otherwise, the string is parsed into "username:groupname". 276 | * 277 | * The behaviour with supplementary groups depends on ${leave_suppgrp}: 278 | * - SETUIDGID_SGROUP_IGNORE: do not attempt to leave supplementary groups. 279 | * - SETUIDGID_SGROUP_LEAVE_WARN: attempt to leave; if it fails, give a 280 | * warning but continue. 281 | * - SETUIDGID_SGROUP_LEAVE_ERROR: attempt to leave; if it fails, return 282 | * an error. 283 | */ 284 | int 285 | setuidgid(const char * user_group_string, int leave_suppgrp) 286 | { 287 | char * username; 288 | char * groupname; 289 | int saved_errno; 290 | 291 | /* Get the username:groupname from ${user_group_string}. */ 292 | if (string_extract_user_group(user_group_string, &username, &groupname)) 293 | goto err0; 294 | 295 | /* If requested, leave supplementary groups. */ 296 | if (leave_suppgrp != SETUIDGID_SGROUP_IGNORE) { 297 | /* Attempt to leave all supplementary groups. */ 298 | if (setgroups_none()) { 299 | if (leave_suppgrp == SETUIDGID_SGROUP_LEAVE_ERROR) 300 | goto err1; 301 | } 302 | 303 | /* Check that we have actually left all supplementary groups. */ 304 | if (check_supplementary_groups_none()) { 305 | if (leave_suppgrp == SETUIDGID_SGROUP_LEAVE_ERROR) { 306 | warn0("Failed to leave supplementary groups"); 307 | goto err1; 308 | } else { 309 | warn0("Warning: Failed to leave supplementary " 310 | "groups"); 311 | } 312 | } 313 | } 314 | 315 | /* Set group; must be changed before user. */ 316 | if (groupname && set_group(groupname)) 317 | goto err1; 318 | 319 | /* Set user. */ 320 | if (username && set_user(username)) 321 | goto err1; 322 | 323 | /* Clean up. */ 324 | free(groupname); 325 | free(username); 326 | 327 | /* Success! */ 328 | return (0); 329 | 330 | err1: 331 | /* POSIX Issue 7 does not forbid free() from modifying errno. */ 332 | saved_errno = errno; 333 | 334 | /* Clean up. */ 335 | free(groupname); 336 | free(username); 337 | 338 | /* Restore errno. */ 339 | errno = saved_errno; 340 | err0: 341 | /* Failure! */ 342 | return (-1); 343 | } 344 | -------------------------------------------------------------------------------- /libcperciva/util/setuidgid.h: -------------------------------------------------------------------------------- 1 | #ifndef _SETUIDGID_H_ 2 | #define _SETUIDGID_H_ 3 | 4 | /* How should we treat supplementary groups? */ 5 | enum { 6 | SETUIDGID_SGROUP_IGNORE = 0, 7 | SETUIDGID_SGROUP_LEAVE_WARN, 8 | SETUIDGID_SGROUP_LEAVE_ERROR 9 | }; 10 | 11 | /** 12 | * setuidgid(user_group_string, leave_suppgrp): 13 | * Set the UID and/or GID to the names given in ${user_group_string}. If no 14 | * UID or GID can be found matching those strings, treat the values as numeric 15 | * IDs. Depending on the existence and position of a colon ":", the behaviour 16 | * is 17 | * - no ":" means that the string is a username. 18 | * - ":" in the first position means that the string is a groupname. 19 | * - otherwise, the string is parsed into "username:groupname". 20 | * 21 | * The behaviour with supplementary groups depends on ${leave_suppgrp}: 22 | * - SETUIDGID_SGROUP_IGNORE: do not attempt to leave supplementary groups. 23 | * - SETUIDGID_SGROUP_LEAVE_WARN: attempt to leave; if it fails, give a 24 | * warning but continue. 25 | * - SETUIDGID_SGROUP_LEAVE_ERROR: attempt to leave; if it fails, return 26 | * an error. 27 | */ 28 | int setuidgid(const char *, int); 29 | 30 | #endif /* !_SETUIDGID_H_ */ 31 | -------------------------------------------------------------------------------- /libcperciva/util/sock.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOCK_H_ 2 | #define _SOCK_H_ 3 | 4 | /** 5 | * Address strings are of the following forms: 6 | * /path/to/unix/socket 7 | * [ip.v4.ad.dr]:port 8 | * [ipv6:add::ress]:port 9 | * host.name:port 10 | */ 11 | 12 | /* Opaque address structure. */ 13 | struct sock_addr; 14 | 15 | /** 16 | * sock_resolve(addr): 17 | * Return a NULL-terminated array of pointers to sock_addr structures. 18 | */ 19 | struct sock_addr ** sock_resolve(const char *); 20 | 21 | /** 22 | * sock_listener(sa): 23 | * Create a socket, set SO_REUSEADDR, bind it to the socket address ${sa}, 24 | * mark it for listening, and mark it as non-blocking. 25 | */ 26 | int sock_listener(const struct sock_addr *); 27 | 28 | /** 29 | * sock_connect(sas): 30 | * Iterate through the addresses in ${sas}, attempting to create a socket and 31 | * connect (blockingly). Once connected, stop iterating, mark the socket as 32 | * non-blocking, and return it. 33 | */ 34 | int sock_connect(struct sock_addr * const *); 35 | 36 | /** 37 | * sock_connect_blocking(sas): 38 | * Behave as sock_connect, except that the socket is not marked as 39 | * non-blocking. 40 | */ 41 | int sock_connect_blocking(struct sock_addr * const *); 42 | 43 | /** 44 | * sock_connect_nb(sa): 45 | * Create a socket, mark it as non-blocking, and attempt to connect to the 46 | * address ${sa}. Return the socket (connected or in the process of 47 | * connecting) or -1 on error. 48 | */ 49 | int sock_connect_nb(const struct sock_addr *); 50 | 51 | /** 52 | * sock_addr_free(sa): 53 | * Free the provided sock_addr structure. 54 | */ 55 | void sock_addr_free(struct sock_addr *); 56 | 57 | /** 58 | * sock_addr_freelist(sas): 59 | * Free the provided NULL-terminated array of sock_addr structures. 60 | */ 61 | void sock_addr_freelist(struct sock_addr **); 62 | 63 | #endif /* !_SOCK_H_ */ 64 | -------------------------------------------------------------------------------- /libcperciva/util/sock_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef _SOCK_INTERNAL_H_ 2 | #define _SOCK_INTERNAL_H_ 3 | 4 | #include 5 | 6 | /* Socket address structure. */ 7 | struct sock_addr { 8 | int ai_family; 9 | int ai_socktype; 10 | struct sockaddr * name; 11 | socklen_t namelen; 12 | }; 13 | 14 | #endif /* !_SOCK_INTERNAL_H_ */ 15 | -------------------------------------------------------------------------------- /libcperciva/util/warnp.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "warnp.h" 8 | 9 | static int initialized = 0; 10 | static char * name = NULL; 11 | 12 | /* Free the name string. */ 13 | static void 14 | done(void) 15 | { 16 | 17 | free(name); 18 | name = NULL; 19 | } 20 | 21 | /** 22 | * warnp_setprogname(progname): 23 | * Set the program name to be used by warn() and warnx() to ${progname}. 24 | */ 25 | void 26 | warnp_setprogname(const char * progname) 27 | { 28 | const char * p; 29 | 30 | /* Free the name if we already have one. */ 31 | free(name); 32 | 33 | /* Find the last segment of the program name. */ 34 | for (p = progname; progname[0] != '\0'; progname++) 35 | if (progname[0] == '/') 36 | p = progname + 1; 37 | 38 | /* Copy the name string. */ 39 | name = strdup(p); 40 | 41 | /* If we haven't already done so, register our exit handler. */ 42 | if (initialized == 0) { 43 | atexit(done); 44 | initialized = 1; 45 | } 46 | } 47 | 48 | void 49 | warn(const char * fmt, ...) 50 | { 51 | va_list ap; 52 | 53 | va_start(ap, fmt); 54 | fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)"); 55 | if (fmt != NULL) { 56 | fprintf(stderr, ": "); 57 | vfprintf(stderr, fmt, ap); 58 | } 59 | fprintf(stderr, ": %s\n", strerror(errno)); 60 | va_end(ap); 61 | } 62 | 63 | void 64 | warnx(const char * fmt, ...) 65 | { 66 | va_list ap; 67 | 68 | va_start(ap, fmt); 69 | fprintf(stderr, "%s", (name != NULL) ? name : "(unknown)"); 70 | if (fmt != NULL) { 71 | fprintf(stderr, ": "); 72 | vfprintf(stderr, fmt, ap); 73 | } 74 | fprintf(stderr, "\n"); 75 | va_end(ap); 76 | } 77 | -------------------------------------------------------------------------------- /libcperciva/util/warnp.h: -------------------------------------------------------------------------------- 1 | #ifndef _WARNP_H_ 2 | #define _WARNP_H_ 3 | 4 | #include 5 | #include 6 | 7 | /* Avoid namespace collisions with BSD . */ 8 | #define warn libcperciva_warn 9 | #define warnx libcperciva_warnx 10 | 11 | /** 12 | * warnp_setprogname(progname): 13 | * Set the program name to be used by warn() and warnx() to ${progname}. 14 | */ 15 | void warnp_setprogname(const char *); 16 | #define WARNP_INIT do { \ 17 | if (argv[0] != NULL) \ 18 | warnp_setprogname(argv[0]); \ 19 | } while (0) 20 | 21 | /* As in BSD . */ 22 | void warn(const char *, ...); 23 | void warnx(const char *, ...); 24 | 25 | /* 26 | * If compiled with DEBUG defined, print __FILE__ and __LINE__. 27 | */ 28 | #ifdef DEBUG 29 | #define warnline do { \ 30 | warnx("%s, %d", __FILE__, __LINE__); \ 31 | } while (0) 32 | #else 33 | #define warnline 34 | #endif 35 | 36 | /* 37 | * Call warn(3) or warnx(3) depending upon whether errno == 0; and clear 38 | * errno (so that the standard error message isn't repeated later). 39 | */ 40 | #define warnp(...) do { \ 41 | warnline; \ 42 | if (errno != 0) { \ 43 | warn(__VA_ARGS__); \ 44 | errno = 0; \ 45 | } else \ 46 | warnx(__VA_ARGS__); \ 47 | } while (0) 48 | 49 | /* 50 | * Call warnx(3) and set errno == 0. Unlike warnp, this should be used 51 | * in cases where we're reporting a problem which we discover ourselves 52 | * rather than one which is reported to us from a library or the kernel. 53 | */ 54 | #define warn0(...) do { \ 55 | warnline; \ 56 | warnx(__VA_ARGS__); \ 57 | errno = 0; \ 58 | } while (0) 59 | 60 | #endif /* !_WARNP_H_ */ 61 | -------------------------------------------------------------------------------- /release-tools/metabuild.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e -o nounset 4 | 5 | D=$1 6 | MAKEBSD=$2 7 | CFLAGS_HARDCODED=$3 8 | 9 | OUT=Makefile 10 | 11 | # Check environment 12 | if [ -z "$CPP" ]; then 13 | echo "Need CPP environment variable." 14 | exit 1 15 | fi 16 | 17 | # Check command-line arguments 18 | if [ "$#" -ne 3 ]; then 19 | echo "Usage: $0 DIR MAKEBSD CFLAGS_HARDCODED" 20 | exit 1 21 | fi 22 | 23 | # Set up directories 24 | cd ${D} 25 | SUBDIR_DEPTH=`${MAKEBSD} -V SUBDIR_DEPTH` 26 | 27 | # Set up *-config.h so that we don't have missing headers 28 | rm -f ${SUBDIR_DEPTH}/cpusupport-config.h 29 | touch ${SUBDIR_DEPTH}/cpusupport-config.h 30 | 31 | copyvar() { 32 | var=$1 33 | if [ -n "`${MAKEBSD} -V $var`" ]; then 34 | printf "%s=" "$var" >> $OUT 35 | ${MAKEBSD} -V $var >> $OUT 36 | fi 37 | } 38 | 39 | add_makefile_prog() { 40 | if [ "$(${MAKEBSD} -V NOINST)" = "1" ]; then 41 | cat ${SUBDIR_DEPTH}/Makefile.prog | \ 42 | perl -0pe 's/(install:.*?)\n\n//s' >> $OUT 43 | else 44 | cat ${SUBDIR_DEPTH}/Makefile.prog >> $OUT 45 | fi 46 | } 47 | 48 | add_object_files() { 49 | # Set up useful variables 50 | OBJ=$(${MAKEBSD} -V SRCS | \ 51 | sed -e 's| cpusupport-config.h||' | \ 52 | tr ' ' '\n' | \ 53 | sed -E 's/.c$/.o/' ) 54 | CPP_CONFIG="-DCPUSUPPORT_CONFIG_FILE=\"cpusupport-config.h\"" 55 | CPP_ARGS_FIXED="-std=c99 ${CPP_CONFIG} -I${SUBDIR_DEPTH} -MM" 56 | OUT_CC_BEGIN="\${CC} \${CFLAGS_POSIX} ${CFLAGS_HARDCODED}" 57 | OUT_CC_MID="-I${SUBDIR_DEPTH} \${IDIRS} \${CPPFLAGS} \${CFLAGS}" 58 | 59 | # Generate build instructions for each object 60 | for F in $OBJ; do 61 | S=`${MAKEBSD} source-${F}` 62 | CF=`${MAKEBSD} cflags-${F}` 63 | IDIRS=`${MAKEBSD} -V IDIRS` 64 | # Get the build instructions, then remove newlines, condense 65 | # multiple spaces, remove line continuations, and replace the 66 | # final space with a newline. 67 | ${CPP} ${S} ${CPP_ARGS_FIXED} ${IDIRS} -MT ${F} | \ 68 | tr '\n' ' ' | \ 69 | tr -s ' ' | \ 70 | sed -e 's| \\ | |g' | \ 71 | sed -e 's| $||g' 72 | printf "\n" 73 | echo " ${OUT_CC_BEGIN} ${CF} ${OUT_CC_MID} -c ${S} -o ${F}" 74 | done >> $OUT 75 | } 76 | 77 | # Add header and variables 78 | echo '.POSIX:' > $OUT 79 | echo '# AUTOGENERATED FILE, DO NOT EDIT' >> $OUT 80 | copyvar PROG 81 | copyvar MAN1 82 | # SRCS is tricker to handle, as we need to remove any -config.h from the list. 83 | if [ -n "`${MAKEBSD} -V SRCS`" ]; then 84 | printf "SRCS=" >> $OUT 85 | ${MAKEBSD} -V SRCS | \ 86 | sed -e 's| cpusupport-config.h||' >> $OUT 87 | fi 88 | copyvar IDIRS 89 | copyvar LDADD_REQ 90 | copyvar SUBDIR_DEPTH 91 | printf "RELATIVE_DIR=$D\n" >> $OUT 92 | 93 | # Add all, install, clean, $PROG 94 | if [ -n "`${MAKEBSD} -V SRCS`" ]; then 95 | add_makefile_prog 96 | else 97 | printf "\nall:\n\ttrue\n\nclean:\n\ttrue\n" >> $OUT 98 | fi 99 | 100 | # Add all object files (if applicable) 101 | if [ -n "`${MAKEBSD} -V SRCS`" ]; then 102 | add_object_files 103 | fi 104 | 105 | # Add test (if applicable) 106 | if grep -q "test:" Makefile.BSD ; then 107 | printf "\n" >> $OUT 108 | awk '/test:/, /^$/' Makefile.BSD | \ 109 | awk '$1' >> $OUT 110 | fi 111 | 112 | # Add all_extra (if applicable) 113 | if grep -q "all_extra:" Makefile.BSD ; then 114 | printf "\n" >> $OUT 115 | awk '/all_extra:/, /^$/' Makefile.BSD | \ 116 | awk '$1' >> $OUT 117 | sed -e 's/${MAKE} ${PROG}/${MAKE} ${PROG} all_extra/' \ 118 | Makefile > Makefile.new 119 | mv Makefile.new Makefile 120 | fi 121 | 122 | # Add clean_extra (if applicable) 123 | if grep -q "clean_extra:" Makefile.BSD ; then 124 | printf "\n" >> $OUT 125 | awk '/clean_extra:/, /^$/' Makefile.BSD | \ 126 | awk '$1' >> $OUT 127 | awk '/clean:/ {print $0 "\tclean_extra";next}{print}' \ 128 | Makefile > Makefile.new 129 | mv Makefile.new Makefile 130 | fi 131 | 132 | # Clean up -config.h files 133 | rm -f ${SUBDIR_DEPTH}/cpusupport-config.h 134 | --------------------------------------------------------------------------------