Update with sslsplit develop changes, especially content logging

Change SIGHUP to behave like SIGUSR1
pull/13/head
Soner Tari 6 years ago
parent 12ecc96648
commit 52d37297b6

@ -1,8 +1,15 @@
Please supply the following with bug reports to allow for diagnostics:
Please only open issues for bug reports or feature requests.
The developers do not have the resources to provide individual support.
For support, please turn to the most applicable Stack Exchange site, such as
[Information Security](https://security.stackexchange.com/),
[Network Engineering](https://networkengineering.stackexchange.com/) or
[Super User](https://superuser.com/).
- Output of `sslsplit -V`
For bug reports, please supply:
- Output of `sslproxy -V`
- Output of `uname -a`
- Exact command line arguments used to run `sslsplit`
- Exact command line arguments used to run `sslproxy`
- Relevant part of debug mode (-D) output, if applicable
- NAT redirection rules you are using, if applicable
- List of failing unit tests in `make test` output

@ -1,8 +1,151 @@
language: c
compiler:
- gcc
- clang
script: make && make travis && ./sslproxy -V
before_install:
- sudo apt-get update -qq
- sudo apt-get install -qq libssl-dev libevent-dev check
matrix:
include:
- os: linux
compiler: gcc
env: SSL=ubuntu EVENT=ubuntu
before_install:
- sudo apt-get install -qq libssl-dev libevent-dev libnet1-dev libpcap-dev check
- os: linux
compiler: clang
env: SSL=ubuntu EVENT=ubuntu
before_install:
- sudo apt-get install -qq libssl-dev libevent-dev libnet1-dev libpcap-dev check
- os: linux
compiler: clang
env: FEATURES=-DWITHOUT_MIRROR SSL=ubuntu EVENT=ubuntu
before_install:
- sudo apt-get install -qq libssl-dev libevent-dev check
- os: linux
compiler: clang
env: SSL=openssl-0.9.8zh EVENT=libevent-2.0.22
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=openssl-1.0.0s EVENT=libevent-2.0.22
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=openssl-1.0.1u EVENT=libevent-2.0.22
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=openssl-1.0.2p EVENT=libevent-2.0.22
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=openssl-1.1.0i EVENT=libevent-2.1.8
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=openssl-1.1.1 EVENT=libevent-2.1.8
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=libressl-2.2.7 EVENT=libevent-2.0.22
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: linux
compiler: clang
env: SSL=libressl-2.7.4 EVENT=libevent-2.1.8
cache:
directories:
- $HOME/opt
before_install:
- sudo apt-get install -qq libnet1-dev libpcap-dev check
install:
- ./Mk/bin/install-opt.sh
before_script:
- export OPENSSL_BASE="$HOME/opt/$SSL"
- export LIBEVENT_BASE="$HOME/opt/$EVENT"
- export LD_LIBRARY_PATH="$HOME/opt/$SSL/lib":"$HOME/opt/$EVENT/lib"
- os: osx
env: SSL=homebrew EVENT=homebrew
cache:
directories:
- /usr/local/Cellar/openssl
- /usr/local/opt/openssl
- /usr/local/Cellar/libevent
- /usr/local/opt/libevent
- /usr/local/Cellar/libnet
- /usr/local/opt/libnet
- /usr/local/Cellar/libpcap
- /usr/local/opt/libpcap
- /usr/local/Cellar/check
- /usr/local/opt/check
before_install:
- test -d /usr/local/opt/openssl/lib || { rmdir /usr/local/opt/openssl; brew install openssl; }
- test -d /usr/local/opt/libevent/lib || { rmdir /usr/local/opt/libevent; brew install libevent; }
- test -d /usr/local/opt/libnet/lib || { rmdir /usr/local/opt/libnet; brew install libnet; }
- test -d /usr/local/opt/libpcap/lib || { rmdir /usr/local/opt/libpcap; brew install libpcap; }
- test -d /usr/local/opt/check/lib || { rmdir /usr/local/opt/check; brew install check; }

@ -26,6 +26,7 @@ patches or pull requests, in chronological order of their first contribution:
- Philip Duldig ([pduldig-at-tw](https://github.com/pduldig-at-tw))
- Levente Polyak ([anthraxx](https://github.com/anthraxx))
- Nick French ([naf419](https://github.com/naf419))
- Cihan Kömeçoğlu ([cihankom](https://github.com/cihankom))
Many more individuals have contributed by reporting bugs or feature requests.
See [issue tracker on Github][1], `NEWS.md` and `git log` for details.

@ -7,6 +7,8 @@
#
# OPENSSL_BASE Prefix of OpenSSL library and headers to build against
# LIBEVENT_BASE Prefix of libevent library and headers to build against
# LIBPCAP_BASE Prefix of libpcap library and headers to build against
# LIBNET_BASE Prefix of libnet library and headers to build against
# CHECK_BASE Prefix of check library and headers to build against (optional)
# PKGCONFIG Name/path of pkg-config program to use for auto-detection
# PCFLAGS Additional pkg-config flags
@ -28,9 +30,35 @@
# CPPFLAGS Additional pre-processor flags
# LDFLAGS Additional linker flags
# LIBS Additional libraries to link against
# SOURCE_DATE_EPOCH Set to epoch time to make the build reproducible
#
# You can e.g. create a statically linked binary by running:
# On macOS, the following build environment variables are respected:
#
# DEVELOPER_DIR Override Xcode Command Line Developer Tools directory
# MACOSX_VERSION_MIN Minimal version of macOS to target, e.g. 10.11
# SDK SDK name to build against, e.g. macosx, macosx10.11
#
# Examples:
#
# Build against custom installed libraries under /opt:
# % OPENSSL_BASE=/opt/openssl LIBEVENT_BASE=/opt/libevent make
#
# Create a statically linked binary:
# % PCFLAGS='--static' CFLAGS='-static' LDFLAGS='-static' make
#
# Build a macOS binary for El Capitan using the default SDK from Xcode 7.3.1:
# % MACOSX_VERSION_MIN=10.11 DEVELOPER_DIR=/Applications/Xcode-7.3.1.app/Contents/Developer make
### Mirroring
# Define to disable support for mirroring connection content as emulated
# packets to a network interface (-I/-T options). Doing so will remove the
# dependency on both libnet and libpcap. Use this for constrained environments
# or on platforms without usable libnet/libpcap. Logging connection content to
# PCAP files will remain fully functional (-X/-Y/-y options) as it does not
# make use of libnet and libpcap.
#FEATURES+= -DWITHOUT_MIRROR
### OpenSSL tweaking
@ -71,6 +99,9 @@ DEBUG_CFLAGS?= -g
# Define to add privilege separation server event loop debugging.
#FEATURES+= -DDEBUG_PRIVSEP_SERVER
# Define to add diagnostic output for debugging option parsing.
#FEATURES+= -DDEBUG_OPTS
# When debugging OpenSSL related issues, make sure you use a debug build of
# OpenSSL and consider enabling its debugging options -DREF_PRINT -DREF_CHECK
# for debugging reference counting of OpenSSL objects and/or
@ -89,6 +120,7 @@ DEBUG_CFLAGS?= -g
# Note that you can override the XNU headers used by defining XNU_VERSION.
ifeq ($(shell uname),Darwin)
include Mk/xcode.mk
ifneq ($(wildcard /usr/include/libproc.h),)
FEATURES+= -DHAVE_DARWIN_LIBPROC
endif
@ -153,20 +185,28 @@ INSTALLGID?= 0
BINUID?= $(INSTALLUID)
BINGID?= $(INSTALLGID)
BINMODE?= 0755
CNFUID?= $(INSTALLUID)
CNFGID?= $(INSTALLGID)
CNFMODE?= 0644
MANUID?= $(INSTALLUID)
MANGID?= $(INSTALLGID)
MANMODE?= 0644
EXAMPLESMODE?= 0444
ifeq ($(shell id -u),0)
BINOWNERFLAGS?= -o $(BINUID) -g $(BINGID)
CNFOWNERFLAGS?= -o $(CNFUID) -g $(CNFGID)
MANOWNERFLAGS?= -o $(MANUID) -g $(MANGID)
else
BINOWNERFLAGS?=
CNFOWNERFLAGS?=
MANOWNERFLAGS?=
endif
OPENSSL?= openssl
PKGCONFIG?= pkg-config
PKGCONFIG?= $(shell command -v pkg-config||echo false)
ifeq ($(PKGCONFIG),false)
$(warning pkg-config not found - guessing paths/flags for dependencies)
endif
BASENAME?= basename
CAT?= cat
@ -182,12 +222,13 @@ SORT?= sort
### Variables only used for developer targets
KHASH_URL?= https://github.com/attractivechaos/klib/raw/master/khash.h
GPGSIGNKEY?= 0xB5D3397E
GPGSIGNKEY?= 0xE1520675375F5E35
CPPCHECK?= cppcheck
GPG?= gpg
GIT?= git
WGET?= wget
DOCKER?= docker
BZIP2?= bzip2
COL?= col
@ -204,6 +245,7 @@ TARGET:= $(PKGNAME)
SRCS:= $(filter-out $(wildcard *.t.c),$(wildcard *.c))
HDRS:= $(wildcard *.h)
OBJS:= $(SRCS:.c=.o)
MKFS= $(wildcard GNUmakefile Mk/*.mk)
FEATURES:= $(sort $(FEATURES))
TSRCS:= $(wildcard *.t.c)
@ -212,6 +254,9 @@ TOBJS+= $(filter-out main.o,$(OBJS))
include Mk/buildinfo.mk
VERSION:= $(BUILD_VERSION)
ifdef GITDIR
CFLAGS+= $(DEBUG_CFLAGS)
endif
# Autodetect dependencies known to pkg-config
PKGS:=
@ -227,26 +272,33 @@ PKGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --exists libevent_openssl \
PKGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --exists libevent_pthreads \
&& echo libevent_pthreads)
endif
ifneq ($(filter -DWITHOUT_MIRROR,$(FEATURES)),-DWITHOUT_MIRROR)
ifndef LIBPCAP_BASE
PKGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --exists libpcap \
&& echo libpcap)
endif
endif
TPKGS:=
ifndef CHECK_BASE
TPKGS+= $(shell $(PKGCONFIG) $(PCFLAGS) --exists check \
&& echo check)
endif
# Function: Generate list of base paths to search when locating packages
# $1 packagename
bases= /usr/local/opt/$(1) \
/opt/local \
/usr/local \
/usr
# Function: Locate base path for a package we depend on
# $1 packagename, $2 pattern suffix, $3 override path(s)
locate= $(subst /$(2),,$(word 1,$(wildcard \
$(addsuffix /$(2),$(if $(3),$(3),$(call bases,$(1)))))))
# Autodetect dependencies not known to pkg-config
ifeq (,$(filter openssl,$(PKGS)))
OPENSSL_PAT:= include/openssl/ssl.h
ifdef OPENSSL_BASE
OPENSSL_FIND:= $(wildcard $(OPENSSL_BASE)/$(OPENSSL_PAT))
else
OPENSSL_FIND:= $(wildcard \
/usr/local/opt/openssl/$(OPENSSL_PAT) \
/opt/local/$(OPENSSL_PAT) \
/usr/local/$(OPENSSL_PAT) \
/usr/$(OPENSSL_PAT))
endif
OPENSSL_AVAIL:= $(OPENSSL_FIND:/$(OPENSSL_PAT)=)
OPENSSL_FOUND:= $(word 1,$(OPENSSL_AVAIL))
OPENSSL_FOUND:= $(call locate,openssl,include/openssl/ssl.h,$(OPENSSL_BASE))
OPENSSL:= $(OPENSSL_FOUND)/bin/openssl
ifndef OPENSSL_FOUND
$(error dependency 'OpenSSL' not found; \
@ -254,41 +306,43 @@ $(error dependency 'OpenSSL' not found; \
endif
endif
ifeq (,$(filter libevent,$(PKGS)))
LIBEVENT_PAT:= include/event2/event.h
ifdef LIBEVENT_BASE
LIBEVENT_FIND:= $(wildcard $(LIBEVENT_BASE)/$(LIBEVENT_PAT))
else
LIBEVENT_FIND:= $(wildcard \
/usr/local/opt/libevent/$(LIBEVENT_PAT) \
/opt/local/$(LIBEVENT_PAT) \
/usr/local/$(LIBEVENT_PAT) \
/usr/$(LIBEVENT_PAT))
endif
LIBEVENT_AVAIL:=$(LIBEVENT_FIND:/$(LIBEVENT_PAT)=)
LIBEVENT_FOUND:=$(word 1,$(LIBEVENT_AVAIL))
LIBEVENT_FOUND:=$(call locate,libevent,include/event2/event.h,$(LIBEVENT_BASE))
ifndef LIBEVENT_FOUND
$(error dependency 'libevent 2.x' not found; \
install it or point LIBEVENT_BASE to base path)
endif
endif
ifneq ($(filter -DWITHOUT_MIRROR,$(FEATURES)),-DWITHOUT_MIRROR)
ifeq (,$(filter libpcap,$(PKGS)))
LIBPCAP_FOUND:= $(call locate,libpcap,include/pcap.h,$(LIBPCAP_BASE))
ifndef LIBPCAP_FOUND
$(error dependency 'libpcap' not found; \
install it or point LIBPCAP_BASE to base path)
endif
endif
endif
ifeq (,$(filter check,$(TPKGS)))
CHECK_PAT:= include/check.h
ifdef CHECK_BASE
CHECK_FIND:= $(wildcard $(CHECK_BASE)/$(CHECK_PAT))
else
CHECK_FIND:= $(wildcard \
/usr/local/opt/check/$(CHECK_PAT) \
/opt/local/$(CHECK_PAT) \
/usr/local/$(CHECK_PAT) \
/usr/$(CHECK_PAT))
endif
CHECK_AVAIL:= $(CHECK_FIND:/$(CHECK_PAT)=)
CHECK_FOUND:= $(word 1,$(CHECK_AVAIL))
CHECK_FOUND:= $(call locate,check,include/check.h,$(CHECK_BASE))
ifndef CHECK_FOUND
CHECK_MISSING:= 1
endif
endif
# Always search filesystem for libnet because libnet-config is unreliable
ifneq ($(filter -DWITHOUT_MIRROR,$(FEATURES)),-DWITHOUT_MIRROR)
LIBNET_FOUND:= $(call locate,libnet,include/libnet-1.1/libnet.h,$(LIBNET_BASE))
ifdef LIBNET_FOUND
LIBNET_FOUND_INC:= $(LIBNET_FOUND)/include/libnet-1.1
else
LIBNET_FOUND:= $(call locate,libnet,include/libnet.h,$(LIBNET_BASE))
LIBNET_FOUND_INC:= $(LIBNET_FOUND)/include
endif
ifndef LIBNET_FOUND
$(error dependency 'libnet' not found; \
install it or point LIBNET_BASE to base path)
endif
endif
ifdef OPENSSL_FOUND
PKG_CPPFLAGS+= -I$(OPENSSL_FOUND)/include
PKG_LDFLAGS+= -L$(OPENSSL_FOUND)/lib
@ -305,6 +359,18 @@ endif
ifeq (,$(filter libevent_pthreads,$(PKGS)))
PKG_LIBS+= -levent_pthreads
endif
ifneq ($(filter -DWITHOUT_MIRROR,$(FEATURES)),-DWITHOUT_MIRROR)
ifdef LIBNET_FOUND
PKG_CPPFLAGS+= -I$(LIBNET_FOUND_INC)
PKG_LDFLAGS+= -L$(LIBNET_FOUND)/lib
PKG_LIBS+= -lnet
endif
ifdef LIBPCAP_FOUND
PKG_CPPFLAGS+= -I$(LIBPCAP_FOUND)/include
PKG_LDFLAGS+= -L$(LIBPCAP_FOUND)/lib
PKG_LIBS+= -lpcap
endif
endif
ifdef CHECK_FOUND
TPKG_CPPFLAGS+= -I$(CHECK_FOUND)/include
TPKG_LDFLAGS+= -L$(CHECK_FOUND)/lib
@ -350,8 +416,6 @@ endif
# _FORTIFY_SOURCE requires -O on Linux
ifeq (,$(findstring -O,$(CFLAGS)))
# TODO: -O w/o -g is failing bufferevent_socket_connect for parent dst,
# so either enable -O w/ -g, or disable -O w/o -g (-O2 is failing too?)
CFLAGS+= -O2
endif
@ -378,6 +442,12 @@ endif
ifdef LIBEVENT_FOUND
$(info LIBEVENT_BASE: $(strip $(LIBEVENT_FOUND)))
endif
ifdef LIBPCAP_FOUND
$(info LIBPCAP_BASE: $(strip $(LIBPCAP_FOUND)))
endif
ifdef LIBNET_FOUND
$(info LIBNET_BASE: $(strip $(LIBNET_FOUND)))
endif
ifdef CHECK_FOUND
$(info CHECK_BASE: $(strip $(CHECK_FOUND)))
endif
@ -393,13 +463,16 @@ endif
all: $(TARGET)
$(TARGET).test: $(TOBJS)
$(CC) $(LDFLAGS) $(TPKG_LDFLAGS) -o $@ $^ $(LIBS) $(TPKG_LIBS)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $^ $(LIBS)
build.o: CPPFLAGS+=$(BUILD_CPPFLAGS)
build.o: build.c FORCE
%.t.o: %.t.c $(HDRS) GNUmakefile
%.t.o: %.t.c $(HDRS) $(MKFS)
ifdef CHECK_MISSING
$(error unit test dependency 'check' not found; \
install it or point CHECK_BASE to base path)
@ -407,24 +480,23 @@ endif
$(CC) -c $(CPPFLAGS) $(TCPPFLAGS) $(CFLAGS) $(TPKG_CFLAGS) -o $@ \
-x c $<
%.o: %.c $(HDRS) GNUmakefile
%.o: %.c $(HDRS) $(MKFS)
$(CC) -c $(CPPFLAGS) $(CFLAGS) -o $@ $<
travis: TCPPFLAGS+=-DTRAVIS
travis: test
test: TCPPFLAGS+=-D"TEST_ZEROUSR=\"$(shell id -u -n root||echo 0)\""
test: TCPPFLAGS+=-D"TEST_ZEROGRP=\"$(shell id -g -n root||echo 0)\""
test: $(TARGET).test
buildtest: TCPPFLAGS+=-D"TEST_ZEROUSR=\"$(shell id -u -n root||echo 0)\""
buildtest: TCPPFLAGS+=-D"TEST_ZEROGRP=\"$(shell id -g -n root||echo 0)\""
buildtest: $(TARGET).test
$(MAKE) -C extra/engine
$(MAKE) -C extra/pki testreqs
test: buildtest
./$(TARGET).test
sudotest: test
sudotest: buildtest
sudo ./$(TARGET).test
$(TARGET).test: $(TOBJS)
$(CC) $(LDFLAGS) $(TPKG_LDFLAGS) -o $@ $^ $(LIBS) $(TPKG_LIBS)
travis: TCPPFLAGS+=-DTRAVIS
travis: test
clean:
$(MAKE) -C extra/engine clean
@ -437,7 +509,7 @@ install: $(TARGET)
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man1
test -d $(DESTDIR)$(PREFIX)/$(MANDIR)/man5 || \
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(MANDIR)/man5
test -d $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/sslproxy || \
test -d $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/$(TARGET) || \
$(MKDIR) -p $(DESTDIR)$(PREFIX)/$(EXAMPLESDIR)/$(TARGET)
$(INSTALL) $(BINOWNERFLAGS) -m $(BINMODE) \
$(TARGET) $(DESTDIR)$(PREFIX)/bin/
@ -457,14 +529,16 @@ ifdef GITDIR
lint:
$(CPPCHECK) $(CPPCHECKFLAGS) --force --enable=all --error-exitcode=1 .
manlint: $(TARGET).1
manlint: $(TARGET).1 $(TARGET).conf.5
$(CHECKNR) $(TARGET).1
mantest: $(TARGET).1
$(RM) -f man1
mantest: $(TARGET).1 $(TARGET).conf.5
$(RM) -f man1 man5
$(LN) -sf . man1
$(LN) -sf . man5
$(MAN) -M . 1 $(TARGET)
$(RM) man1
$(MAN) -M . 5 $(TARGET).conf
$(RM) man1 man5
copyright: *.c *.h *.1 *.5 extra/*/*.c
Mk/bin/copyright.py $^
@ -475,10 +549,16 @@ $(PKGNAME)-$(VERSION).1.txt: $(TARGET).1
$(MAN) -M . 1 $(TARGET) | $(COL) -b >$@
$(RM) man1
man: $(PKGNAME)-$(VERSION).1.txt
$(PKGNAME)-$(VERSION).conf.5.txt: $(TARGET).conf.5
$(RM) -f man5
$(LN) -sf . man5
$(MAN) -M . 5 $(TARGET).conf | $(COL) -b >$@
$(RM) man5
man: $(PKGNAME)-$(VERSION).1.txt $(PKGNAME)-$(VERSION).conf.5.txt
manclean:
$(RM) -f $(PKGNAME)-*.1.txt
$(RM) -f $(PKGNAME)-*.1.txt $(PKGNAME)-*.conf.5.txt
fetchdeps:
$(WGET) -O- $(KHASH_URL) >khash.h
@ -516,8 +596,14 @@ realclean: distclean manclean clean
$(MAKE) -C extra/pki clean
endif
docker:
$(DOCKER) build -f docker/sslproxy/Dockerfile --target builder -t sslproxy-builder:$(VERSION) .
$(DOCKER) build -f docker/sslproxy/Dockerfile --target production -t sslproxy:$(VERSION) .
$(DOCKER) run sslproxy:$(VERSION)
FORCE:
.PHONY: all config clean test travis lint install deinstall copyright manlint \
mantest man manclean fetchdeps dist disttest distclean realclean
.PHONY: all config clean buildtest test sudotest travis lint \
install deinstall copyright manlint mantest man manclean fetchdeps \
dist disttest distclean realclean docker

@ -4,19 +4,24 @@ different copyright and license terms:
khash.h:
Copyright (c) 2008, 2009, 2011, Attractive Chaos.
All rights reserved.
Licensed under the MIT license.
https://github.com/attractivechaos/klib
xnu/xnu-*:
Copyright (c) 1988-2017, Apple Inc. and original copyright holders.
All rights reserved.
Licensed under the APSL.
https://opensource.apple.com/
extra/log*.py:
Copyright (C) 2015, Maciej Kotowicz and Daniel Roethlisberger.
All rights reserved.
Licensed under a 2-clause BSD license.
Mk/xcode.mk:
Copyright (c) Daniel Roethlisberger.
Released under the Unlicense.
https://github.com/droe/example.kext
See the respective source and/or license files for details.

@ -1,62 +1,83 @@
#!/usr/bin/env python3
# vim: set ft=python list et ts=8 sts=4 sw=4:
# Update the copyright headers in all source files passed on the command line.
# The copyright headers are comments at the beginning of lines that are
# marked by a dash immediately at the start of the comment.
# The entire copyright header is replaced by the copyright in LICENSE, with the
# exception of contributor's additional Copyright lines, which are kept intact
# as found in each source file.
import sys
import os
MAIN_NAME = 'Daniel Roethlisberger'
MAIN_EMAIL = 'daniel@roe.ch'
TITLE = ('SSLsplit - transparent SSL/TLS interception\n'
'https://www.roe.ch/SSLsplit\n\n')
class Language:
def __init__(self, begin, each, end):
self.begin = begin
self.each = each
self.end = end
def is_end(self, line):
if self.end != None:
return line.startswith(self.end)
else:
return not line.startswith(self.each)
languages = []
languages.append(Language('/*-', ' *', ' */')) # c
languages.append(Language('#-', '#', None)) # scripts and make files
languages.append(Language('.\\"-', '.\\"', None)) # troff
def split_before(s, delimiter):
s1, s2 = s.split(delimiter, 1)
return s1, delimiter + s2
def commentline(prefix, line):
if len(line) > 0:
return prefix + ' ' + line + '\n'
return prefix + '\n'
def license(outfile, filetype):
def license(outfile, language, contribrights=''):
with open('LICENSE', 'r') as f:
# skip title
f.readline()
f.readline()
text = f.read()
text = ('SSLsplit - transparent SSL/TLS interception\n'
'https://www.roe.ch/SSLsplit\n\n') + text.replace(
'and contributors', '<daniel@roe.ch>')
lines = text.splitlines()
if filetype == 'c':
outfile.write('/*-\n')
for line in lines:
outfile.write(commentline(' *', line))
elif filetype == 'script':
outfile.write('#-\n')
for line in lines:
outfile.write(commentline('#', line))
elif filetype == 'man':
outfile.write('.\\"-\n')
for line in lines:
outfile.write(commentline('.\\"', line))
else:
raise RuntimeError()
license = f.read()
header, rest = split_before(license, 'Copyright')
copyright, legalese = split_before(rest, 'All rights reserved')
copyright = copyright.replace('and contributors', '<%s>' % MAIN_EMAIL)
text = TITLE + copyright + contribrights + legalese
outfile.write('%s\n' % language.begin)
for line in text.splitlines():
outfile.write(commentline(language.each, line))
def mangle(outfile, infile):
contribs = []
language = None
have_first = False
have_header = False
for line in infile:
if have_header:
outfile.write(line)
elif have_first:
if (filetype == 'c' and line.startswith(' */')) or \
(filetype == 'script' and not line.startswith('#')) or \
(filetype == 'man' and not line.startswith('.\\"')):
if language.is_end(line):
license(outfile, language, ''.join(contribs))
outfile.write(line)
have_header = True
elif 'Copyright' in line and not MAIN_NAME in line:
prefix, copyright = split_before(line, 'Copyright')
contribs.append(copyright)
else:
if line.startswith('/*-'):
filetype = 'c'
elif line.startswith('#-'):
filetype = 'script'
elif line.startswith('.\\"-'):
filetype = 'man'
else:
for lang in languages:
if line.startswith(lang.begin):
language = lang
break
if language == None:
outfile.write(line)
continue
license(outfile, filetype)
have_first = True
for fn in sys.argv[1:]:

@ -34,6 +34,7 @@ case "$EVENT" in
libevent-2.1.8)
EVENTURL=https://github.com/libevent/libevent/releases/download/release-2.1.8-stable/libevent-2.1.8-stable.tar.gz
EVENTPATCH=Mk/patches/libevent-2.1.8.diff
EVENTOPTS="$EVENTOPTS --disable-libevent-regress --disable-samples"
;;
libevent-2.0.22)
EVENTURL=https://github.com/libevent/libevent/releases/download/release-2.0.22-stable/libevent-2.0.22-stable.tar.gz
@ -44,13 +45,16 @@ libevent-2.0.22)
esac
if [ ! -d "$HOME/opt/$SSL" ]; then
if [ "`uname`" = "Linux" ]; then
SSLOPTS="$SSLOPTS -Wl,-rpath=$HOME/opt/$SSL/lib"
fi
wget "$SSLURL" || exit 1
tar -xzvf "$SSL.tar.gz" || exit 1
cd "$SSL" || exit 1
./config shared \
-Wl,-rpath="$HOME/opt/$SSL/lib" \
--prefix="$HOME/opt/$SSL" \
--openssldir="$HOME/opt/$SSL" || exit 1
--openssldir="$HOME/opt/$SSL" \
$SSLOPTS || exit 1
make && make install || { rm -rf "$HOME/opt/$SSL"; exit 1; }
cd ..
fi
@ -65,7 +69,7 @@ if [ ! -d "$HOME/opt/$EVENT" ]; then
if [ -n "$EVENTPATCH" ]; then
patch -p0 < ../$EVENTPATCH || exit 1
fi
./configure --prefix="$HOME/opt/$EVENT" || exit 1
./configure --prefix="$HOME/opt/$EVENT" $EVENTOPTS || exit 1
make && make install || { rm -rf "$HOME/opt/$EVENT"; exit 1; }
cd ..
fi

@ -3,6 +3,7 @@
# in: BUILD_INFO (optional)
# in: OPENSSL (optional)
# in: OPENSSL_FOUND (optional)
# in: SOURCE_DATE_EPOCH (optional)
ifndef PKGNAME
$(error PKGNAME not defined)

@ -0,0 +1,42 @@
# macOS Xcode and SDK selection makefile
# Authored 2018, Daniel Roethlisberger
# Provided under the Unlicense
# https://github.com/droe/example.kext
# DEVELOPER_DIR override Xcode Command Line Developer Tools directory
# MACOSX_VERSION_MIN minimal version of macOS to target, e.g. 10.11
# SDK SDK name to build against (e.g. macosx, macosx10.11, ...);
# for kernel extensions, use macosx$(MACOSX_VERSION_MIN)
# target specific macOS min version
ifdef MACOSX_VERSION_MIN
CFLAGS+= -mmacosx-version-min=$(MACOSX_VERSION_MIN)
LDFLAGS+= -mmacosx-version-min=$(MACOSX_VERSION_MIN)
endif
# select specific Xcode
ifdef DEVELOPER_DIR
ifndef SDK
SDK:= macosx
endif
else
DEVELOPER_DIR:= $(shell xcode-select -p)
endif
# activate the selected Xcode and SDK
ifdef SDK
SDKPATH:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) --show-sdk-path||echo none)
ifeq "$(SDKPATH)" "none"
$(error SDK not found)
endif
CPPFLAGS+= -isysroot $(SDKPATH)
LDFLAGS+= -isysroot $(SDKPATH)
CC:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) cc||echo false)
CXX:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) c++||echo false)
CODESIGN:= $(shell DEVELOPER_DIR="$(DEVELOPER_DIR)" xcrun -find -sdk $(SDK) codesign||echo false)
else
CC?= cc
CXX?= c++
CODESIGN?= codesign
endif

@ -1,13 +1,31 @@
### SSLsplit develop
- Minor bugfixes and improvements.
### SSLsplit 0.5.4 2018-10-29
This release includes work sponsored by HackerOne.
- Add PCAP content log modes (-X, -Y, -y) and a packet mirroring content log
mode (-T, -I) to encapsulate decrypted traffic segments in emulated TCP, IP
and Ethernet headers and write the result to PCAP files or send it to a
packet capture host on the local network segment (issue #215, based on pull
req #149 by @cihankom).
- Suppress Expect-CT header in order to avoid Certificate Transparency log
lookup failures (issue #205).
- Add -x option for activating an OpenSSL engine (issue #204, pull req #206).
- Add -f option for loading configuration from file (pull req #193).
- Add `sudotest` target with unit tests which require privileges to run.
- Add XNU headers for macOS High Sierra 10.13.1, 10.13.2 and 10.13.3.
- Add -f option for loading configuration from file, including a new manual
page, sslsplit.conf(5) (pull req #193).
- Bypass privilege separation overhead for when privileges are not actually
dropped; this allows the use of `-u root` to actively prevent privilege
separation and remove the associated IPC overhead (issue #222).
- Add `sudotest` target for optional unit tests which require privileges to
run successfully.
- Fix crash when using LibreSSL (pull req #207).
- Add XNU headers for macOS High Sierra 10.13.1 to 10.13.6.
- Release sig PGP/GPG key rollover from 0xB5D3397E to 0xE1520675375F5E35.
- Minor bugfixes and improvements.

@ -14,14 +14,14 @@ as UTM services, for deep SSL inspection. See [this
presentation](https://drive.google.com/open?id=12YaGIGs0-xfpqMNAY3rzUbIyed-Tso8W)
for a summary.
SSLproxy is designed to transparently terminate connections that are redirected
to it using a network address translation engine. SSLproxy then terminates
SSL/TLS and initiates a new SSL/TLS connection to the original destination
address. Packets received on the client side are decrypted and sent to the
program listening on a port given in the proxy specification. SSLproxy inserts
in the first packet the address and port it is expecting to receive the packets
back from the program. Upon receiving the packets back, SSLproxy re-encrypts
and sends them to their original destination. The return traffic follows the
SSLproxy is designed to transparently terminate connections that are redirected
to it using a network address translation engine. SSLproxy then terminates
SSL/TLS and initiates a new SSL/TLS connection to the original destination
address. Packets received on the client side are decrypted and sent to the
program listening on a port given in the proxy specification. SSLproxy inserts
in the first packet the address and port it is expecting to receive the packets
back from the program. Upon receiving the packets back, SSLproxy re-encrypts
and sends them to their original destination. The return traffic follows the
same path back to the client in reverse order.
![Mode of Operation
@ -77,18 +77,18 @@ Indication (SNI) and is able to work with RSA, DSA and ECDSA keys and DHE and
ECDHE cipher suites. Depending on the version of OpenSSL, SSLproxy supports
SSL 3.0, TLS 1.0, TLS 1.1 and TLS 1.2, and optionally SSL 2.0 as well.
For SSL and HTTPS connections, SSLproxy generates and signs forged X509v3
certificates on-the-fly, mimicking the original server certificate's subject
DN, subjectAltName extension and other characteristics. SSLproxy has the
ability to use existing certificates of which the private key is available,
instead of generating forged ones. SSLproxy supports NULL-prefix CN
certificates but otherwise does not implement exploits against specific
For SSL and HTTPS connections, SSLproxy generates and signs forged X509v3
certificates on-the-fly, mimicking the original server certificate's subject
DN, subjectAltName extension and other characteristics. SSLproxy has the
ability to use existing certificates of which the private key is available,
instead of generating forged ones. SSLproxy supports NULL-prefix CN
certificates but otherwise does not implement exploits against specific
certificate verification vulnerabilities in SSL/TLS stacks.
SSLproxy implements a number of defenses against mechanisms which would
normally prevent MitM attacks or make them more difficult. SSLproxy can deny
OCSP requests in a generic way. For HTTP and HTTPS connections, SSLproxy
mangles headers to prevent server-instructed public key pinning (HPKP), avoid
SSLproxy implements a number of defences against mechanisms which would
normally prevent MitM attacks or make them more difficult. SSLproxy can deny
OCSP requests in a generic way. For HTTP and HTTPS connections, SSLproxy
mangles headers to prevent server-instructed public key pinning (HPKP), avoid
strict transport security restrictions (HSTS), avoid Certificate Transparency
enforcement (Expect-CT) and prevent switching to QUIC/SPDY, HTTP/2 or
WebSockets (Upgrade, Alternate Protocols). HTTP compression, encodings and
@ -99,13 +99,19 @@ usage. Accordingly, connections are closed if they remain idle for a certain
period of time. The default timeout is 120 seconds, which can be changed in a
configuration file.
SSLproxy verifies upstream certificates by default. If the verification fails, the
connection is terminated immediately. This is in contrast to SSLsplit, because
in order to maximize the chances that a connection can be successfully split,
SSLsplit accepts all certificates including self-signed ones. See [The Risks of
SSL Inspection](https://insights.sei.cmu.edu/cert/2015/03/the-risks-of-ssl-inspection.html)
SSLproxy verifies upstream certificates by default. If the verification fails,
the connection is terminated immediately. This is in contrast to SSLsplit,
because in order to maximize the chances that a connection can be successfully
split, SSLsplit accepts all certificates by default, including self-signed
ones. See [The Risks of SSL Inspection]
(https://insights.sei.cmu.edu/cert/2015/03/the-risks-of-ssl-inspection.html)
for the reasons of this difference.
Logging options include traditional SSLproxy connect and content log files as
well as PCAP files and mirroring decrypted traffic to a network interface.
Additionally, certificates, master secrets and local process information can be
logged.
As SSLproxy is based on SSLsplit, this is a modified SSLsplit README file.
See the manual page sslproxy(1) for details on using SSLproxy and setting up
the various NAT engines.
@ -113,10 +119,11 @@ the various NAT engines.
## Requirements
SSLproxy depends on the OpenSSL and libevent 2.x libraries.
The build depends on GNU make and a POSIX.2 environment in `PATH`.
If available, pkg-config is used to locate and configure the dependencies.
The optional unit tests depend on the check library.
SSLproxy depends on the OpenSSL, libevent 2.x, libpcap and libnet 1.1.x
libraries by default; libpcap and libnet are not needed if the mirroring
feature is omitted. The build depends on GNU make and a POSIX.2 environment in
`PATH`. If available, pkg-config is used to locate and configure the
dependencies. The optional unit tests depend on the check library.
SSLproxy currently supports the following operating systems and NAT mechanisms:
@ -129,13 +136,13 @@ Support for local process information (`-i`) is currently available on Mac OS X
and FreeBSD.
SSL/TLS features and compatibility greatly depend on the version of OpenSSL
linked against; for optimal results, use a recent release of OpenSSL proper.
OpenSSL forks like BoringSSL may or may not work.
linked against. For optimal results, use a recent release of OpenSSL or
LibreSSL.
## Installation
With OpenSSL, libevent 2.x, pkg-config and check available, run:
With the requirements above available, run:
make
make test # optional unit tests
@ -144,31 +151,35 @@ With OpenSSL, libevent 2.x, pkg-config and check available, run:
Dependencies are autoconfigured using pkg-config. If dependencies are not
picked up and fixing `PKG_CONFIG_PATH` does not help, you can specify their
respective locations manually by setting `OPENSSL_BASE`, `LIBEVENT_BASE` and/or
`CHECK_BASE` to the respective prefixes.
respective locations manually by setting `OPENSSL_BASE`, `LIBEVENT_BASE`,
`LIBPCAP_BASE`, `LIBNET_BASE` and/or `CHECK_BASE` to the respective prefixes.
You can override the default install prefix (`/usr/local`) by setting `PREFIX`.
For more build options see `GNUmakefile` and `defaults.h`.
For more build options and build-time defaults see [`GNUmakefile`](GNUmakefile)
and [`defaults.h`](defaults.h).
## Documentation
See the manual page `sslproxy.1` for user documentation.
See `NEWS.md` for release notes listing significant changes between releases.
See the manual pages `sslproxy(1)` and `sslproxy.conf(5)` for user
documentation. See [`NEWS.md`](NEWS.md) for release notes listing significant
changes between releases and [`SECURITY.md`](SECURITY.md) for information on
security vulnerability disclosure.
## License
SSLsplit is provided under a 2-clause BSD license.
SSLsplit contains components licensed under the MIT and APSL licenses.
See `LICENSE`, `LICENSE.contrib` and `LICENSE.third` as well as the respective
source file headers for details.
See [`LICENSE`](LICENSE), [`LICENSE.contrib`](LICENSE.contrib) and
[`LICENSE.third`](LICENSE.third) as well as the respective source file headers
for details.
The modifications for SSLproxy are licensed under the same terms as SSLsplit.
## Credits
See `AUTHORS.md` for the list of contributors.
See [`AUTHORS.md`](AUTHORS.md) for the list of contributors.
SSLsplit was inspired by `mitm-ssl` by Claes M. Nyberg and `sslsniff` by Moxie
Marlinspike, but shares no source code with them.

@ -114,13 +114,10 @@ START_TEST(cache_fkcrt_04)
cachemgr_fini();
fail_unless(c1->references == 1, "refcount != 1");
X509_free(c2);
#ifndef LIBRESSL_VERSION_NUMBER
#if 0
/* deliberate access of free'd X509* */
fail_unless(c1->references == 0, "refcount != 0");
#else /* LIBRESSL_VERSION_NUMBER */
fprintf(stderr, "deliberate access after free test in cache_fkcrt_04 "
"omitted because LibreSSL fails with refcount != 0\n");
#endif /* LIBRESSL_VERSION_NUMBER */
#endif
fail_unless(cachemgr_preinit() != -1, "reinit");
}
END_TEST

@ -111,13 +111,10 @@ START_TEST(cache_tgcrt_04)
cachemgr_fini();
fail_unless(c1->references == 1, "refcount != 1");
cert_free(c2);
#ifndef LIBRESSL_VERSION_NUMBER
#if 0
/* deliberate access of free'd cert_t* */
fail_unless(c1->references == 0, "refcount != 0");
#else /* LIBRESSL_VERSION_NUMBER */
fprintf(stderr, "deliberate access after free test in cache_tgcrt_04 "
"omitted because LibreSSL fails with refcount != 0\n");
#endif /* LIBRESSL_VERSION_NUMBER */
#endif
fail_unless(cachemgr_preinit() != -1, "reinit");
}
END_TEST

@ -62,14 +62,10 @@ START_TEST(cert_refcount_inc_01)
cert_free(c);
fail_unless(c->references == 1, "refcount mismatch");
cert_free(c);
#ifndef LIBRESSL_VERSION_NUMBER
#if 0
/* deliberate access after last free() */
fail_unless(c->references == 0, "refcount mismatch");
#else /* LIBRESSL_VERSION_NUMBER */
fprintf(stderr, "deliberate access after free test in "
"cert_refcount_inc_01 omitted because LibreSSL fails with refcount "
"mismatch\n");
#endif /* LIBRESSL_VERSION_NUMBER */
#endif
}
END_TEST

@ -69,6 +69,17 @@
*/
#define DFLT_CURVE "prime256v1"
/*
* Default leaf key RSA keysize in bits.
*
* While browsers still generally accept it, use a leaf key size of 1024 bit
* for leaf keys. When browsers start to sunset 1024 bit RSA in leaf keys, we
* will need to make this value bigger, and/or configurable.
* Until then, users who want a different size can always use their own
* pre-generated leaf key instead of generating one automatically.
*/
#define DFLT_LEAFKEY_RSABITS 1024
#endif /* !DEFAULTS_H */
/* vim: set noet ft=c: */

@ -39,6 +39,7 @@
#define CONNECT_CMD "openssl s_client -connect www.google.com:443" \
" -quiet -no_ign_eof </dev/null >/dev/null 2>/dev/null"
#ifndef DOCKER
START_TEST(defaults_dropuser_01)
{
fail_unless(0 == sys_privdrop(DFLT_DROPUSER, NULL, NULL),
@ -49,7 +50,7 @@ END_TEST
/*
* This test is designed to fail in the third assertion if the currently
* configured default dropuser is not allowed to make outbound network
* connections.
* connections. It also fails if we do not have Internet connection.
*/
START_TEST(defaults_dropuser_02)
{
@ -61,6 +62,7 @@ START_TEST(defaults_dropuser_02)
"connect failed for default dropuser " DFLT_DROPUSER);
}
END_TEST
#endif /* DOCKER */
Suite *
defaults_suite(void)
@ -71,13 +73,18 @@ defaults_suite(void)
s = suite_create("defaults");
tc = tcase_create("dropuser");
#ifndef DOCKER
if (getuid() == 0) {
tcase_add_test(tc, defaults_dropuser_01);
tcase_add_test(tc, defaults_dropuser_02);
} else {
fprintf(stderr, "2 tests omitted because "
"not running as root\n");
fprintf(stderr, "defaults: 2 tests omitted because "
"not building as root\n");
}
#else /* DOCKER */
fprintf(stderr, "defaults: 2 tests omitted because "
"building in docker\n");
#endif /* DOCKER */
suite_add_tcase(s, tc);
return s;

@ -0,0 +1,17 @@
FROM alpine:3.8 as builder
RUN apk add --no-cache fts libressl libevent libpcap libnet
RUN apk add --no-cache libressl-dev libevent-dev libpcap-dev libnet-dev check-dev libc-dev fts-dev linux-headers gcc make git
COPY . /opt/sslproxy
WORKDIR /opt/sslproxy
ENV LIBS -lfts
ENV TCPPFLAGS -DDOCKER
RUN export SOURCE_DATE_EPOCH=$(stat -c '%Y' *.c *.h|sort -r|head -1); make clean && make all test
FROM alpine:3.8 as production
RUN apk add --no-cache fts libressl libevent libpcap libnet
WORKDIR /root/
COPY --from=builder /opt/sslproxy/sslproxy /usr/local/bin/sslproxy
#EXPOSE 80 443
ENTRYPOINT [ "sslproxy" ]
CMD [ "-V" ]

@ -17,7 +17,7 @@ NO_SSL2:= $(shell $(OPENSSL) s_server -h 2>&1|awk '/-no_ssl2/ {print $$1}')
all: rsa dsa ec targets server pwd
testreqs: rsa targets server
testreqs: rsa targets server session
session: session.pem
@ -112,7 +112,7 @@ targets/wildcard.roe.ch.pem: rsa.crt
targets/wildcard.roe.ch.crt
# localhost network connectivity is required
session.pem: server.pem
session.pem: | server.pem
$(OPENSSL) s_server -accept 46143 -cert server.pem -quiet $(NO_SSL2) & \
pid=$$! ; \
sleep 1 ; \

1069
log.c

File diff suppressed because it is too large Load Diff

23
log.h

@ -61,7 +61,7 @@ extern logger_t *masterkey_log;
#define log_masterkey_print_free(s) \
logger_print_freebuf(masterkey_log, NULL, 0, (s))
#define log_masterkey_write_free(buf, sz) \
logger_write_freebuf(masterkey_log, 0, NULL, 0, (buf), (sz))
logger_write_freebuf(masterkey_log, NULL, 0, (buf), (sz))
extern logger_t *connect_log;
#define log_connect_printf(fmt, ...) \
@ -73,17 +73,28 @@ extern logger_t *connect_log;
#define log_connect_print_free(s) \
logger_print_freebuf(connect_log, NULL, 0, (s))
#define log_connect_write_free(buf, sz) \
logger_write_freebuf(connect_log, 0, NULL, 0, (buf), (sz))
logger_write_freebuf(connect_log, NULL, 0, (buf), (sz))
int log_stats(const char *);
int log_conn(const char *);
typedef struct log_content_ctx log_content_ctx_t;
int log_content_open(log_content_ctx_t **, opts_t *, char *, char *, char *,
char *, char *, char *, char *) NONNULL(1,2,3) WUNRES;
struct log_content_file_ctx;
struct log_content_pcap_ctx;
struct log_content_mirror_ctx;
struct log_content_ctx {
struct log_content_file_ctx *file;
struct log_content_pcap_ctx *pcap;
struct log_content_mirror_ctx *mirror;
};
int log_content_open(log_content_ctx_t *, opts_t *,
const struct sockaddr *, socklen_t,
const struct sockaddr *, socklen_t,
char *, char *, char *, char *,
char *, char *, char *) NONNULL(1,2,3) WUNRES;
int log_content_submit(log_content_ctx_t *, logbuf_t *, int)
NONNULL(1,2) WUNRES;
int log_content_close(log_content_ctx_t **, int) NONNULL(1) WUNRES;
int log_content_close(log_content_ctx_t *, int) NONNULL(1) WUNRES;
int log_content_split_pathspec(const char *, char **,
char **) NONNULL(1,2,3) WUNRES;
@ -91,7 +102,7 @@ int log_cert_submit(const char *, X509 *) NONNULL(1,2) WUNRES;
int log_preinit(opts_t *) NONNULL(1) WUNRES;
void log_preinit_undo(void);
int log_init(opts_t *, proxy_ctx_t *, int, int) NONNULL(1,2) WUNRES;
int log_init(opts_t *, proxy_ctx_t *, int[3]) NONNULL(1,2) WUNRES;
void log_fini(void);
int log_reopen(void) WUNRES;
void log_exceptcb(void);

@ -32,7 +32,6 @@
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/syslog.h>
/*
* Dynamic log buffer with zero-copy chaining, generic void * file handle
@ -46,7 +45,7 @@
* logbuf_new() in case it fails returning NULL.
*/
logbuf_t *
logbuf_new(int level, void *buf, size_t sz, void *fh, logbuf_t *next)
logbuf_new(int level, void *buf, size_t sz, logbuf_t *next)
{
logbuf_t *lb;
@ -58,9 +57,15 @@ logbuf_new(int level, void *buf, size_t sz, void *fh, logbuf_t *next)
lb->prio = level;
lb->buf = buf;
lb->sz = sz;
lb->fh = fh;
lb->ctl = 0;
lb->next = next;
if (next) {
lb->fh = next->fh;
lb->ctl = next->ctl;
lb->next = next;
} else {
lb->fh = NULL;
lb->ctl = 0;
lb->next = NULL;
}
return lb;
}
@ -68,7 +73,7 @@ logbuf_new(int level, void *buf, size_t sz, void *fh, logbuf_t *next)
* Create new logbuf, allocating sz bytes into the internal buffer.
*/
logbuf_t *
logbuf_new_alloc(size_t sz, void *fh, logbuf_t *next)
logbuf_new_alloc(size_t sz, logbuf_t *next)
{
logbuf_t *lb;
@ -79,9 +84,15 @@ logbuf_new_alloc(size_t sz, void *fh, logbuf_t *next)
return NULL;
}
lb->sz = sz;
lb->fh = fh;
lb->ctl = 0;
lb->next = next;
if (next) {
lb->fh = next->fh;
lb->ctl = next->ctl;
lb->next = next;
} else {
lb->fh = NULL;
lb->ctl = 0;
lb->next = NULL;
}
return lb;
}
@ -89,7 +100,7 @@ logbuf_new_alloc(size_t sz, void *fh, logbuf_t *next)
* Create new logbuf, copying buf into a newly allocated internal buffer.
*/
logbuf_t *
logbuf_new_copy(const void *buf, size_t sz, void *fh, logbuf_t *next)
logbuf_new_copy(const void *buf, size_t sz, logbuf_t *next)
{
logbuf_t *lb;
@ -101,17 +112,23 @@ logbuf_new_copy(const void *buf, size_t sz, void *fh, logbuf_t *next)
}
memcpy(lb->buf, buf, sz);
lb->sz = sz;
lb->fh = fh;
lb->ctl = 0;
lb->next = next;
if (next) {
lb->fh = next->fh;
lb->ctl = next->ctl;
lb->next = next;
} else {
lb->fh = NULL;
lb->ctl = 0;
lb->next = NULL;
}
return lb;
}
/*
* Create new logbuf using printf, setting fh and next.
* Create new logbuf using printf.
*/
logbuf_t *
logbuf_new_printf(void *fh, logbuf_t *next, const char *fmt, ...)
logbuf_new_printf(logbuf_t *next, const char *fmt, ...)
{
va_list ap;
logbuf_t *lb;
@ -125,9 +142,76 @@ logbuf_new_printf(void *fh, logbuf_t *next, const char *fmt, ...)
free(lb);
return NULL;
}
lb->fh = fh;
lb->ctl = 0;
lb->next = next;
if (next) {
lb->fh = next->fh;
lb->ctl = next->ctl;
lb->next = next;
} else {
lb->fh = NULL;
lb->ctl = 0;
lb->next = NULL;
}
return lb;
}
/*
* Create new logbuf from lb. If combine is set, combine all the buffer
* segments into a single contiguous one. Otherwise, copy segment by segment.
*/
logbuf_t *
logbuf_new_deepcopy(logbuf_t *lb, int combine)
{
logbuf_t *lbnew;
unsigned char *p;
if (!lb)
return NULL;
if (combine) {
lbnew = logbuf_new_alloc(logbuf_size(lb), NULL);
if (!lbnew)
return NULL;
lbnew->fh = lb->fh;
lbnew->ctl = lb->ctl;
p = lbnew->buf;
while (lb) {
memcpy(p, lb->buf, lb->sz);
p += lb->sz;
lb = lb->next;
}
} else {
lbnew = logbuf_new_copy(lb->buf, lb->sz, NULL);
if (!lbnew)
return NULL;
lbnew->fh = lb->fh;
lbnew->ctl = lb->ctl;
lbnew->next = logbuf_new_deepcopy(lb->next, 0);
}
return lbnew;
}
logbuf_t *
logbuf_make_contiguous(logbuf_t *lb) {
unsigned char *p;
logbuf_t *lbtmp;
if (!lb)
return NULL;
if (!lb->next)
return lb;
p = realloc(lb->buf, logbuf_size(lb));
if (!p)
return NULL;
lb->buf = p;
lbtmp = lb;
p += lbtmp->sz;
while ((lbtmp = lbtmp->next)) {
memcpy(p, lbtmp->buf, lbtmp->sz);
lb->sz += lbtmp->sz;
p += lbtmp->sz;
}
logbuf_free(lb->next);
lb->next = NULL;
return lb;
}
@ -155,8 +239,7 @@ ssize_t
logbuf_write_free(logbuf_t *lb, writefunc_t writefunc)
{
ssize_t rv1, rv2 = 0;
rv1 = writefunc(lb->prio, lb->fh, lb->buf, lb->sz);
rv1 = writefunc(lb->prio, lb->fh, lb->ctl, lb->buf, lb->sz);
if (lb->buf) {
free(lb->buf);
}
@ -176,7 +259,7 @@ logbuf_write_free(logbuf_t *lb, writefunc_t writefunc)
}
/*
* Free dynbuf including internal and chained buffers.
* Free logbuf including internal and chained buffers.
*/
void
logbuf_free(logbuf_t *lb)

@ -43,13 +43,14 @@ typedef struct logbuf {
struct logbuf *next;
} logbuf_t;
typedef ssize_t (*writefunc_t)(int, void *, const void *, size_t);
typedef ssize_t (*writefunc_t)(int, void *, unsigned long, const void *, size_t);
logbuf_t * logbuf_new(int, void *, size_t, void *, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_alloc(size_t, void *, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_copy(const void *, size_t, void *, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_printf(void *, logbuf_t *, const char *, ...)
MALLOC PRINTF(3,4);
logbuf_t * logbuf_new(int, void *, size_t, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_alloc(size_t, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_copy(const void *, size_t, logbuf_t *) MALLOC;
logbuf_t * logbuf_new_printf(logbuf_t *, const char *, ...) MALLOC PRINTF(2,3);
logbuf_t * logbuf_new_deepcopy(logbuf_t *, int) MALLOC;
logbuf_t * logbuf_make_contiguous(logbuf_t *) WUNRES;
ssize_t logbuf_size(logbuf_t *) NONNULL(1) WUNRES;
ssize_t logbuf_write_free(logbuf_t *, writefunc_t) NONNULL(1);
void logbuf_free(logbuf_t *) NONNULL(1);
@ -59,6 +60,12 @@ void logbuf_free(logbuf_t *) NONNULL(1);
#define logbuf_ctl_unset(x, y) (x)->ctl &= ~(y)
#define logbuf_ctl_isset(x, y) (!!((x)->ctl & (y)))
#define LBFLAG_REOPEN (1 << 0) /* logger */
#define LBFLAG_OPEN (1 << 1) /* logger */
#define LBFLAG_CLOSE (1 << 2) /* logger */
#define LBFLAG_IS_REQ (1 << 3) /* pcap/mirror content log */
#define LBFLAG_IS_RESP (1 << 4) /* pcap/mirror content log */
#endif /* !LOGBUF_H */
/* vim: set noet ft=c: */

@ -0,0 +1,66 @@
/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "logbuf.h"
#include <string.h>
#include <check.h>
START_TEST(logbuf_make_contiguous_01)
{
logbuf_t *lb;
lb = logbuf_new_printf(NULL, "%s", "789");
lb = logbuf_new_printf(lb, "%s", "456");
lb = logbuf_new_printf(lb, "%s", "123");
lb = logbuf_make_contiguous(lb);
fail_unless(!!lb, "logbuf_make_contiguous failed");
fail_unless(!lb->next, "multiple buffers");
fail_unless(logbuf_size(lb) == 9, "buffer size incorrect");
fail_unless(!memcmp(lb->buf, "123456789", 9), "buffer value incorrect");
logbuf_free(lb);
}
END_TEST
Suite *
logbuf_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("logbuf");
tc = tcase_create("");
tcase_add_test(tc, logbuf_make_contiguous_01);
suite_add_tcase(s, tc);
return s;
}
/* vim: set noet ft=c: */

@ -54,10 +54,6 @@ struct logger {
thrqueue_t *queue;
};
#define LBFLAG_REOPEN (1 << 0)
#define LBFLAG_OPEN (1 << 1)
#define LBFLAG_CLOSE (1 << 2)
static void
logger_clear(logger_t *logger)
{
@ -65,9 +61,19 @@ logger_clear(logger_t *logger)
}
/*
* Create new logger with a specific write function callback.
* The callback will be executed in the logger's writer thread,
* not in the thread calling logger_submit().
* Create new logger with a set of specific function callbacks:
*
* reopenfunc: handle SIGHUP for the log by reopening all open files across
* multiple connections
* openfunc: open a new log for a new connection
* closefunc: close a log for a connection
* writefunc: write a single logbuf to the log
* prepfunc: prepare a log buffer before adding it to the logbuffer's queue
* exceptfunc: called after failed callback operations
*
* All callbacks except prepfunc will be executed in the logger's writer
* thread, not in the thread calling logger_submit(). Prepfunc will be called
* in the thread calling logger_submit().
*/
logger_t *
logger_new(logger_reopen_func_t reopenfunc, logger_open_func_t openfunc,
@ -113,12 +119,16 @@ int
logger_submit(logger_t *logger, void *fh, unsigned long prepflags,
logbuf_t *lb)
{
if (lb) {
lb->fh = fh;
logbuf_ctl_clear(lb);
}
if (logger->prep)
lb = logger->prep(fh, prepflags, lb);
/* If we got passed lb == NULL and prep callback did not replace it
* with an actual log buffer, stop here. */
if (!lb)
return 0;
lb->fh = fh;
logbuf_ctl_clear(lb);
if (thrqueue_enqueue(logger->queue, lb)) {
return 0;
} else {
@ -138,7 +148,8 @@ logger_reopen(logger_t *logger)
if (!logger->reopen)
return 0;
lb = logbuf_new(0, NULL, 0, NULL, NULL);
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
return -1;
logbuf_ctl_set(lb, LBFLAG_REOPEN);
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
}
@ -157,7 +168,8 @@ logger_open(logger_t *logger, void *fh)
if (!logger->open)
return 0;
lb = logbuf_new(0, NULL, 0, NULL, NULL);
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
return -1;
lb->fh = fh;
logbuf_ctl_set(lb, LBFLAG_OPEN);
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
@ -169,15 +181,17 @@ logger_open(logger_t *logger, void *fh)
* Returns 0 on success, -1 on failure.
*/
int
logger_close(logger_t *logger, void *fh)
logger_close(logger_t *logger, void *fh, unsigned long ctl)
{
logbuf_t *lb;
if (!logger->close)
return 0;
lb = logbuf_new(0, NULL, 0, NULL, NULL);
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
return -1;
lb->fh = fh;
lb->ctl = ctl;
logbuf_ctl_set(lb, LBFLAG_CLOSE);
return thrqueue_enqueue(logger->queue, lb) ? 0 : -1;
}
@ -200,7 +214,7 @@ logger_thread(void *arg)
if (logger->open(lb->fh) != 0)
e = 1;
} else if (logbuf_ctl_isset(lb, LBFLAG_CLOSE)) {
logger->close(lb->fh);
logger->close(lb->fh, lb->ctl);
} else {
if (logbuf_write_free(lb, logger->write) < 0)
e = 1;
@ -287,9 +301,9 @@ logger_printf(logger_t *logger, void *fh, unsigned long prepflags,
va_list ap;
logbuf_t *lb;
lb = logbuf_new(0, NULL, 0, fh, NULL);
if (!lb)
if (!(lb = logbuf_new(0, NULL, 0, NULL)))
return -1;
lb->fh = fh;
va_start(ap, fmt);
lb->sz = vasprintf((char**)&lb->buf, fmt, ap);
va_end(ap);
@ -305,8 +319,9 @@ logger_write(logger_t *logger, void *fh, unsigned long prepflags,
{
logbuf_t *lb;
if (!(lb = logbuf_new_copy(buf, sz, fh, NULL)))
if (!(lb = logbuf_new_copy(buf, sz, NULL)))
return -1;
lb->fh = fh;
return logger_submit(logger, fh, prepflags, lb);
}
int
@ -315,8 +330,9 @@ logger_print(logger_t *logger, void *fh, unsigned long prepflags,
{
logbuf_t *lb;
if (!(lb = logbuf_new_copy(s, strlen(s), fh, NULL)))
if (!(lb = logbuf_new_copy(s, strlen(s), NULL)))
return -1;
lb->fh = fh;
return logger_submit(logger, fh, prepflags, lb);
}
int
@ -325,8 +341,9 @@ logger_write_freebuf(logger_t *logger, int level, void *fh, unsigned long prepfl
{
logbuf_t *lb;
if (!(lb = logbuf_new(level, buf, sz, fh, NULL)))
if (!(lb = logbuf_new(level, buf, sz, NULL)))
return -1;
lb->fh = fh;
return logger_submit(logger, fh, prepflags, lb);
}
int
@ -335,8 +352,9 @@ logger_print_freebuf(logger_t *logger, void *fh, unsigned long prepflags,
{
logbuf_t *lb;
if (!(lb = logbuf_new(0, s, strlen(s), fh, NULL)))
if (!(lb = logbuf_new(0, s, strlen(s), NULL)))
return -1;
lb->fh = fh;
return logger_submit(logger, fh, prepflags, lb);
}

@ -37,8 +37,9 @@
typedef int (*logger_reopen_func_t)(void);
typedef int (*logger_open_func_t)(void *);
typedef void (*logger_close_func_t)(void *);
typedef ssize_t (*logger_write_func_t)(int, void *, const void *, size_t);
typedef void (*logger_close_func_t)(void *, unsigned long);
typedef ssize_t (*logger_write_func_t)(int, void *, unsigned long,
const void *, size_t);
typedef logbuf_t * (*logger_prep_func_t)(void *, unsigned long, logbuf_t *);
typedef void (*logger_except_func_t)(void);
typedef struct logger logger_t;
@ -54,7 +55,7 @@ int logger_join(logger_t *) NONNULL(1);
int logger_stop(logger_t *) NONNULL(1) WUNRES;
int logger_reopen(logger_t *) NONNULL(1) WUNRES;
int logger_open(logger_t *, void *) NONNULL(1,2) WUNRES;
int logger_close(logger_t *, void *) NONNULL(1,2) WUNRES;
int logger_close(logger_t *, void *, unsigned long) NONNULL(1,2) WUNRES;
int logger_submit(logger_t *, void *, unsigned long,
logbuf_t *) NONNULL(1) WUNRES;
int logger_printf(logger_t *, void *, unsigned long,

@ -0,0 +1,843 @@
/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "logpkt.h"
#include "sys.h"
#include "log.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <errno.h>
#ifndef WITHOUT_MIRROR
#include <pcap.h>
#endif /* !WITHOUT_MIRROR */
typedef struct __attribute__((packed)) {
uint32_t magic_number; /* magic number */
uint16_t version_major; /* major version number */
uint16_t version_minor; /* minor version number */
uint32_t thiszone; /* GMT to local correction */
uint32_t sigfigs; /* accuracy of timestamps */
uint32_t snaplen; /* max length of captured packets, in octets */
uint32_t network; /* data link type */
} pcap_file_hdr_t;
typedef struct __attribute__((packed)) {
uint32_t ts_sec; /* timestamp seconds */
uint32_t ts_usec; /* timestamp microseconds */
uint32_t incl_len; /* number of octets of packet saved in file */
uint32_t orig_len; /* actual length of packet */
} pcap_rec_hdr_t;
#define PCAP_MAGIC 0xa1b2c3d4
typedef struct __attribute__((packed)) {
uint8_t dst_mac[ETHER_ADDR_LEN];
uint8_t src_mac[ETHER_ADDR_LEN];
uint16_t ethertype;
} ether_hdr_t;
#ifndef ETHERTYPE_IP
#define ETHERTYPE_IP 0x0800
#endif
#ifndef ETHERTYPE_IPV6
#define ETHERTYPE_IPV6 0x86dd
#endif
typedef struct __attribute__((packed)) {
uint8_t version_ihl;
uint8_t dscp_ecn;
uint16_t len;
uint16_t id;
uint16_t frag;
uint8_t ttl;
uint8_t proto;
uint16_t chksum;
uint32_t src_addr;
uint32_t dst_addr;
} ip4_hdr_t;
typedef struct __attribute__((packed)) {
uint32_t flags;
uint16_t len;
uint8_t next_hdr;
uint8_t hop_limit;
uint8_t src_addr[16];
uint8_t dst_addr[16];
} ip6_hdr_t;
typedef struct __attribute__((packed)) {
uint16_t src_port;
uint16_t dst_port;
uint32_t seq;
uint32_t ack;
uint16_t flags;
uint16_t win;
uint16_t chksum;
uint16_t urgp;
} tcp_hdr_t;
#ifndef TH_FIN
#define TH_FIN 0x01
#endif
#ifndef TH_SYN
#define TH_SYN 0x02
#endif
#ifndef TH_RST
#define TH_RST 0x04
#endif
#ifndef TH_PUSH
#define TH_PUSH 0x08
#endif
#ifndef TH_ACK
#define TH_ACK 0x10
#endif
/*
* *MTU* is the size of the largest layer 3 packet, including IP header.
*
* *MAX_PKTSZ* is the buffer size needed to construct a layer 2 frame
* containing the largest possible layer 3 packet allowed by MTU.
*
* *MSS_IP4* and *MSS_IP6* are the maximum TCP segment sizes that fit into a
* single IPv4 and IPv6 packet, respectively.
*
* The calculations assume no IPv4 options and no IPv6 option headers.
*
* These constants are only used for PCAP writing, not for mirroring.
*/
#define MTU 1500
#define MAX_PKTSZ (MTU + sizeof(ether_hdr_t))
#define MSS_IP4 (MTU - sizeof(ip4_hdr_t) - sizeof(tcp_hdr_t))
#define MSS_IP6 (MTU - sizeof(ip6_hdr_t) - sizeof(tcp_hdr_t))
/*
* IP/TCP checksumming operating on uint32_t intermediate state variable C.
*/
#define CHKSUM_INIT(C) \
{ \
(C) = 0; \
}
#define CHKSUM_ADD_RANGE(C,B,S) \
{ \
uint16_t *p = (uint16_t *)(B); \
size_t words = (S) >> 1; \
while (words--) { \
(C) += *p++; \
} \
if ((S) & 1) { \
(C) += htons(*((char *)p) << 8); \
} \
}
#define CHKSUM_ADD_UINT32(C,U) \
{ \
(C) += ((U) >> 16) + ((U) & 0xFFFF); \
}
#define CHKSUM_ADD_UINT16(C,U) \
{ \
(C) += (U); \
}
#define CHKSUM_FINALIZE(C) \
{ \
(C) = ((C) >> 16) + ((C) & 0xffff); \
(C) += ((C) >> 16); \
(C) = ~(C); \
}
/* Socket address typecasting shorthand notations. */
#define CSA(X) ((const struct sockaddr *)(X))
#define CSIN(X) ((const struct sockaddr_in *)(X))
#define CSIN6(X) ((const struct sockaddr_in6 *)(X))
/*
* Write the PCAP file-level header to file descriptor *fd* open for writing,
* positioned at the beginning of an empty file.
*
* Returns 0 on success and -1 on failure.
*/
static int
logpkt_write_global_pcap_hdr(int fd)
{
pcap_file_hdr_t hdr;
memset(&hdr, 0x0, sizeof(hdr));
hdr.magic_number = PCAP_MAGIC;
hdr.version_major = 2;
hdr.version_minor = 4;
hdr.snaplen = MAX_PKTSZ;
hdr.network = 1;
return write(fd, &hdr, sizeof(hdr)) != sizeof(hdr) ? -1 : 0;
}
/*
* Called on a file descriptor open for reading and writing.
* If the fd points to an empty file, a pcap header is added and 0 is returned.
* If the fd points to a file with PCAP magic bytes, the file position is moved
* to the end of the file and 0 is returned.
* If the fd points to a file without PCAP magic bytes, the file is truncated
* to zero bytes and a new PCAP header is written.
* On a return value of 0, the caller can continue to write PCAP records to the
* file descriptor. On error, -1 is returned and the file descriptor is in an
* undefined but still open state.
*/
int
logpkt_pcap_open_fd(int fd) {
pcap_file_hdr_t hdr;
off_t sz;
ssize_t n;
sz = lseek(fd, 0, SEEK_END);
if (sz == -1)
return -1;
if (sz > 0) {
if (lseek(fd, 0, SEEK_SET) == -1)
return -1;
n = read(fd, &hdr, sizeof(pcap_file_hdr_t));
if (n != sizeof(pcap_file_hdr_t))
return -1;
if (hdr.magic_number == PCAP_MAGIC)
return lseek(fd, 0, SEEK_END) == -1 ? -1 : 0;
if (lseek(fd, 0, SEEK_SET) == -1)
return -1;
if (ftruncate(fd, 0) == -1)
return -1;
}
return logpkt_write_global_pcap_hdr(fd);
}
/*
* Initialize the per-connection packet crafting context. For mirroring,
* *libnet* must be an initialized libnet instance and *mtu* must be the
* target interface MTU greater than 0. For PCAP writing, *libnet* must be
* NULL and *mtu* must be 0. The ether and sockaddr addresses are used as the
* layer 2 and layer 3 addresses respectively. For mirroring, the ethers must
* match the actual link layer addresses to be used when sending traffic, not
* some emulated addresses.
*/
void
logpkt_ctx_init(logpkt_ctx_t *ctx, libnet_t *libnet, size_t mtu,
const uint8_t *src_ether, const uint8_t *dst_ether,
const struct sockaddr *src_addr, socklen_t src_addr_len,
const struct sockaddr *dst_addr, socklen_t dst_addr_len)
{
ctx->libnet = libnet;
memcpy(ctx->src_ether, src_ether, ETHER_ADDR_LEN);
memcpy(ctx->dst_ether, dst_ether, ETHER_ADDR_LEN);
memcpy(&ctx->src_addr, src_addr, src_addr_len);
memcpy(&ctx->dst_addr, dst_addr, dst_addr_len);
ctx->src_seq = 0;
ctx->dst_seq = 0;
if (mtu) {
ctx->mss = mtu - sizeof(tcp_hdr_t)
- (dst_addr->sa_family == AF_INET
? sizeof(ip4_hdr_t)
: sizeof(ip6_hdr_t));
} else {
ctx->mss = dst_addr->sa_family == AF_INET ? MSS_IP4 : MSS_IP6;
}
}
/*
* Write the layer 2 frame contained in *pkt* to file descriptor *fd* already
* open for writing. First writes a PCAP record header, then the actual frame.
*/
static int
logpkt_pcap_write(const uint8_t *pkt, size_t pktsz, int fd)
{
pcap_rec_hdr_t rec_hdr;
struct timeval tv;
gettimeofday(&tv, NULL);
rec_hdr.ts_sec = tv.tv_sec;
rec_hdr.ts_usec = tv.tv_usec;
rec_hdr.orig_len = rec_hdr.incl_len = pktsz;
if (write(fd, &rec_hdr, sizeof(rec_hdr)) != sizeof(rec_hdr)) {
log_err_printf("Error writing pcap record hdr: %s\n",
strerror(errno));
return -1;
}
if (write(fd, pkt, pktsz) != (ssize_t)pktsz) {
log_err_printf("Error writing pcap record: %s\n",
strerror(errno));
return -1;
}
return 0;
}
/*
* Build a frame from the given layer 2, layer 3 and layer 4 parameters plus
* payload, write the resulting bytes into buffer pointed to by *pkt*, and fix
* the checksums on all layers. The receiving buffer must be at least
* MAX_PKTSZ bytes large and payload must be a maximum of MSS_IP4 or MSS_IP6
* respectively. Layer 2 is Ethernet II, layer 3 is IPv4 or IPv6 depending on
* the address family of *dst_addr*, and layer 4 is TCP.
*
* This function is stateless. For header fields that cannot be directly
* derived from the arguments, default values will be used.
*/
static size_t
logpkt_pcap_build(uint8_t *pkt,
uint8_t *src_ether, uint8_t *dst_ether,
const struct sockaddr *src_addr,
const struct sockaddr *dst_addr,
char flags, uint32_t seq, uint32_t ack,
const uint8_t *payload, size_t payloadlen)
{
ether_hdr_t *ether_hdr;
ip4_hdr_t *ip4_hdr;
ip6_hdr_t *ip6_hdr;
tcp_hdr_t *tcp_hdr;
size_t sz;
uint32_t sum;
ether_hdr = (ether_hdr_t *)pkt;
memcpy(ether_hdr->src_mac, src_ether, sizeof(ether_hdr->src_mac));
memcpy(ether_hdr->dst_mac, dst_ether, sizeof(ether_hdr->dst_mac));
sz = sizeof(ether_hdr_t);
if (dst_addr->sa_family == AF_INET) {
ether_hdr->ethertype = htons(ETHERTYPE_IP);
ip4_hdr = (ip4_hdr_t *)(((uint8_t *)ether_hdr) +
sizeof(ether_hdr_t));
ip4_hdr->version_ihl = 0x45;
ip4_hdr->dscp_ecn = 0;
ip4_hdr->len = htons(sizeof(ip4_hdr_t) +
sizeof(tcp_hdr_t) + payloadlen);
ip4_hdr->id = sys_rand16(),
ip4_hdr->frag = 0;
ip4_hdr->ttl = 64;
ip4_hdr->proto = IPPROTO_TCP;
ip4_hdr->src_addr = CSIN(src_addr)->sin_addr.s_addr;
ip4_hdr->dst_addr = CSIN(dst_addr)->sin_addr.s_addr;
ip4_hdr->chksum = 0;
CHKSUM_INIT(sum);
CHKSUM_ADD_RANGE(sum, ip4_hdr, sizeof(ip4_hdr_t));
CHKSUM_FINALIZE(sum);
ip4_hdr->chksum = sum;
sz += sizeof(ip4_hdr_t);
tcp_hdr = (tcp_hdr_t *)(((uint8_t *)ip4_hdr) +
sizeof(ip4_hdr_t));
tcp_hdr->src_port = CSIN(src_addr)->sin_port;
tcp_hdr->dst_port = CSIN(dst_addr)->sin_port;
/* pseudo header */
CHKSUM_INIT(sum);
CHKSUM_ADD_UINT32(sum, ip4_hdr->src_addr);
CHKSUM_ADD_UINT32(sum, ip4_hdr->dst_addr);
CHKSUM_ADD_UINT16(sum, htons(ip4_hdr->proto));
CHKSUM_ADD_UINT16(sum, htons(sizeof(tcp_hdr_t) + payloadlen));
} else {
ether_hdr->ethertype = htons(ETHERTYPE_IPV6);
ip6_hdr = (ip6_hdr_t *)(((uint8_t *)ether_hdr) +
sizeof(ether_hdr_t));
ip6_hdr->flags = htonl(0x60000000UL);
ip6_hdr->len = htons(sizeof(tcp_hdr_t) + payloadlen);
ip6_hdr->next_hdr = IPPROTO_TCP;
ip6_hdr->hop_limit = 255;
memcpy(ip6_hdr->src_addr, CSIN6(src_addr)->sin6_addr.s6_addr,
sizeof(ip6_hdr->src_addr));
memcpy(ip6_hdr->dst_addr, CSIN6(dst_addr)->sin6_addr.s6_addr,
sizeof(ip6_hdr->dst_addr));
sz += sizeof(ip6_hdr_t);
tcp_hdr = (tcp_hdr_t *)(((uint8_t *)ip6_hdr) +
sizeof(ip6_hdr_t));
tcp_hdr->src_port = CSIN6(src_addr)->sin6_port;
tcp_hdr->dst_port = CSIN6(dst_addr)->sin6_port;
/* pseudo header */
CHKSUM_INIT(sum);
CHKSUM_ADD_RANGE(sum, ip6_hdr->src_addr,
sizeof(ip6_hdr->src_addr));
CHKSUM_ADD_RANGE(sum, ip6_hdr->dst_addr,
sizeof(ip6_hdr->dst_addr));
CHKSUM_ADD_UINT32(sum, ip6_hdr->len);
CHKSUM_ADD_UINT16(sum, htons(IPPROTO_TCP));
}
tcp_hdr->seq = htonl(seq);
tcp_hdr->ack = htonl(ack);
tcp_hdr->flags = htons(0x5000|flags);
tcp_hdr->win = htons(32767);
tcp_hdr->urgp = 0;
tcp_hdr->chksum = 0;
sz += sizeof(tcp_hdr_t);
memcpy(((uint8_t *)tcp_hdr) + sizeof(tcp_hdr_t), payload, payloadlen);
CHKSUM_ADD_RANGE(sum, tcp_hdr, sizeof(tcp_hdr_t) + payloadlen);
CHKSUM_FINALIZE(sum);
tcp_hdr->chksum = sum;
return sz + payloadlen;
}
#ifndef WITHOUT_MIRROR
/*
* Build a packet using libnet intended for mirroring mode. The packet will
* be dynamically allocated on the heap by the libnet instance *libnet*.
*/
static int
logpkt_mirror_build(libnet_t *libnet,
uint8_t *src_ether, uint8_t *dst_ether,
const struct sockaddr *src_addr,
const struct sockaddr *dst_addr,
char flags, uint32_t seq, uint32_t ack,
const uint8_t *payload, size_t payloadlen)
{
libnet_ptag_t ptag;
ptag = libnet_build_tcp(src_addr->sa_family == AF_INET
? CSIN(src_addr)->sin_port
: CSIN6(src_addr)->sin6_port,
dst_addr->sa_family == AF_INET
? CSIN(dst_addr)->sin_port
: CSIN6(dst_addr)->sin6_port,
seq,
ack,
flags,
32767, /* window size */
0, /* checksum */
0, /* urgent pointer */
LIBNET_TCP_H + payloadlen,
(uint8_t *)payload, payloadlen,
libnet, 0);
if (ptag == -1) {
log_err_printf("Error building tcp header: %s",
libnet_geterror(libnet));
return -1;
}
if (dst_addr->sa_family == AF_INET) {
ptag = libnet_build_ipv4(LIBNET_IPV4_H + LIBNET_TCP_H +
payloadlen,
0, /* TOS */
(uint16_t)
sys_rand16(), /* id */
0x4000, /* frag */
64, /* TTL */
IPPROTO_TCP, /* protocol */
0, /* checksum */
CSIN(src_addr)->sin_addr.s_addr,
CSIN(dst_addr)->sin_addr.s_addr,
NULL, 0,
libnet, 0);
} else {
ptag = libnet_build_ipv6(0, /* traffic class */
0, /* flow label */
LIBNET_IPV6_H + LIBNET_TCP_H +
payloadlen,
IPPROTO_TCP,
255, /* hop limit */
*(struct libnet_in6_addr *)
&CSIN6(src_addr)->sin6_addr,
*(struct libnet_in6_addr *)
&CSIN6(dst_addr)->sin6_addr,
NULL, 0,
libnet, 0);
}
if (ptag == -1) {
log_err_printf("Error building ip header: %s",
libnet_geterror(libnet));
return -1;
}
ptag = libnet_build_ethernet(dst_ether,
src_ether,
dst_addr->sa_family == AF_INET
? ETHERTYPE_IP : ETHERTYPE_IPV6,
NULL, 0,
libnet, 0);
if (ptag == -1) {
log_err_printf("Error building ethernet header: %s",
libnet_geterror(libnet));
return -1;
}
return 0;
}
#endif /* !WITHOUT_MIRROR */
/*
* Write a single packet to either PCAP (*fd* != -1) or a network interface
* (*fd* == -1). Caller must ensure that *ctx* was initialized accordingly.
* The packet will be in direction *direction*, use TCP flags *flags*, and
* transmit a payload *payload*. TCP sequence and acknowledgment numbers as
* well as source and destination identifiers are taken from *ctx*.
*
* Caller must ensure that *payload* fits into a frame depending on the MTU
* selected (interface in mirroring mode, MTU value in PCAP writing mode).
*/
static int
logpkt_write_packet(logpkt_ctx_t *ctx, int fd, int direction, char flags,
const uint8_t *payload, size_t payloadlen)
{
int rv;
if (fd != -1) {
uint8_t buf[MAX_PKTSZ];
size_t sz;
if (direction == LOGPKT_REQUEST) {
sz = logpkt_pcap_build(buf,
ctx->src_ether, ctx->dst_ether,
CSA(&ctx->src_addr),
CSA(&ctx->dst_addr),
flags,
ctx->src_seq, ctx->dst_seq,
payload, payloadlen);
} else {
sz = logpkt_pcap_build(buf,
ctx->dst_ether, ctx->src_ether,
CSA(&ctx->dst_addr),
CSA(&ctx->src_addr),
flags,
ctx->dst_seq, ctx->src_seq,
payload, payloadlen);
}
rv = logpkt_pcap_write(buf, sz, fd);
if (rv == -1) {
log_err_printf("Error writing packet to PCAP file\n");
return -1;
}
} else {
#ifndef WITHOUT_MIRROR
/* Source and destination ether are determined by the actual
* local MAC address and target MAC address for mirroring the
* packets to; use them as-is for both directions. */
if (direction == LOGPKT_REQUEST) {
rv = logpkt_mirror_build(ctx->libnet,
ctx->src_ether, ctx->dst_ether,
CSA(&ctx->src_addr),
CSA(&ctx->dst_addr),
flags,
ctx->src_seq, ctx->dst_seq,
payload, payloadlen);
} else {
rv = logpkt_mirror_build(ctx->libnet,
ctx->src_ether, ctx->dst_ether,
CSA(&ctx->dst_addr),
CSA(&ctx->src_addr),
flags,
ctx->dst_seq, ctx->src_seq,
payload, payloadlen);
}
if (rv == -1) {
log_err_printf("Error building packet\n");
return -1;
}
rv = libnet_write(ctx->libnet);
if (rv == -1) {
log_err_printf("Error writing packet: %s\n",
libnet_geterror(ctx->libnet));
}
libnet_clear_packet(ctx->libnet);
#else /* WITHOUT_MIRROR */
rv = -1;
#endif /* WITHOUT_MIRROR */
}
return rv;
}
/*
* Emulate the initial SYN handshake.
*/
static int
logpkt_write_syn_handshake(logpkt_ctx_t *ctx, int fd)
{
ctx->src_seq = sys_rand32();
if (logpkt_write_packet(ctx, fd, LOGPKT_REQUEST,
TH_SYN, NULL, 0) == -1)
return -1;
ctx->src_seq += 1;
ctx->dst_seq = sys_rand32();
if (logpkt_write_packet(ctx, fd, LOGPKT_RESPONSE,
TH_SYN|TH_ACK, NULL, 0) == -1)
return -1;
ctx->dst_seq += 1;
if (logpkt_write_packet(ctx, fd, LOGPKT_REQUEST,
TH_ACK, NULL, 0) == -1)
return -1;
return 0;
}
/*
* Emulate the necessary packets to write a single payload segment. If
* necessary, a SYN handshake will automatically be generated before emitting
* the packet carrying the payload plus a matching ACK.
*/
int
logpkt_write_payload(logpkt_ctx_t *ctx, int fd, int direction,
const uint8_t *payload, size_t payloadlen)
{
int other_direction = (direction == LOGPKT_REQUEST) ? LOGPKT_RESPONSE
: LOGPKT_REQUEST;
if (ctx->src_seq == 0) {
if (logpkt_write_syn_handshake(ctx, fd) == -1)
return -1;
}
while (payloadlen > 0) {
size_t n = payloadlen > ctx->mss ? ctx->mss : payloadlen;
if (logpkt_write_packet(ctx, fd, direction,
TH_PUSH|TH_ACK, payload, n) == -1) {
log_err_printf("Warning: Failed to write to pcap log"
": %s\n", strerror(errno));
return -1;
}
if (direction == LOGPKT_REQUEST) {
ctx->src_seq += n;
} else {
ctx->dst_seq += n;
}
payload += n;
payloadlen -= n;
}
if (logpkt_write_packet(ctx, fd, other_direction,
TH_ACK, NULL, 0) == -1) {
log_err_printf("Warning: Failed to write to pcap log: %s\n",
strerror(errno));
return -1;
}
return 0;
}
/*
* Emulate a connection close, emitting a FIN handshake in the correct
* direction. Does not close the file descriptor.
*/
int
logpkt_write_close(logpkt_ctx_t *ctx, int fd, int direction) {
int other_direction = (direction == LOGPKT_REQUEST) ? LOGPKT_RESPONSE
: LOGPKT_REQUEST;
if (ctx->src_seq == 0) {
if (logpkt_write_syn_handshake(ctx, fd) == -1)
return -1;
}
if (logpkt_write_packet(ctx, fd, direction,
TH_FIN|TH_ACK, NULL, 0) == -1) {
log_err_printf("Warning: Failed to write packet\n");
return -1;
}
if (direction == LOGPKT_REQUEST) {
ctx->src_seq += 1;
} else {
ctx->dst_seq += 1;
}
if (logpkt_write_packet(ctx, fd, other_direction,
TH_FIN|TH_ACK, NULL, 0) == -1) {
log_err_printf("Warning: Failed to write packet\n");
return -1;
}
if (other_direction == LOGPKT_REQUEST) {
ctx->src_seq += 1;
} else {
ctx->dst_seq += 1;
}
if (logpkt_write_packet(ctx, fd, direction,
TH_ACK, NULL, 0) == -1) {
log_err_printf("Warning: Failed to write packet\n");
return -1;
}
return 0;
}
#ifndef WITHOUT_MIRROR
typedef struct {
uint32_t ip;
int result;
uint8_t ether[ETHER_ADDR_LEN];
} logpkt_recv_arp_reply_ctx_t;
/*
* Receive a single ARP reply and copy the resulting ether to ctx->ether.
*/
static void
logpkt_recv_arp_reply(uint8_t *user,
UNUSED const struct pcap_pkthdr *h,
const uint8_t *packet)
{
logpkt_recv_arp_reply_ctx_t *ctx = (logpkt_recv_arp_reply_ctx_t*)user;
struct libnet_802_3_hdr *heth = (void*)packet;
struct libnet_arp_hdr *harp = (void*)((char*)heth + LIBNET_ETH_H);
/* skip if wrong protocol */
if (htons(harp->ar_op) != ARPOP_REPLY)
return;
if (htons(harp->ar_pro) != ETHERTYPE_IP)
return;
if (htons(harp->ar_hrd) != ARPHRD_ETHER)
return;
/* skip if wrong target IP address */
if (!!memcmp(&ctx->ip, (char*)harp + harp->ar_hln + LIBNET_ARP_H, 4))
return;
/* skip if source ether mismatch */
if (!!memcmp((u_char*)harp + sizeof(struct libnet_arp_hdr),
heth->_802_3_shost, ETHER_ADDR_LEN))
return;
memcpy(ctx->ether,
(u_char*)harp + sizeof(struct libnet_arp_hdr),
ETHER_ADDR_LEN);
ctx->result = 0;
}
/*
* Look up the appropriate source and destination ethernet addresses for
* mirroring packets to dst_ip_s on interface dst_if_s.
* Only IPv4 mirror targets are supported.
*/
int
logpkt_ether_lookup(libnet_t *libnet,
uint8_t *src_ether, uint8_t *dst_ether,
const char *dst_ip_s, const char *dst_if_s)
{
char errbuf[PCAP_ERRBUF_SIZE];
uint8_t broadcast_ether[ETHER_ADDR_LEN] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
uint8_t zero_ether[ETHER_ADDR_LEN] = {
0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
struct libnet_ether_addr *src_ether_addr;
uint32_t src_ip;
struct bpf_program bp;
int count = 50;
logpkt_recv_arp_reply_ctx_t ctx;
if (sys_get_af(dst_ip_s) != AF_INET) {
log_err_printf("Mirroring target must be an IPv4 address.\n");
return -1;
}
ctx.result = -1;
ctx.ip = libnet_name2addr4(libnet, (char *)dst_ip_s,
LIBNET_DONT_RESOLVE);
if (ctx.ip == (uint32_t)-1) {
log_err_printf("Error converting dst IP address: %s\n",
libnet_geterror(libnet));
goto out;
}
src_ip = libnet_get_ipaddr4(libnet);
if (src_ip == (uint32_t)-1) {
log_err_printf("Error getting src IP address: %s\n",
libnet_geterror(libnet));
goto out;
}
src_ether_addr = libnet_get_hwaddr(libnet);
if (src_ether_addr == NULL) {
log_err_printf("Error getting src ethernet address: %s\n",
libnet_geterror(libnet));
goto out;
}
memcpy(src_ether, src_ether_addr->ether_addr_octet, ETHER_ADDR_LEN);
if (libnet_autobuild_arp(ARPOP_REQUEST,
src_ether,
(uint8_t*)&src_ip,
zero_ether,
(uint8_t*)&ctx.ip,
libnet) == -1) {
log_err_printf("Error building arp header: %s\n",
libnet_geterror(libnet));
goto out;
}
if (libnet_autobuild_ethernet(broadcast_ether,
ETHERTYPE_ARP,
libnet) == -1) {
log_err_printf("Error building ethernet header: %s",
libnet_geterror(libnet));
goto out;
}
pcap_t *pcap = pcap_open_live(dst_if_s, 100, 0, 10, errbuf);
if (pcap == NULL) {
log_err_printf("Error in pcap_open_live(): %s\n", errbuf);
goto out;
}
if (pcap_compile(pcap, &bp, "arp", 0, -1) == -1) {
log_err_printf("Error in pcap_compile(): %s\n",
pcap_geterr(pcap));
goto out2;
}
if (pcap_setfilter(pcap, &bp) == -1) {
log_err_printf("Error in pcap_setfilter(): %s\n",
pcap_geterr(pcap));
goto out3;
}
do {
if (libnet_write(libnet) != -1) {
/* Limit # of packets to process, so we can loop to
* send arp requests on busy networks. */
if (pcap_dispatch(pcap, 1000,
(pcap_handler)logpkt_recv_arp_reply,
(u_char*)&ctx) < 0) {
log_err_printf("Error in pcap_dispatch(): %s\n",
pcap_geterr(pcap));
break;
}
} else {
log_err_printf("Error writing arp packet: %s",
libnet_geterror(libnet));
break;
}
sleep(1);
} while (ctx.result == -1 && --count > 0);
if (ctx.result == 0) {
memcpy(dst_ether, &ctx.ether, ETHER_ADDR_LEN);
log_dbg_printf("Mirror target is up: "
"%02x:%02x:%02x:%02x:%02x:%02x\n",
dst_ether[0], dst_ether[1], dst_ether[2],
dst_ether[3], dst_ether[4], dst_ether[5]);
}
out3:
pcap_freecode(&bp);
out2:
pcap_close(pcap);
out:
libnet_clear_packet(libnet);
return ctx.result;
}
#endif /* !WITHOUT_MIRROR */
/* vim: set noet ft=c: */

@ -0,0 +1,70 @@
/*-
* SSLsplit - transparent SSL/TLS interception
* https://www.roe.ch/SSLsplit
*
* Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef LOGPKT_H
#define LOGPKT_H
#include "attrib.h"
#include <sys/socket.h>
#include <stdint.h>
#include <time.h>
#ifndef WITHOUT_MIRROR
#include <libnet.h>
#else /* WITHOUT_MIRROR */
#define libnet_t void
#define ETHER_ADDR_LEN 6
#endif /* WITHOUT_MIRROR */
typedef struct {
libnet_t *libnet;
uint8_t src_ether[ETHER_ADDR_LEN];
uint8_t dst_ether[ETHER_ADDR_LEN];
struct sockaddr_storage src_addr;
struct sockaddr_storage dst_addr;
uint32_t src_seq;
uint32_t dst_seq;
size_t mss;
} logpkt_ctx_t;
#define LOGPKT_REQUEST 0
#define LOGPKT_RESPONSE 1
int logpkt_pcap_open_fd(int fd) WUNRES;
void logpkt_ctx_init(logpkt_ctx_t *, libnet_t *, size_t,
const uint8_t *, const uint8_t *,
const struct sockaddr *, socklen_t,
const struct sockaddr *, socklen_t);
int logpkt_write_payload(logpkt_ctx_t *, int, int,
const unsigned char *, size_t) WUNRES;
int logpkt_write_close(logpkt_ctx_t *, int, int);
int logpkt_ether_lookup(libnet_t *, uint8_t *, uint8_t *,
const char *, const char *) WUNRES;
#endif /* !LOGPKT_H */

167
main.c

@ -53,10 +53,13 @@
#include <getopt.h>
#endif /* !__BSD__ */
#include <event2/event.h>
#include <openssl/ssl.h>
#include <openssl/x509.h>
#include <event2/event.h>
#ifndef WITHOUT_MIRROR
#include <libnet.h>
#include <pcap.h>
#endif /* !WITHOUT_MIRROR */
#if __APPLE__
#undef daemon
@ -115,6 +118,22 @@ main_version(void)
ssl_openssl_version();
fprintf(stderr, "compiled against libevent %s\n", LIBEVENT_VERSION);
fprintf(stderr, "rtlinked against libevent %s\n", event_get_version());
#ifndef WITHOUT_MIRROR
fprintf(stderr, "compiled against libnet %s\n", LIBNET_VERSION);
#ifndef __OpenBSD__
const char *lnv = libnet_version();
if (!strncmp(lnv, "libnet version ", 15))
lnv += 15;
fprintf(stderr, "rtlinked against libnet %s\n", lnv);
#else /* __OpenBSD__ */
fprintf(stderr, "rtlinked against libnet n/a\n");
#endif /* __OpenBSD__ */
fprintf(stderr, "compiled against libpcap n/a\n");
const char *lpv = pcap_lib_version();
if (!strncmp(lpv, "libpcap version ", 16))
lpv += 16;
fprintf(stderr, "rtlinked against libpcap %s\n", lpv);
#endif /* !WITHOUT_MIRROR */
fprintf(stderr, "%d CPU cores detected\n", sys_get_cpu_cores());
}
@ -125,7 +144,7 @@ static void
main_usage(void)
{
const char *dflt, *warn;
const char *usagefmt =
const char *usagefmt1 =
"Usage: %s [-D] [-f conffile] [-o opt=val] [options...] [proxyspecs...]\n"
" -f conffile use conffile to load configuration from\n"
" -o opt=val override conffile option opt with value val\n"
@ -177,7 +196,7 @@ main_usage(void)
" -j jaildir chroot() to jaildir (impacts sni proxyspecs, see manual page)\n"
" -p pidfile write pid to pidfile (default: no pid file)\n"
" -l logfile connect log: log one line summary per connection to logfile\n"
" -I enable connection statistics logging\n"
" -J enable connection statistics logging\n"
" -L logfile content log: full data to file or named pipe (excludes -S/-F)\n"
" -S logdir content log: full data to separate files in dir (excludes -L/-F)\n"
" -F pathspec content log: full data to sep files with %% subst (excl. -L/-S):\n"
@ -197,17 +216,34 @@ main_usage(void)
" %%%% - literal '%%'\n"
#ifdef HAVE_LOCAL_PROCINFO
" e.g. \"/var/log/sslproxy/%%X/%%u-%%s-%%d-%%T.log\"\n"
#else /* !HAVE_LOCAL_PROCINFO */
" e.g. \"/var/log/sslproxy/%%T-%%s-%%d.log\"\n"
#endif /* HAVE_LOCAL_PROCINFO */
" -X pcapfile pcap log: packets to pcapfile (excludes -Y/-y)\n"
" -Y pcapdir pcap log: packets to separate files in dir (excludes -X/-y)\n"
" -y pathspec pcap log: packets to sep files with %% subst (excl. -X/-Y):\n"
" see option -F for pathspec format\n"
#ifndef WITHOUT_MIRROR
" -I if mirror packets to interface\n"
" -T addr mirror packets to target address (used with -I)\n"
#define OPT_I "I:"
#define OPT_T "T:"
#else /* WITHOUT_MIRROR */
#define OPT_I
#define OPT_T
#endif /* WITHOUT_MIRROR */
" -M logfile log master keys to logfile in SSLKEYLOGFILE format\n"
#ifdef HAVE_LOCAL_PROCINFO
" -i look up local process owning each connection for logging\n"
#define OPT_i "i"
#else /* !HAVE_LOCAL_PROCINFO */
" e.g. \"/var/log/sslproxy/%%T-%%s-%%d.log\"\n"
#define OPT_i
#endif /* HAVE_LOCAL_PROCINFO */
" -M logfile log master keys to logfile in SSLKEYLOGFILE format\n"
" -d daemon mode: run in background, log error messages to syslog\n"
" -D debug mode: run in foreground, log debug messages on stderr\n"
" -V print version information and exit\n"
" -h print usage information and exit\n"
" -h print usage information and exit\n";
const char *usagefmt2 =
" proxyspec = type listenaddr+port \"up:\"port\n"
" [natengine|targetaddr+port|\"sni\"+port]\n"
" e.g. http 0.0.0.0 8080 up:8080 roe.ch 80 # http/4; static\n"
@ -233,7 +269,8 @@ main_usage(void)
warn = "";
}
fprintf(stderr, usagefmt, build_pkgname, dflt, build_pkgname, warn);
fprintf(stderr, usagefmt1, build_pkgname, dflt);
fprintf(stderr, usagefmt2, build_pkgname, warn);
}
/*
@ -313,9 +350,10 @@ main(int argc, char *argv[])
natengine = NULL;
}
while ((ch = getopt(argc, argv, OPT_g OPT_G OPT_Z OPT_i OPT_x
while ((ch = getopt(argc, argv,
OPT_g OPT_G OPT_Z OPT_i OPT_x OPT_T OPT_I
"k:c:C:K:t:OPa:b:s:r:R:e:Eu:m:j:p:l:L:S:F:M:"
"dD::VhW:w:q:f:o:I")) != -1) {
"dD::VhW:w:q:f:o:X:Y:y:J")) != -1) {
switch (ch) {
case 'f':
if (opts->conffile)
@ -326,7 +364,9 @@ main(int argc, char *argv[])
if (load_conffile(opts, argv0, &natengine) == -1) {
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
fprintf(stderr, "Conf file: %s\n", opts->conffile);
#endif /* DEBUG_OPTS */
break;
case 'o':
if (opts_set_option(opts, argv0, optarg, &natengine) == -1) {
@ -418,7 +458,7 @@ main(int argc, char *argv[])
case 'l':
opts_set_connectlog(opts, argv0, optarg);
break;
case 'I':
case 'J':
opts_set_statslog(opts);
break;
case 'L':
@ -430,6 +470,23 @@ main(int argc, char *argv[])
case 'F':
opts_set_contentlogpathspec(opts, argv0, optarg);
break;
case 'X':
opts_set_pcaplog(opts, argv0, optarg);
break;
case 'Y':
opts_set_pcaplogdir(opts, argv0, optarg);
break;
case 'y':
opts_set_pcaplogpathspec(opts, argv0, optarg);
break;
#ifndef WITHOUT_MIRROR
case 'I':
opts_set_mirrorif(opts, argv0, optarg);
break;
case 'T':
opts_set_mirrortarget(opts, argv0, optarg);
break;
#endif /* !WITHOUT_MIRROR */
case 'W':
opts_set_certgendir_writeall(opts, argv0, optarg);
break;
@ -469,13 +526,23 @@ main(int argc, char *argv[])
argc -= optind;
argv += optind;
proxyspec_parse(&argc, &argv, natengine, &opts->spec);
/* usage checks before defaults */
if (opts->detach && OPTS_DEBUG(opts)) {
fprintf(stderr, "%s: -d and -D are mutually exclusive.\n",
argv0);
exit(EXIT_FAILURE);
}
#ifndef WITHOUT_MIRROR
if (opts->mirrortarget && !opts->mirrorif) {
fprintf(stderr, "%s: -T depends on -I.\n", argv0);
exit(EXIT_FAILURE);
}
if (opts->mirrorif && !opts->mirrortarget) {
fprintf(stderr, "%s: -I depends on -T.\n", argv0);
exit(EXIT_FAILURE);
}
#endif /* !WITHOUT_MIRROR */
if (!opts->spec) {
fprintf(stderr, "%s: no proxyspec specified.\n", argv0);
exit(EXIT_FAILURE);
@ -572,13 +639,61 @@ main(int argc, char *argv[])
}
#endif /* __APPLE__ */
}
if (opts->dropuser && sys_isgeteuid(opts->dropuser)) {
if (opts->dropgroup) {
fprintf(stderr, "%s: cannot use -m when -u is "
"current user\n", argv0);
exit(EXIT_FAILURE);
}
free(opts->dropuser);
opts->dropuser = NULL;
}
/* usage checks after defaults */
if (opts->dropgroup && !opts->dropuser) {
fprintf(stderr, "%s: -m depends on -u.\n", argv0);
fprintf(stderr, "%s: -m depends on -u\n", argv0);
exit(EXIT_FAILURE);
}
/* Warn about options that require per-connection privileged operations
* to be executed through privsep, but only if dropuser is set and is
* not root, because privsep will fastpath in that situation, skipping
* the latency-incurring overhead. */
int privsep_warn = 0;
if (opts->dropuser) {
if (opts->contentlog_isdir) {
log_dbg_printf("| Warning: -F requires a privileged "
"operation for each connection!\n");
privsep_warn = 1;
}
if (opts->contentlog_isspec) {
log_dbg_printf("| Warning: -S requires a privileged "
"operation for each connection!\n");
privsep_warn = 1;
}
if (opts->pcaplog_isdir) {
log_dbg_printf("| Warning: -Y requires a privileged "
"operation for each connection!\n");
privsep_warn = 1;
}
if (opts->pcaplog_isspec) {
log_dbg_printf("| Warning: -y requires a privileged "
"operation for each connection!\n");
privsep_warn = 1;
}
if (opts->certgendir) {
log_dbg_printf("| Warning: -w/-W require a privileged "
"op for each connection!\n");
privsep_warn = 1;
}
}
if (privsep_warn) {
log_dbg_printf("| Privileged operations require communication "
"between parent and child process\n"
"| and will negatively impact latency and "
"performance on each connection.\n");
}
/* debug log, part 1 */
if (OPTS_DEBUG(opts)) {
main_version();
@ -586,15 +701,7 @@ main(int argc, char *argv[])
/* generate leaf key */
if (opts_has_ssl_spec(opts) && opts->cakey && !opts->key) {
/*
* While browsers still generally accept it, use a leaf key
* size of 1024 bit for leaf keys. When browsers start to
* sunset 1024 bit RSA in leaf keys, we will need to make this
* value bigger, and/or configurable. Until then, users who
* want a different size can always use their own pre-generated
* leaf key instead of generating one.
*/
opts->key = ssl_key_genrsa(1024);
opts->key = ssl_key_genrsa(DFLT_LEAFKEY_RSABITS);
if (!opts->key) {
fprintf(stderr, "%s: error generating RSA key:\n",
argv0);
@ -722,13 +829,15 @@ main(int argc, char *argv[])
descriptor_table_size = getdtablesize();
/* Fork into parent monitor process and (potentially unprivileged)
* child process doing the actual work. We request 3 privsep client
* sockets: content logger thread, cert writer thread, and the child
* process main thread (main proxy thread) */
int clisock[3];
if (privsep_fork(opts, clisock, 3) != 0) {
* child process doing the actual work. We request 6 privsep client
* sockets: five logger threads, and the child process main thread,
* which will become the main proxy thread. First slot is main thread,
* remaining slots are passed down to log subsystem. */
int clisock[6];
if (privsep_fork(opts, clisock,
sizeof(clisock)/sizeof(clisock[0])) != 0) {
/* parent has exited the monitor loop after waiting for child,
* or an error occured */
* or an error occurred */
if (opts->pidfile) {
sys_pidf_close(pidfd, opts->pidfile);
}
@ -764,7 +873,7 @@ main(int argc, char *argv[])
}
/* Post-privdrop/chroot/detach initialization, thread spawning */
if (log_init(opts, proxy, clisock[1], clisock[2]) == -1) {
if (log_init(opts, proxy, &clisock[1]) == -1) {
fprintf(stderr, "%s: failed to init log facility: %s\n",
argv0, strerror(errno));
goto out_log_failed;

@ -67,6 +67,7 @@ main_suite(void)
Suite * opts_suite(void);
Suite * dynbuf_suite(void);
Suite * logbuf_suite(void);
Suite * cert_suite(void);
Suite * cachemgr_suite(void);
Suite * cachefkcrt_suite(void);
@ -91,6 +92,7 @@ main(UNUSED int argc, UNUSED char *argv[])
srunner_add_suite(sr, main_suite());
srunner_add_suite(sr, opts_suite());
srunner_add_suite(sr, dynbuf_suite());
srunner_add_suite(sr, logbuf_suite());
srunner_add_suite(sr, cert_suite());
srunner_add_suite(sr, cachemgr_suite());
srunner_add_suite(sr, cachefkcrt_suite());

@ -73,7 +73,7 @@
/*
* Access NAT state tables in a NAT engine independant way.
* Access NAT state tables in a NAT engine independent way.
* Adding support for additional NAT engines should require only
* changes in this file.
*/

402
opts.c

@ -147,6 +147,20 @@ opts_free(opts_t *opts)
if (opts->masterkeylog) {
free(opts->masterkeylog);
}
if (opts->pcaplog) {
free(opts->pcaplog);
}
if (opts->pcaplog_basedir) {
free(opts->pcaplog_basedir);
}
#ifndef WITHOUT_MIRROR
if (opts->mirrorif) {
free(opts->mirrorif);
}
if (opts->mirrortarget) {
free(opts->mirrortarget);
}
#endif /* !WITHOUT_MIRROR */
memset(opts, 0, sizeof(opts_t));
free(opts);
}
@ -251,7 +265,8 @@ opts_proto_dbg_dump(opts_t *opts)
* Parse proxyspecs using a simple state machine.
*/
void
proxyspec_parse(int *argc, char **argv[], const char *natengine, proxyspec_t **opts_spec)
proxyspec_parse(int *argc, char **argv[], const char *natengine,
proxyspec_t **opts_spec)
{
proxyspec_t *spec = NULL;
char *addr = NULL;
@ -540,12 +555,10 @@ opts_set_cacrt(opts_t *opts, const char *argv0, const char *optarg)
X509_free(opts->cacrt);
opts->cacrt = ssl_x509_load(optarg);
if (!opts->cacrt) {
fprintf(stderr, "%s: error loading CA "
"cert from '%s':\n",
argv0, optarg);
fprintf(stderr, "%s: error loading CA cert from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
@ -561,7 +574,9 @@ opts_set_cacrt(opts_t *opts, const char *argv0, const char *optarg)
opts->dh = ssl_dh_load(optarg);
}
#endif /* !OPENSSL_NO_DH */
#ifdef DEBUG_OPTS
log_dbg_printf("CACert: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
@ -571,12 +586,10 @@ opts_set_cakey(opts_t *opts, const char *argv0, const char *optarg)
EVP_PKEY_free(opts->cakey);
opts->cakey = ssl_key_load(optarg);
if (!opts->cakey) {
fprintf(stderr, "%s: error loading CA "
"key from '%s':\n",
argv0, optarg);
fprintf(stderr, "%s: error loading CA key from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
@ -585,10 +598,8 @@ opts_set_cakey(opts_t *opts, const char *argv0, const char *optarg)
if (!opts->cacrt) {
opts->cacrt = ssl_x509_load(optarg);
if (opts->cacrt) {
ssl_x509_refcount_inc(
opts->cacrt);
sk_X509_insert(opts->chain,
opts->cacrt, 0);
ssl_x509_refcount_inc(opts->cacrt);
sk_X509_insert(opts->chain, opts->cacrt, 0);
}
}
#ifndef OPENSSL_NO_DH
@ -596,26 +607,27 @@ opts_set_cakey(opts_t *opts, const char *argv0, const char *optarg)
opts->dh = ssl_dh_load(optarg);
}
#endif /* !OPENSSL_NO_DH */
#ifdef DEBUG_OPTS
log_dbg_printf("CAKey: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
opts_set_chain(opts_t *opts, const char *argv0, const char *optarg)
{
if (ssl_x509chain_load(NULL, &opts->chain,
optarg) == -1) {
fprintf(stderr, "%s: error loading "
"chain from '%s':\n",
argv0, optarg);
if (ssl_x509chain_load(NULL, &opts->chain, optarg) == -1) {
fprintf(stderr, "%s: error loading chain from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("CAChain: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
@ -625,12 +637,10 @@ opts_set_key(opts_t *opts, const char *argv0, const char *optarg)
EVP_PKEY_free(opts->key);
opts->key = ssl_key_load(optarg);
if (!opts->key) {
fprintf(stderr, "%s: error loading lea"
"f key from '%s':\n",
argv0, optarg);
fprintf(stderr, "%s: error loading leaf key from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
@ -641,7 +651,9 @@ opts_set_key(opts_t *opts, const char *argv0, const char *optarg)
opts->dh = ssl_dh_load(optarg);
}
#endif /* !OPENSSL_NO_DH */
#ifdef DEBUG_OPTS
log_dbg_printf("LeafCerts: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
@ -650,16 +662,17 @@ opts_set_crl(opts_t *opts, const char *optarg)
if (opts->crlurl)
free(opts->crlurl);
opts->crlurl = strdup(optarg);
#ifdef DEBUG_OPTS
log_dbg_printf("CRL: %s\n", opts->crlurl);
#endif /* DEBUG_OPTS */
}
void
opts_set_tgcrtdir(opts_t *opts, const char *argv0, const char *optarg)
{
if (!sys_isdir(optarg)) {
fprintf(stderr, "%s: '%s' is not a "
"directory\n",
argv0, optarg);
fprintf(stderr, "%s: '%s' is not a directory\n",
argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->tgcrtdir)
@ -667,7 +680,9 @@ opts_set_tgcrtdir(opts_t *opts, const char *argv0, const char *optarg)
opts->tgcrtdir = strdup(optarg);
if (!opts->tgcrtdir)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("TargetCertDir: %s\n", opts->tgcrtdir);
#endif /* DEBUG_OPTS */
}
static void
@ -681,21 +696,27 @@ set_certgendir(opts_t *opts, const char *argv0, const char *optarg)
}
void
opts_set_certgendir_writegencerts(opts_t *opts, const char *argv0, const char *optarg)
opts_set_certgendir_writegencerts(opts_t *opts, const char *argv0,
const char *optarg)
{
opts->certgen_writeall = 0;
set_certgendir(opts, argv0, optarg);
#ifdef DEBUG_OPTS
log_dbg_printf("WriteGenCertsDir: certgendir=%s, writeall=%u\n",
opts->certgendir, opts->certgen_writeall);
#endif /* DEBUG_OPTS */
}
void
opts_set_certgendir_writeall(opts_t *opts, const char *argv0, const char *optarg)
opts_set_certgendir_writeall(opts_t *opts, const char *argv0,
const char *optarg)
{
opts->certgen_writeall = 1;
set_certgendir(opts, argv0, optarg);
#ifdef DEBUG_OPTS
log_dbg_printf("WriteAllCertsDir: certgendir=%s, writeall=%u\n",
opts->certgendir, opts->certgen_writeall);
#endif /* DEBUG_OPTS */
}
void
@ -729,18 +750,18 @@ opts_set_clientcrt(opts_t *opts, const char *argv0, const char *optarg)
X509_free(opts->clientcrt);
opts->clientcrt = ssl_x509_load(optarg);
if (!opts->clientcrt) {
fprintf(stderr, "%s: error loading cli"
"ent cert from '%s':\n",
argv0, optarg);
fprintf(stderr, "%s: error loading client cert from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("ClientCert: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
@ -750,18 +771,18 @@ opts_set_clientkey(opts_t *opts, const char *argv0, const char *optarg)
EVP_PKEY_free(opts->clientkey);
opts->clientkey = ssl_key_load(optarg);
if (!opts->clientkey) {
fprintf(stderr, "%s: error loading cli"
"ent key from '%s':\n",
argv0, optarg);
fprintf(stderr, "%s: error loading client key from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("ClientKey: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
#ifndef OPENSSL_NO_DH
@ -772,18 +793,18 @@ opts_set_dh(opts_t *opts, const char *argv0, const char *optarg)
DH_free(opts->dh);
opts->dh = ssl_dh_load(optarg);
if (!opts->dh) {
fprintf(stderr, "%s: error loading DH "
"params from '%s':\n",
argv0, optarg);
fprintf(stderr, "%s: error loading DH params from '%s':\n",
argv0, optarg);
if (errno) {
fprintf(stderr, "%s\n",
strerror(errno));
fprintf(stderr, "%s\n", strerror(errno));
} else {
ERR_print_errors_fp(stderr);
}
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("DHGroupParams: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
#endif /* !OPENSSL_NO_DH */
@ -795,16 +816,16 @@ opts_set_ecdhcurve(opts_t *opts, const char *argv0, const char *optarg)
if (opts->ecdhcurve)
free(opts->ecdhcurve);
if (!(ec = ssl_ec_by_name(optarg))) {
fprintf(stderr, "%s: unknown curve "
"'%s'\n",
argv0, optarg);
fprintf(stderr, "%s: unknown curve '%s'\n", argv0, optarg);
exit(EXIT_FAILURE);
}
EC_KEY_free(ec);
opts->ecdhcurve = strdup(optarg);
if (!opts->ecdhcurve)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("ECDHCurve: %s\n", opts->ecdhcurve);
#endif /* DEBUG_OPTS */
}
#endif /* !OPENSSL_NO_ECDH */
@ -828,7 +849,9 @@ opts_set_ciphers(opts_t *opts, const char *argv0, const char *optarg)
opts->ciphers = strdup(optarg);
if (!opts->ciphers)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("Ciphers: %s\n", opts->ciphers);
#endif /* DEBUG_OPTS */
}
#ifndef OPENSSL_NO_ENGINE
@ -840,7 +863,9 @@ opts_set_openssl_engine(opts_t *opts, const char *argv0, const char *optarg)
opts->openssl_engine = strdup(optarg);
if (!opts->openssl_engine)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("OpenSSLEngine: %s\n", opts->openssl_engine);
#endif /* DEBUG_OPTS */
}
#endif /* !OPENSSL_NO_ENGINE */
@ -918,7 +943,9 @@ opts_force_proto(opts_t *opts, const char *argv0, const char *optarg)
argv0, optarg);
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("ForceSSLProto: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
/*
@ -958,16 +985,17 @@ opts_disable_proto(opts_t *opts, const char *argv0, const char *optarg)
argv0, optarg);
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("DisableSSLProto: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
opts_set_user(opts_t *opts, const char *argv0, const char *optarg)
{
if (!sys_isuser(optarg)) {
fprintf(stderr, "%s: '%s' is not an "
"existing user\n",
argv0, optarg);
fprintf(stderr, "%s: '%s' is not an existing user\n",
argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->dropuser)
@ -975,7 +1003,9 @@ opts_set_user(opts_t *opts, const char *argv0, const char *optarg)
opts->dropuser = strdup(optarg);
if (!opts->dropuser)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("User: %s\n", opts->dropuser);
#endif /* DEBUG_OPTS */
}
void
@ -983,9 +1013,8 @@ opts_set_group(opts_t *opts, const char *argv0, const char *optarg)
{
if (!sys_isgroup(optarg)) {
fprintf(stderr, "%s: '%s' is not an "
"existing group\n",
argv0, optarg);
fprintf(stderr, "%s: '%s' is not an existing group\n",
argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->dropgroup)
@ -993,30 +1022,29 @@ opts_set_group(opts_t *opts, const char *argv0, const char *optarg)
opts->dropgroup = strdup(optarg);
if (!opts->dropgroup)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("Group: %s\n", opts->dropgroup);
#endif /* DEBUG_OPTS */
}
void
opts_set_jaildir(opts_t *opts, const char *argv0, const char *optarg)
{
if (!sys_isdir(optarg)) {
fprintf(stderr, "%s: '%s' is not a "
"directory\n",
argv0, optarg);
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->jaildir)
free(opts->jaildir);
opts->jaildir = realpath(optarg, NULL);
if (!opts->jaildir) {
fprintf(stderr, "%s: Failed to "
"canonicalize '%s': "
"%s (%i)\n",
argv0, optarg,
strerror(errno), errno);
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
argv0, optarg, strerror(errno), errno);
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("Chroot: %s\n", opts->jaildir);
#endif /* DEBUG_OPTS */
}
void
@ -1027,7 +1055,9 @@ opts_set_pidfile(opts_t *opts, const char *argv0, const char *optarg)
opts->pidfile = strdup(optarg);
if (!opts->pidfile)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("PidFile: %s\n", opts->pidfile);
#endif /* DEBUG_OPTS */
}
void
@ -1035,10 +1065,20 @@ opts_set_connectlog(opts_t *opts, const char *argv0, const char *optarg)
{
if (opts->connectlog)
free(opts->connectlog);
opts->connectlog = strdup(optarg);
if (!opts->connectlog)
oom_die(argv0);
if (!(opts->connectlog = sys_realdir(optarg))) {
if (errno == ENOENT) {
fprintf(stderr, "Directory part of '%s' does not "
"exist\n", optarg);
exit(EXIT_FAILURE);
} else {
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
optarg, strerror(errno), errno);
oom_die(argv0);
}
}
#ifdef DEBUG_OPTS
log_dbg_printf("ConnectLog: %s\n", opts->connectlog);
#endif /* DEBUG_OPTS */
}
void
@ -1046,41 +1086,49 @@ opts_set_contentlog(opts_t *opts, const char *argv0, const char *optarg)
{
if (opts->contentlog)
free(opts->contentlog);
opts->contentlog = strdup(optarg);
if (!opts->contentlog)
oom_die(argv0);
if (!(opts->contentlog = sys_realdir(optarg))) {
if (errno == ENOENT) {
fprintf(stderr, "Directory part of '%s' does not "
"exist\n", optarg);
exit(EXIT_FAILURE);
} else {
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
optarg, strerror(errno), errno);
oom_die(argv0);
}
}
opts->contentlog_isdir = 0;
opts->contentlog_isspec = 0;
#ifdef DEBUG_OPTS
log_dbg_printf("ContentLog: %s\n", opts->contentlog);
#endif /* DEBUG_OPTS */
}
void
opts_set_contentlogdir(opts_t *opts, const char *argv0, const char *optarg)
{
if (!sys_isdir(optarg)) {
fprintf(stderr, "%s: '%s' is not a "
"directory\n",
argv0, optarg);
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->contentlog)
free(opts->contentlog);
opts->contentlog = realpath(optarg, NULL);
if (!opts->contentlog) {
fprintf(stderr, "%s: Failed to "
"canonicalize '%s': "
"%s (%i)\n",
argv0, optarg,
strerror(errno), errno);
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
argv0, optarg, strerror(errno), errno);
exit(EXIT_FAILURE);
}
opts->contentlog_isdir = 1;
opts->contentlog_isspec = 0;
#ifdef DEBUG_OPTS
log_dbg_printf("ContentLogDir: %s\n", opts->contentlog);
#endif /* DEBUG_OPTS */
}
static void
opts_set_logbasedir(const char *argv0, const char *optarg, char **basedir, char **log)
opts_set_logbasedir(const char *argv0, const char *optarg,
char **basedir, char **log)
{
char *lhs, *rhs, *p, *q;
size_t n;
@ -1088,13 +1136,10 @@ opts_set_logbasedir(const char *argv0, const char *optarg, char **basedir, char
free(*basedir);
if (*log)
free(*log);
if (log_content_split_pathspec(optarg, &lhs,
&rhs) == -1) {
fprintf(stderr, "%s: Failed to split "
"'%s' in lhs/rhs: "
"%s (%i)\n",
argv0, optarg,
strerror(errno), errno);
if (log_content_split_pathspec(optarg, &lhs, &rhs) == -1) {
fprintf(stderr, "%s: Failed to split '%s' in lhs/rhs:"
" %s (%i)\n", argv0, optarg,
strerror(errno), errno);
exit(EXIT_FAILURE);
}
/* eliminate %% from lhs */
@ -1107,19 +1152,14 @@ opts_set_logbasedir(const char *argv0, const char *optarg, char **basedir, char
*q = '\0';
/* all %% in lhs resolved to % */
if (sys_mkpath(lhs, 0777) == -1) {
fprintf(stderr, "%s: Failed to create "
"'%s': %s (%i)\n",
argv0, lhs,
strerror(errno), errno);
fprintf(stderr, "%s: Failed to create '%s': %s (%i)\n",
argv0, lhs, strerror(errno), errno);
exit(EXIT_FAILURE);
}
*basedir = realpath(lhs, NULL);
if (!*basedir) {
fprintf(stderr, "%s: Failed to "
"canonicalize '%s': "
"%s (%i)\n",
argv0, lhs,
strerror(errno), errno);
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
argv0, lhs, strerror(errno), errno);
exit(EXIT_FAILURE);
}
/* count '%' in basedir */
@ -1143,8 +1183,7 @@ opts_set_logbasedir(const char *argv0, const char *optarg, char **basedir, char
}
*q = '\0';
/* lhs contains encoded realpathed basedir */
if (asprintf(log,
"%s/%s", lhs, rhs) < 0)
if (asprintf(log, "%s/%s", lhs, rhs) < 0)
oom_die(argv0);
free(lhs);
free(rhs);
@ -1153,11 +1192,14 @@ opts_set_logbasedir(const char *argv0, const char *optarg, char **basedir, char
void
opts_set_contentlogpathspec(opts_t *opts, const char *argv0, const char *optarg)
{
opts_set_logbasedir(argv0, optarg, &opts->contentlog_basedir, &opts->contentlog);
opts_set_logbasedir(argv0, optarg, &opts->contentlog_basedir,
&opts->contentlog);
opts->contentlog_isdir = 0;
opts->contentlog_isspec = 1;
#ifdef DEBUG_OPTS
log_dbg_printf("ContentLogPathSpec: basedir=%s, %s\n",
opts->contentlog_basedir, opts->contentlog);
#endif /* DEBUG_OPTS */
}
#ifdef HAVE_LOCAL_PROCINFO
@ -1179,11 +1221,107 @@ opts_set_masterkeylog(opts_t *opts, const char *argv0, const char *optarg)
{
if (opts->masterkeylog)
free(opts->masterkeylog);
opts->masterkeylog = strdup(optarg);
if (!opts->masterkeylog)
oom_die(argv0);
if (!(opts->masterkeylog = sys_realdir(optarg))) {
if (errno == ENOENT) {
fprintf(stderr, "Directory part of '%s' does not "
"exist\n", optarg);
exit(EXIT_FAILURE);
} else {
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
optarg, strerror(errno), errno);
oom_die(argv0);
}
}
#ifdef DEBUG_OPTS
log_dbg_printf("MasterKeyLog: %s\n", opts->masterkeylog);
#endif /* DEBUG_OPTS */
}
void
opts_set_pcaplog(opts_t *opts, const char *argv0, const char *optarg)
{
if (opts->pcaplog)
free(opts->pcaplog);
if (!(opts->pcaplog = sys_realdir(optarg))) {
if (errno == ENOENT) {
fprintf(stderr, "Directory part of '%s' does not "
"exist\n", optarg);
exit(EXIT_FAILURE);
} else {
fprintf(stderr, "Failed to realpath '%s': %s (%i)\n",
optarg, strerror(errno), errno);
oom_die(argv0);
}
}
opts->pcaplog_isdir = 0;
opts->pcaplog_isspec = 0;
#ifdef DEBUG_OPTS
log_dbg_printf("PcapLog: %s\n", opts->pcaplog);
#endif /* DEBUG_OPTS */
}
void
opts_set_pcaplogdir(opts_t *opts, const char *argv0, const char *optarg)
{
if (!sys_isdir(optarg)) {
fprintf(stderr, "%s: '%s' is not a directory\n", argv0, optarg);
exit(EXIT_FAILURE);
}
if (opts->pcaplog)
free(opts->pcaplog);
opts->pcaplog = realpath(optarg, NULL);
if (!opts->pcaplog) {
fprintf(stderr, "%s: Failed to realpath '%s': %s (%i)\n",
argv0, optarg, strerror(errno), errno);
exit(EXIT_FAILURE);
}
opts->pcaplog_isdir = 1;
opts->pcaplog_isspec = 0;
#ifdef DEBUG_OPTS
log_dbg_printf("PcapLogDir: %s\n", opts->pcaplog);
#endif /* DEBUG_OPTS */
}
void
opts_set_pcaplogpathspec(opts_t *opts, const char *argv0, const char *optarg)
{
opts_set_logbasedir(argv0, optarg, &opts->pcaplog_basedir,
&opts->pcaplog);
opts->pcaplog_isdir = 0;
opts->pcaplog_isspec = 1;
#ifdef DEBUG_OPTS
log_dbg_printf("PcapLogPathSpec: basedir=%s, %s\n",
opts->pcaplog_basedir, opts->pcaplog);
#endif /* DEBUG_OPTS */
}
#ifndef WITHOUT_MIRROR
void
opts_set_mirrorif(opts_t *opts, const char *argv0, const char *optarg)
{
if (opts->mirrorif)
free(opts->mirrorif);
opts->mirrorif = strdup(optarg);
if (!opts->mirrorif)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("MirrorIf: %s\n", opts->mirrorif);
#endif /* DEBUG_OPTS */
}
void
opts_set_mirrortarget(opts_t *opts, const char *argv0, const char *optarg)
{
if (opts->mirrortarget)
free(opts->mirrortarget);
opts->mirrortarget = strdup(optarg);
if (!opts->mirrortarget)
oom_die(argv0);
#ifdef DEBUG_OPTS
log_dbg_printf("MirrorTarget: %s\n", opts->mirrortarget);
#endif /* DEBUG_OPTS */
}
#endif /* !WITHOUT_MIRROR */
void
opts_set_daemon(opts_t *opts)
@ -1225,7 +1363,9 @@ opts_set_debug_level(const char *optarg)
fprintf(stderr, "Invalid DebugLevel '%s', use 2-4\n", optarg);
exit(EXIT_FAILURE);
}
#ifdef DEBUG_OPTS
log_dbg_printf("DebugLevel: %s\n", optarg);
#endif /* DEBUG_OPTS */
}
void
@ -1304,7 +1444,8 @@ check_value_yesno(const char *value, const char *name, int line_num)
#define MAX_TOKEN 10
static int
set_option(opts_t *opts, const char *argv0, const char *name, char *value, char **natengine, int line_num)
set_option(opts_t *opts, const char *argv0,
const char *name, char *value, char **natengine, int line_num)
{
int yes;
int retval = -1;
@ -1336,14 +1477,18 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
goto leave;
}
yes ? opts_set_deny_ocsp(opts) : opts_unset_deny_ocsp(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("DenyOCSP: %u\n", opts->deny_ocsp);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "Passthrough", 12)) {
yes = check_value_yesno(value, "Passthrough", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_passthrough(opts) : opts_unset_passthrough(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("Passthrough: %u\n", opts->passthrough);
#endif /* DEBUG_OPTS */
#ifndef OPENSSL_NO_DH
} else if (!strncmp(name, "DHGroupParams", 14)) {
opts_set_dh(opts, argv0, value);
@ -1359,7 +1504,9 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
goto leave;
}
yes ? opts_set_sslcomp(opts) : opts_unset_sslcomp(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("SSLCompression: %u\n", opts->sslcomp);
#endif /* DEBUG_OPTS */
#endif /* SSL_OP_NO_COMPRESSION */
} else if (!strncmp(name, "ForceSSLProto", 14)) {
opts_force_proto(opts, argv0, value);
@ -1377,7 +1524,9 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
*natengine = strdup(value);
if (!*natengine)
goto leave;
#ifdef DEBUG_OPTS
log_dbg_printf("NATEngine: %s\n", *natengine);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "User", 5)) {
opts_set_user(opts, argv0, value);
} else if (!strncmp(name, "Group", 6)) {
@ -1401,24 +1550,42 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
goto leave;
}
yes ? opts_set_lprocinfo(opts) : opts_unset_lprocinfo(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("LogProcInfo: %u\n", opts->lprocinfo);
#endif /* DEBUG_OPTS */
#endif /* HAVE_LOCAL_PROCINFO */
} else if (!strncmp(name, "MasterKeyLog", 13)) {
opts_set_masterkeylog(opts, argv0, value);
} else if (!strncmp(name, "PcapLog", 8)) {
opts_set_pcaplog(opts, argv0, value);
} else if (!strncmp(name, "PcapLogDir", 11)) {
opts_set_pcaplogdir(opts, argv0, value);
} else if (!strncmp(name, "PcapLogPathSpec", 16)) {
opts_set_pcaplogpathspec(opts, argv0, value);
#ifndef WITHOUT_MIRROR
} else if (!strncmp(name, "MirrorIf", 9)) {
opts_set_mirrorif(opts, argv0, value);
} else if (!strncmp(name, "MirrorTarget", 13)) {
opts_set_mirrortarget(opts, argv0, value);
#endif /* !WITHOUT_MIRROR */
} else if (!strncmp(name, "Daemon", 7)) {
yes = check_value_yesno(value, "Daemon", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_daemon(opts) : opts_unset_daemon(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("Daemon: %u\n", opts->detach);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "Debug", 6)) {
yes = check_value_yesno(value, "Debug", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_debug(opts) : opts_unset_debug(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("Debug: %u\n", opts->debug);
#endif /* DEBUG_OPTS */
} else if (!strncmp(name, "DebugLevel", 11)) {
opts_set_debug_level(value);
} else if (!strncmp(name, "ProxySpec", 10)) {
@ -1428,7 +1595,9 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
int argc = 0;
char *p, *last = NULL;
for ((p = strtok_r(value, " ", &last)); p; (p = strtok_r(NULL, " ", &last))) {
for ((p = strtok_r(value, " ", &last));
p;
(p = strtok_r(NULL, " ", &last))) {
/* Limit max # token */
if (argc < MAX_TOKEN) {
argv[argc++] = p;
@ -1445,14 +1614,19 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
goto leave;
}
yes ? opts_set_verify_peer(opts) : opts_unset_verify_peer(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("VerifyPeer: %u\n", opts->verify_peer);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "AllowWrongHost", 15)) {
yes = check_value_yesno(value, "AllowWrongHost", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_allow_wrong_host(opts) : opts_unset_allow_wrong_host(opts);
yes ? opts_set_allow_wrong_host(opts)
: opts_unset_allow_wrong_host(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("AllowWrongHost: %u\n", opts->allow_wrong_host);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "ConnIdleTimeout", 16)) {
unsigned int i = atoi(value);
if (i >= 10 && i <= 3600) {
@ -1461,7 +1635,9 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
fprintf(stderr, "Invalid ConnIdleTimeout %s at line %d, use 10-3600\n", value, line_num);
goto leave;
}
#ifdef DEBUG_OPTS
log_dbg_printf("ConnIdleTimeout: %u\n", opts->conn_idle_timeout);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "ExpiredConnCheckPeriod", 23)) {
unsigned int i = atoi(value);
if (i >= 10 && i <= 60) {
@ -1470,7 +1646,9 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
fprintf(stderr, "Invalid ExpiredConnCheckPeriod %s at line %d, use 10-60\n", value, line_num);
goto leave;
}
#ifdef DEBUG_OPTS
log_dbg_printf("ExpiredConnCheckPeriod: %u\n", opts->expired_conn_check_period);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "SSLShutdownRetryDelay", 22)) {
unsigned int i = atoi(value);
if (i >= 100 && i <= 10000) {
@ -1479,14 +1657,18 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
fprintf(stderr, "Invalid SSLShutdownRetryDelay %s at line %d, use 100-10000\n", value, line_num);
goto leave;
}
#ifdef DEBUG_OPTS
log_dbg_printf("SSLShutdownRetryDelay: %u\n", opts->ssl_shutdown_retry_delay);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "LogStats", 9)) {
yes = check_value_yesno(value, "LogStats", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_statslog(opts) : opts_unset_statslog(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("LogStats: %u\n", opts->statslog);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "StatsPeriod", 12)) {
unsigned int i = atoi(value);
if (i >= 1 && i <= 10) {
@ -1495,23 +1677,30 @@ set_option(opts_t *opts, const char *argv0, const char *name, char *value, char
fprintf(stderr, "Invalid StatsPeriod %s at line %d, use 1-10\n", value, line_num);
goto leave;
}
#ifdef DEBUG_OPTS
log_dbg_printf("StatsPeriod: %u\n", opts->stats_period);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "RemoveHTTPAcceptEncoding", 25)) {
yes = check_value_yesno(value, "RemoveHTTPAcceptEncoding", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_remove_http_accept_encoding(opts) : opts_unset_remove_http_accept_encoding(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("RemoveHTTPAcceptEncoding: %u\n", opts->remove_http_accept_encoding);
#endif /* DEBUG_OPTS */
} else if (!strncasecmp(name, "RemoveHTTPReferer", 18)) {
yes = check_value_yesno(value, "RemoveHTTPReferer", line_num);
if (yes == -1) {
goto leave;
}
yes ? opts_set_remove_http_referer(opts) : opts_unset_remove_http_referer(opts);
#ifdef DEBUG_OPTS
log_dbg_printf("RemoveHTTPReferer: %u\n", opts->remove_http_referer);
#endif /* DEBUG_OPTS */
} else {
fprintf(stderr, "Error in conf: Unknown option '%s' at line %d\n", name, line_num);
fprintf(stderr, "Error in conf: Unknown option "
"'%s' at line %d\n", name, line_num);
goto leave;
}
@ -1589,7 +1778,8 @@ leave:
}
int
opts_set_option(opts_t *opts, const char *argv0, const char *optarg, char **natengine)
opts_set_option(opts_t *opts, const char *argv0, const char *optarg,
char **natengine)
{
char *name, *value;
int retval = -1;

@ -87,10 +87,12 @@ typedef struct opts {
unsigned int deny_ocsp : 1;
unsigned int contentlog_isdir : 1;
unsigned int contentlog_isspec : 1;
unsigned int pcaplog_isdir : 1;
unsigned int pcaplog_isspec : 1;
#ifdef HAVE_LOCAL_PROCINFO
unsigned int lprocinfo : 1;
#endif /* HAVE_LOCAL_PROCINFO */
unsigned int certgen_writeall: 1;
unsigned int certgen_writeall : 1;
#ifndef OPENSSL_NO_ENGINE
char *openssl_engine;
#endif /* !OPENSSL_NO_ENGINE */
@ -104,8 +106,14 @@ typedef struct opts {
char *conffile;
char *connectlog;
char *contentlog;
char *contentlog_basedir; /* static part of logspec, for privsep srv */
char *contentlog_basedir; /* static part of logspec for privsep srv */
char *masterkeylog;
char *pcaplog;
char *pcaplog_basedir; /* static part of pcap logspec for privsep srv */
#ifndef WITHOUT_MIRROR
char *mirrorif;
char *mirrortarget;
#endif /* !WITHOUT_MIRROR */
CONST_SSL_METHOD *(*sslmethod)(void);
#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(LIBRESSL_VERSION_NUMBER)
int sslversion;
@ -189,6 +197,15 @@ void opts_set_contentlogpathspec(opts_t *, const char *, const char *)
void opts_set_lprocinfo(opts_t *) NONNULL(1);
#endif /* HAVE_LOCAL_PROCINFO */
void opts_set_masterkeylog(opts_t *, const char *, const char *) NONNULL(1,2,3);
void opts_set_pcaplog(opts_t *, const char *, const char *) NONNULL(1,2,3);
void opts_set_pcaplogdir(opts_t *, const char *, const char *)
NONNULL(1,2,3);
void opts_set_pcaplogpathspec(opts_t *, const char *, const char *)
NONNULL(1,2,3);
#ifndef WITHOUT_MIRROR
void opts_set_mirrorif(opts_t *, const char *, const char *) NONNULL(1,2,3);
void opts_set_mirrortarget(opts_t *, const char *, const char *) NONNULL(1,2,3);
#endif /* !WITHOUT_MIRROR */
void opts_set_daemon(opts_t *) NONNULL(1);
void opts_set_debug(opts_t *) NONNULL(1);
void opts_set_debug_level(const char *) NONNULL(1);

@ -38,9 +38,11 @@
static char *argv01[] = {
"https", "127.0.0.1", "10443", "up:8080", "127.0.0.2", "443"
};
#ifndef TRAVIS
static char *argv02[] = {
"https", "::1", "10443", "up:8080", "::2", "443"
};
#endif /* !TRAVIS */
static char *argv03[] = {
"http", "127.0.0.1", "10443", "up:8080", "127.0.0.2", "443"
};
@ -53,9 +55,11 @@ static char *argv05[] = {
static char *argv06[] = {
"https", "127.0.0.1", "10443", "up:8080", "sni", "443"
};
#ifndef DOCKER
static char *argv07[] = {
"http", "127.0.0.1", "10443", "up:8080", "sni", "443"
};
#endif /* !DOCKER */
static char *argv08[] = {
"https", "127.0.0.1", "10443", "up:8080", "no_such_engine"
};
@ -74,15 +78,21 @@ static char *argv12[] = {
"autossl", "127.0.0.1", "10025", "up:9199", "127.0.0.2", "25",
"https", "127.0.0.1", "10443", "up:8080", "127.0.0.2", "443"
};
#ifndef DOCKER
static char *argv13[] = {
"autossl", "127.0.0.1", "10025", "up:9199", "sni", "25"
};
#endif /* !DOCKER */
static char *argv14[] = {
"https", "127.0.0.1", "10443", "up:8080",
"autossl", "127.0.0.1", "10025", "up:9199", "127.0.0.2", "25"
};
#ifdef __linux__
#define NATENGINE "netfilter"
#else
#define NATENGINE "pf"
#endif
START_TEST(proxyspec_parse_01)
{
@ -108,6 +118,7 @@ START_TEST(proxyspec_parse_01)
}
END_TEST
#ifndef TRAVIS
START_TEST(proxyspec_parse_02)
{
proxyspec_t *spec = NULL;
@ -131,7 +142,9 @@ START_TEST(proxyspec_parse_02)
proxyspec_free(spec);
}
END_TEST
#endif /* !TRAVIS */
#ifndef DOCKER
START_TEST(proxyspec_parse_03)
{
proxyspec_t *spec = NULL;
@ -144,7 +157,9 @@ START_TEST(proxyspec_parse_03)
proxyspec_free(spec);
}
END_TEST
#endif /* !DOCKER */
#ifndef DOCKER
START_TEST(proxyspec_parse_04)
{
proxyspec_t *spec = NULL;
@ -157,6 +172,7 @@ START_TEST(proxyspec_parse_04)
proxyspec_free(spec);
}
END_TEST
#endif /* !DOCKER */
START_TEST(proxyspec_parse_05)
{
@ -253,6 +269,7 @@ START_TEST(proxyspec_parse_08)
}
END_TEST
#ifndef DOCKER
START_TEST(proxyspec_parse_09)
{
proxyspec_t *spec = NULL;
@ -278,6 +295,7 @@ START_TEST(proxyspec_parse_10)
proxyspec_free(spec);
}
END_TEST
#endif /* !DOCKER */
START_TEST(proxyspec_parse_11)
{
@ -303,6 +321,7 @@ START_TEST(proxyspec_parse_11)
}
END_TEST
#ifndef DOCKER
START_TEST(proxyspec_parse_12)
{
proxyspec_t *spec = NULL;
@ -315,7 +334,9 @@ START_TEST(proxyspec_parse_12)
proxyspec_free(spec);
}
END_TEST
#endif /* !DOCKER */
#ifndef TRAVIS
START_TEST(proxyspec_parse_13)
{
proxyspec_t *spec = NULL;
@ -386,6 +407,7 @@ START_TEST(proxyspec_parse_14)
proxyspec_free(spec);
}
END_TEST
#endif /* !TRAVIS */
START_TEST(proxyspec_parse_15)
{
@ -445,6 +467,7 @@ START_TEST(proxyspec_parse_16)
}
END_TEST
#ifndef DOCKER
START_TEST(proxyspec_parse_17)
{
proxyspec_t *spec = NULL;
@ -457,6 +480,7 @@ START_TEST(proxyspec_parse_17)
proxyspec_free(spec);
}
END_TEST
#endif /* !DOCKER */
START_TEST(proxyspec_parse_18)
{
@ -518,24 +542,32 @@ opts_suite(void)
tcase_add_test(tc, proxyspec_parse_01);
#ifndef TRAVIS
tcase_add_test(tc, proxyspec_parse_02); /* IPv6 */
#endif /* TRAVIS */
#endif /* !TRAVIS */
#ifndef DOCKER
tcase_add_exit_test(tc, proxyspec_parse_03, EXIT_FAILURE);
tcase_add_exit_test(tc, proxyspec_parse_04, EXIT_FAILURE);
#endif /* !DOCKER */
tcase_add_test(tc, proxyspec_parse_05);
tcase_add_test(tc, proxyspec_parse_06);
tcase_add_test(tc, proxyspec_parse_07);
tcase_add_test(tc, proxyspec_parse_08);
#ifndef DOCKER
tcase_add_exit_test(tc, proxyspec_parse_09, EXIT_FAILURE);
tcase_add_exit_test(tc, proxyspec_parse_10, EXIT_FAILURE);
#endif /* !DOCKER */
tcase_add_test(tc, proxyspec_parse_11);
#ifndef DOCKER
tcase_add_exit_test(tc, proxyspec_parse_12, EXIT_FAILURE);
#endif /* !DOCKER */
#ifndef TRAVIS
tcase_add_test(tc, proxyspec_parse_13); /* IPv6 */
tcase_add_test(tc, proxyspec_parse_14); /* IPv6 */
#endif /* TRAVIS */
#endif /* !TRAVIS */
tcase_add_test(tc, proxyspec_parse_15);
tcase_add_test(tc, proxyspec_parse_16);
#ifndef DOCKER
tcase_add_exit_test(tc, proxyspec_parse_17, EXIT_FAILURE);
#endif /* !DOCKER */
tcase_add_test(tc, proxyspec_parse_18);
suite_add_tcase(s, tc);
@ -543,6 +575,13 @@ opts_suite(void)
tcase_add_test(tc, opts_debug_01);
suite_add_tcase(s, tc);
#ifdef DOCKER
fprintf(stderr, "opts: 6 tests omitted because building in docker\n");
#endif
#ifdef TRAVIS
fprintf(stderr, "opts: 3 tests omitted because building in travis\n");
#endif
return s;
}

@ -73,6 +73,15 @@
#define PRIVSEP_ANS_DENIED 3 /* request denied */
#define PRIVSEP_ANS_SYS_ERR 4 /* system error; arg=errno */
/* Whether we short-circuit calls to privsep_client_* directly to
* privsep_server_* within the client process, bypassing the privilege
* separation mechanism; this is a performance optimization for use cases
* where the user chooses performance over security, especially with options
* that require privsep operations for each connection passing through.
* In the current implementation, for consistency, we still fork normally, but
* will not actually send any privsep requests to the parent process. */
static int privsep_fastpath;
/* communication with signal handler */
static volatile sig_atomic_t received_sighup;
static volatile sig_atomic_t received_sigint;
@ -88,11 +97,12 @@ privsep_server_signal_handler(int sig)
{
int saved_errno;
saved_errno = errno;
#ifdef DEBUG_PRIVSEP_SERVER
log_dbg_printf("privsep_server_signal_handler\n");
#endif /* DEBUG_PRIVSEP_SERVER */
saved_errno = errno;
switch (sig) {
case SIGHUP:
received_sighup = 1;
@ -136,53 +146,89 @@ privsep_server_signal_handler(int sig)
}
static int WUNRES
privsep_server_openfile_verify(opts_t *opts, char *fn, int mkpath)
privsep_server_openfile_verify(opts_t *opts, const char *fn, UNUSED int mkpath)
{
if (mkpath && !opts->contentlog_isspec)
return -1;
if (!mkpath && !opts->contentlog_isdir)
/* Prefix must match one of the active log files that use privsep. */
do {
if (opts->contentlog) {
if (strstr(fn, opts->contentlog_isspec
? opts->contentlog_basedir
: opts->contentlog) == fn)
break;
}
if (opts->pcaplog) {
if (strstr(fn, opts->pcaplog_isspec
? opts->pcaplog_basedir
: opts->pcaplog) == fn)
break;
}
if (opts->connectlog) {
if (strstr(fn, opts->connectlog) == fn)
break;
}
if (opts->masterkeylog) {
if (strstr(fn, opts->masterkeylog) == fn)
break;
}
return -1;
if (strstr(fn, mkpath ? opts->contentlog_basedir
: opts->contentlog) != fn ||
strstr(fn, "/../"))
} while (0);
/* Path must not contain dot-dot to prevent escaping the prefix. */
if (strstr(fn, "/../"))
return -1;
return 0;
}
static int WUNRES
privsep_server_openfile(char *fn, int mkpath)
privsep_server_openfile(const char *fn, int mkpath)
{
int fd;
int fd, tmp;
if (mkpath) {
char *filedir, *fn2;
fn2 = strdup(fn);
if (!fn2) {
tmp = errno;
log_err_level_printf(LOG_CRIT, "Could not duplicate filname: %s (%i)\n",
strerror(errno), errno);
errno = tmp;
return -1;
}
filedir = dirname(fn2);
if (!filedir) {
tmp = errno;
log_err_level_printf(LOG_CRIT, "Could not get dirname: %s (%i)\n",
strerror(errno), errno);
free(fn2);
errno = tmp;
return -1;
}
if (sys_mkpath(filedir, DFLT_DIRMODE) == -1) {
tmp = errno;
log_err_level_printf(LOG_CRIT, "Could not create directory '%s': %s (%i)\n",
filedir, strerror(errno), errno);
free(fn2);
errno = tmp;
return -1;
}
free(fn2);
}
fd = open(fn, O_WRONLY|O_APPEND|O_CREAT, DFLT_FILEMODE);
fd = open(fn, O_RDWR|O_CREAT, DFLT_FILEMODE);
if (fd == -1) {
tmp = errno;
log_err_level_printf(LOG_CRIT, "Failed to open '%s': %s (%i)\n",
fn, strerror(errno), errno);
errno = tmp;
return -1;
}
if (lseek(fd, 0, SEEK_END) == -1) {
tmp = errno;
log_err_level_printf(LOG_CRIT, "Failed to seek on '%s': %s (%i)\n",
fn, strerror(errno), errno);
errno = tmp;
return -1;
}
return fd;
@ -191,6 +237,8 @@ privsep_server_openfile(char *fn, int mkpath)
static int WUNRES
privsep_server_opensock_verify(opts_t *opts, void *arg)
{
/* This check is safe, because modifications of the spec in the child
* process do not affect the copy of the spec here in the parent. */
for (proxyspec_t *spec = opts->spec; spec; spec = spec->next) {
if (spec == arg)
return 0;
@ -199,7 +247,7 @@ privsep_server_opensock_verify(opts_t *opts, void *arg)
}
static int WUNRES
privsep_server_opensock(proxyspec_t *spec)
privsep_server_opensock(const proxyspec_t *spec)
{
evutil_socket_t fd;
int on = 1;
@ -255,7 +303,7 @@ privsep_server_opensock(proxyspec_t *spec)
}
static int WUNRES
privsep_server_opensock_child(proxyspec_t *spec)
privsep_server_opensock_child(const proxyspec_t *spec)
{
evutil_socket_t fd;
int on = 1;
@ -305,7 +353,7 @@ privsep_server_opensock_child(proxyspec_t *spec)
}
static int WUNRES
privsep_server_certfile_verify(opts_t *opts, char *fn)
privsep_server_certfile_verify(opts_t *opts, const char *fn)
{
if (!opts->certgendir)
return -1;
@ -315,7 +363,7 @@ privsep_server_certfile_verify(opts_t *opts, char *fn)
}
static int WUNRES
privsep_server_certfile(char *fn)
privsep_server_certfile(const char *fn)
{
int fd;
@ -627,7 +675,7 @@ privsep_server(opts_t *opts, int sigpipe, int srvsock[], size_t nsrvsock,
#endif /* DEBUG_PRIVSEP_SERVER */
} while (rv == -1 && errno == EINTR);
if (rv == -1) {
log_err_level_printf(LOG_CRIT, "Select failed: %s (%i)\n",
log_err_level_printf(LOG_CRIT, "select() failed: %s (%i)\n",
strerror(errno), errno);
return -1;
}
@ -747,6 +795,9 @@ privsep_client_openfile(int clisock, const char *fn, int mkpath)
int fd = -1;
ssize_t n;
if (privsep_fastpath)
return privsep_server_openfile(fn, mkpath);
req[0] = mkpath ? PRIVSEP_REQ_OPENFILE_P : PRIVSEP_REQ_OPENFILE;
memcpy(req + 1, fn, sizeof(req) - 1);
@ -794,6 +845,9 @@ privsep_client_opensock(int clisock, const proxyspec_t *spec)
int fd = -1;
ssize_t n;
if (privsep_fastpath)
return privsep_server_opensock(spec);
req[0] = PRIVSEP_REQ_OPENSOCK;
*((const proxyspec_t **)&req[1]) = spec;
@ -888,6 +942,9 @@ privsep_client_certfile(int clisock, const char *fn)
int fd = -1;
ssize_t n;
if (privsep_fastpath)
return privsep_server_certfile(fn);
req[0] = PRIVSEP_REQ_CERTFILE;
memcpy(req + 1, fn, sizeof(req) - 1);
@ -958,6 +1015,14 @@ privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
int sockcliv[nclisock][2];
pid_t pid;
if (!opts->dropuser) {
log_dbg_printf("Privsep fastpath enabled\n");
privsep_fastpath = 1;
} else {
log_dbg_printf("Privsep fastpath disabled\n");
privsep_fastpath = 0;
}
received_sigquit = 0;
received_sighup = 0;
received_sigint = 0;
@ -990,6 +1055,7 @@ privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
i, sockcliv[i][0], sockcliv[i][1]);
}
log_dbg_printf("Privsep parent pid %i\n", getpid());
pid = fork();
if (pid == -1) {
log_err_level_printf(LOG_CRIT, "Failed to fork: %s (%i)\n",
@ -1018,7 +1084,7 @@ privsep_fork(opts_t *opts, int clisock[], size_t nclisock)
n = read(chldpipev[0], buf, sizeof(buf));
} while (n == -1 && errno == EINTR);
close(chldpipev[0]);
log_dbg_printf("Privsep child pid %i\n", getpid());
/* return the privsep client sockets */
for (size_t i = 0; i < nclisock; i++)
clisock[i] = sockcliv[i][1];

@ -268,7 +268,7 @@ proc_freebsd_get_info(pid_t pid, char **path, uid_t *uid, gid_t *gid) {
static struct kinfo_proc proc;
size_t len;
int mib[4];
char buf[PATH_MAX + 1];
char buf[PATH_MAX];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;

@ -135,7 +135,7 @@ protoautossl_conn_connect(pxy_conn_ctx_t *ctx)
bufferevent_enable(ctx->srvdst.bev, EV_READ|EV_WRITE);
/* initiate connection */
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->addr, ctx->addrlen) == -1) {
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
log_err_level_printf(LOG_CRIT, "protoautossl_conn_connect: bufferevent_socket_connect for srvdst failed\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "protoautossl_conn_connect: bufferevent_socket_connect for srvdst failed, fd=%d\n", ctx->fd);

@ -119,7 +119,7 @@ protopassthrough_conn_connect(pxy_conn_ctx_t *ctx)
bufferevent_enable(ctx->srvdst.bev, EV_READ|EV_WRITE);
/* initiate connection */
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->addr, ctx->addrlen) == -1) {
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
log_err_level_printf(LOG_CRIT, "protopassthrough_conn_connect: bufferevent_socket_connect for srvdst failed\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "protopassthrough_conn_connect: bufferevent_socket_connect for srvdst failed, fd=%d\n", ctx->fd);

@ -540,8 +540,8 @@ protossl_srcssl_create(pxy_conn_ctx_t *ctx, SSL *origssl)
{
cert_t *cert;
cachemgr_dsess_set((struct sockaddr*)&ctx->addr,
ctx->addrlen, ctx->sslctx->sni,
cachemgr_dsess_set((struct sockaddr*)&ctx->dstaddr,
ctx->dstaddrlen, ctx->sslctx->sni,
SSL_get0_session(origssl));
ctx->sslctx->origcrt = SSL_get_peer_certificate(origssl);
@ -769,8 +769,8 @@ protossl_dstssl_create(pxy_conn_ctx_t *ctx)
#endif /* SSL_MODE_RELEASE_BUFFERS */
/* session resuming based on remote endpoint address and port */
sess = cachemgr_dsess_get((struct sockaddr *)&ctx->addr,
ctx->addrlen, ctx->sslctx->sni); /* new sess inst */
sess = cachemgr_dsess_get((struct sockaddr *)&ctx->dstaddr,
ctx->dstaddrlen, ctx->sslctx->sni); /* new sess inst */
if (sess) {
if (OPTS_DEBUG(ctx->opts)) {
log_dbg_printf("Attempt reuse dst SSL session\n");
@ -924,8 +924,8 @@ protossl_sni_resolve_cb(int errcode, struct evutil_addrinfo *ai, void *arg)
return;
}
memcpy(&ctx->addr, ai->ai_addr, ai->ai_addrlen);
ctx->addrlen = ai->ai_addrlen;
memcpy(&ctx->dstaddr, ai->ai_addr, ai->ai_addrlen);
ctx->dstaddrlen = ai->ai_addrlen;
evutil_freeaddrinfo(ai);
pxy_conn_connect(ctx);
}
@ -1016,7 +1016,7 @@ protossl_fd_readcb(MAYBE_UNUSED evutil_socket_t fd, UNUSED short what, void *arg
event_free(ctx->ev);
ctx->ev = NULL;
if (ctx->sslctx->sni && !ctx->addrlen && ctx->spec->sni_port) {
if (ctx->sslctx->sni && !ctx->dstaddrlen && ctx->spec->sni_port) {
char sniport[6];
struct evutil_addrinfo hints;
@ -1110,7 +1110,7 @@ protossl_conn_connect(pxy_conn_ctx_t *ctx)
bufferevent_enable(ctx->srvdst.bev, EV_WRITE);
/* initiate connection */
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->addr, ctx->addrlen) == -1) {
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
log_err_level_printf(LOG_CRIT, "protossl_conn_connect: bufferevent_socket_connect for srvdst failed\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "protossl_conn_connect: bufferevent_socket_connect for srvdst failed, fd=%d\n", ctx->fd);

@ -179,7 +179,7 @@ prototcp_conn_connect(pxy_conn_ctx_t *ctx)
bufferevent_enable(ctx->srvdst.bev, EV_WRITE);
/* initiate connection */
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->addr, ctx->addrlen) == -1) {
if (bufferevent_socket_connect(ctx->srvdst.bev, (struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen) == -1) {
log_err_level_printf(LOG_CRIT, "prototcp_conn_connect: bufferevent_socket_connect for srvdst failed\n");
#ifdef DEBUG_PROXY
log_dbg_level_printf(LOG_DBG_MODE_FINE, "prototcp_conn_connect: bufferevent_socket_connect for srvdst failed, fd=%d\n", ctx->fd);

@ -208,9 +208,9 @@ proxy_signal_cb(evutil_socket_t fd, UNUSED short what, void *arg)
case SIGTERM:
case SIGQUIT:
case SIGINT:
case SIGHUP:
proxy_loopbreak(ctx);
break;
case SIGHUP:
case SIGUSR1:
if (log_reopen() == -1) {
log_err_level_printf(LOG_WARNING, "Failed to reopen logs\n");
@ -376,7 +376,7 @@ leave0:
}
/*
* Run the event loop. Returns when the event loop is cancelled by a signal
* Run the event loop. Returns when the event loop is canceled by a signal
* or on failure.
*/
void

@ -390,7 +390,7 @@ pxy_conn_ctx_free(pxy_conn_ctx_t *ctx, int by_requestor)
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_ctx_free: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (WANT_CONTENT_LOG(ctx) && ctx->logctx) {
if (WANT_CONTENT_LOG(ctx)) {
if (log_content_close(&ctx->logctx, by_requestor) == -1) {
log_err_level_printf(LOG_WARNING, "Content log close failed\n");
}
@ -585,13 +585,13 @@ pxy_log_content_buf(pxy_conn_ctx_t *ctx, unsigned char *buf, size_t sz, int req)
{
if (WANT_CONTENT_LOG(ctx->conn)) {
if (buf) {
logbuf_t *lb = logbuf_new_alloc(sz, NULL, NULL);
logbuf_t *lb = logbuf_new_alloc(sz, NULL);
if (!lb) {
ctx->conn->enomem = 1;
return -1;
}
memcpy(lb->buf, buf, lb->sz);
if (log_content_submit(ctx->conn->logctx, lb, req) == -1) {
if (log_content_submit(&ctx->conn->logctx, lb, req) == -1) {
logbuf_free(lb);
log_err_level_printf(LOG_WARNING, "Content log submission failed\n");
return -1;
@ -659,10 +659,16 @@ pxy_prepare_logging(pxy_conn_ctx_t *ctx)
{
/* prepare logging, part 2 */
if (WANT_CONNECT_LOG(ctx) || WANT_CONTENT_LOG(ctx)) {
return pxy_prepare_logging_local_procinfo(ctx);
if (pxy_prepare_logging_local_procinfo(ctx) == -1) {
return -1;
}
}
if (WANT_CONTENT_LOG(ctx)) {
if (log_content_open(&ctx->logctx, ctx->opts,
(struct sockaddr *)&ctx->srcaddr,
ctx->srcaddrlen,
(struct sockaddr *)&ctx->dstaddr,
ctx->dstaddrlen,
STRORDASH(ctx->srchost_str), STRORDASH(ctx->srcport_str),
STRORDASH(ctx->dsthost_str), STRORDASH(ctx->dstport_str),
#ifdef HAVE_LOCAL_PROCINFO
@ -952,7 +958,7 @@ pxy_listener_acceptcb_child(UNUSED struct evconnlistener *listener, evutil_socke
}
#endif /* DEBUG_PROXY */
if (!conn->addrlen) {
if (!conn->dstaddrlen) {
log_err_level_printf(LOG_CRIT, "Child no target address; aborting connection\n");
evutil_closesocket(fd);
pxy_conn_term(conn, 1);
@ -1013,7 +1019,7 @@ pxy_listener_acceptcb_child(UNUSED struct evconnlistener *listener, evutil_socke
if (OPTS_DEBUG(ctx->conn->opts)) {
char *host, *port;
if (sys_sockaddr_str((struct sockaddr *)&ctx->conn->addr, ctx->conn->addrlen, &host, &port) == 0) {
if (sys_sockaddr_str((struct sockaddr *)&ctx->conn->dstaddr, ctx->conn->dstaddrlen, &host, &port) == 0) {
log_dbg_printf("Child connecting to [%s]:%s\n", host, port);
free(host);
free(port);
@ -1024,7 +1030,7 @@ pxy_listener_acceptcb_child(UNUSED struct evconnlistener *listener, evutil_socke
/* initiate connection */
// @attention No need to check retval here, the eventcb should handle the errors
bufferevent_socket_connect(ctx->dst.bev, (struct sockaddr *)&ctx->conn->addr, ctx->conn->addrlen);
bufferevent_socket_connect(ctx->dst.bev, (struct sockaddr *)&ctx->conn->dstaddr, ctx->conn->dstaddrlen);
ctx->dst_fd = bufferevent_getfd(ctx->dst.bev);
ctx->conn->child_dst_fd = ctx->dst_fd;
@ -1227,7 +1233,7 @@ pxy_try_consume_last_input_child(struct bufferevent *bev, pxy_conn_child_ctx_t *
int
pxy_set_dstaddr(pxy_conn_ctx_t *ctx)
{
if (sys_sockaddr_str((struct sockaddr *)&ctx->addr, ctx->addrlen, &ctx->dsthost_str, &ctx->dstport_str) != 0) {
if (sys_sockaddr_str((struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen, &ctx->dsthost_str, &ctx->dstport_str) != 0) {
// sys_sockaddr_str() may fail due to either malloc() or getnameinfo()
ctx->enomem = 1;
pxy_conn_term(ctx, 1);
@ -1479,7 +1485,7 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
log_dbg_level_printf(LOG_DBG_MODE_FINEST, "pxy_conn_connect: ENTER, fd=%d\n", ctx->fd);
#endif /* DEBUG_PROXY */
if (!ctx->addrlen) {
if (!ctx->dstaddrlen) {
log_err_level_printf(LOG_CRIT, "No target address; aborting connection\n");
evutil_closesocket(ctx->fd);
pxy_conn_ctx_free(ctx, 1);
@ -1488,7 +1494,7 @@ pxy_conn_connect(pxy_conn_ctx_t *ctx)
if (OPTS_DEBUG(ctx->opts)) {
char *host, *port;
if (sys_sockaddr_str((struct sockaddr *)&ctx->addr, ctx->addrlen, &host, &port) == 0) {
if (sys_sockaddr_str((struct sockaddr *)&ctx->dstaddr, ctx->dstaddrlen, &host, &port) == 0) {
log_dbg_printf("Connecting to [%s]:%s\n", host, port);
free(host);
free(port);
@ -1590,8 +1596,8 @@ pxy_conn_setup(evutil_socket_t fd,
/* determine original destination of connection */
if (spec->natlookup) {
/* NAT engine lookup */
ctx->addrlen = sizeof(struct sockaddr_storage);
if (spec->natlookup((struct sockaddr *)&ctx->addr, &ctx->addrlen, fd, peeraddr, peeraddrlen) == -1) {
ctx->dstaddrlen = sizeof(struct sockaddr_storage);
if (spec->natlookup((struct sockaddr *)&ctx->dstaddr, &ctx->dstaddrlen, fd, peeraddr, peeraddrlen) == -1) {
log_err_printf("Connection not found in NAT state table, aborting connection\n");
evutil_closesocket(fd);
pxy_conn_ctx_free(ctx, 1);
@ -1599,8 +1605,8 @@ pxy_conn_setup(evutil_socket_t fd,
}
} else if (spec->connect_addrlen > 0) {
/* static forwarding */
ctx->addrlen = spec->connect_addrlen;
memcpy(&ctx->addr, &spec->connect_addr, ctx->addrlen);
ctx->dstaddrlen = spec->connect_addrlen;
memcpy(&ctx->dstaddr, &spec->connect_addr, ctx->dstaddrlen);
} else {
/* SNI mode */
if (!ctx->spec->ssl) {
@ -1617,13 +1623,16 @@ pxy_conn_setup(evutil_socket_t fd,
}
/* prepare logging, part 1 */
if (WANT_CONNECT_LOG(ctx) || WANT_CONTENT_LOG(ctx)) {
if (opts->pcaplog
#ifndef WITHOUT_MIRROR
|| opts->mirrorif
#endif /* !WITHOUT_MIRROR */
#ifdef HAVE_LOCAL_PROCINFO
if (ctx->opts->lprocinfo) {
memcpy(&ctx->lproc.srcaddr, peeraddr, peeraddrlen);
ctx->lproc.srcaddrlen = peeraddrlen;
}
|| opts->lprocinfo
#endif /* HAVE_LOCAL_PROCINFO */
) {
ctx->srcaddrlen = peeraddrlen;
memcpy(&ctx->srcaddr, peeraddr, ctx->srcaddrlen);
}
/* for SSL, defer dst connection setup to initial_readcb */

@ -181,7 +181,7 @@ struct pxy_conn_ctx {
char *dstport_str;
/* content log context */
log_content_ctx_t *logctx;
log_content_ctx_t logctx;
/* status flags */
unsigned int connected : 1; /* 0 until both ends are connected */
@ -195,9 +195,11 @@ struct pxy_conn_ctx {
struct event *ev;
/* original destination address, family and certificate */
struct sockaddr_storage addr;
socklen_t addrlen;
/* original source and destination address, and family */
struct sockaddr_storage srcaddr;
socklen_t srcaddrlen;
struct sockaddr_storage dstaddr;
socklen_t dstaddrlen;
int af;
// Thread that the conn is attached to

35
ssl.c

@ -148,12 +148,12 @@ ssl_openssl_version(void)
"---------------------------------------\n");
}
#ifdef LIBRESSL_VERSION_NUMBER
fprintf(stderr, "LibreSSL detected: %s (%lx)\n",
fprintf(stderr, "OpenSSL API provided by LibreSSL: %s (%lx)\n",
LIBRESSL_VERSION_TEXT,
(long unsigned int)LIBRESSL_VERSION_NUMBER);
#endif /* LIBRESSL_VERSION_NUMBER */
#ifdef OPENSSL_IS_BORINGSSL
fprintf(stderr, "BoringSSL detected\n")
fprintf(stderr, "OpenSSL API provided by BoringSSL\n")
#endif /* OPENSSL_IS_BORINGSSL */
#ifndef OPENSSL_NO_TLSEXT
fprintf(stderr, "OpenSSL has support for TLS extensions\n"
@ -484,7 +484,8 @@ ssl_fini(void)
ERR_remove_state(0); /* current thread */
#endif
#if defined(OPENSSL_THREADS) && ((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER))
#if defined(OPENSSL_THREADS) && \
((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER))
CRYPTO_set_locking_callback(NULL);
CRYPTO_set_dynlock_create_callback(NULL);
CRYPTO_set_dynlock_lock_callback(NULL);
@ -500,7 +501,8 @@ ssl_fini(void)
free(ssl_mutex);
#endif
#if !defined(OPENSSL_NO_ENGINE) && ((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER))
#if !defined(OPENSSL_NO_ENGINE) && \
((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER))
ENGINE_cleanup();
#endif /* !OPENSSL_NO_ENGINE && OPENSSL_VERSION_NUMBER < 0x10100000L */
CONF_modules_finish();
@ -510,6 +512,8 @@ ssl_fini(void)
EVP_cleanup();
ERR_free_strings();
CRYPTO_cleanup_all_ex_data();
ssl_initialized = 0;
}
/*
@ -602,10 +606,10 @@ ssl_ssl_masterkey_to_str(SSL *ssl)
r = &rbuf[0];
SSL_SESSION_get_master_key(SSL_get0_session(ssl), k, sizeof(kbuf));
SSL_get_client_random(ssl, r, sizeof(rbuf));
#else /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) */
#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
k = ssl->session->master_key;
r = ssl->s3->client_random;
#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) */
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
rv = asprintf(&str,
"CLIENT_RANDOM "
"%02X%02X%02X%02X%02X%02X%02X%02X"
@ -876,7 +880,7 @@ ssl_rand(void *p, size_t sz)
rv = RAND_pseudo_bytes((unsigned char*)p, sz);
if (rv == 1)
return 0;
#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) */
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
rv = RAND_bytes((unsigned char*)p, sz);
if (rv == 1)
return 0;
@ -1265,18 +1269,21 @@ leave1:
* Copies the certificate stack to the SSL_CTX internal data structures
* and increases reference counts accordingly.
*/
void
int
ssl_x509chain_use(SSL_CTX *sslctx, X509 *crt, STACK_OF(X509) *chain)
{
SSL_CTX_use_certificate(sslctx, crt);
if (SSL_CTX_use_certificate(sslctx, crt) != 1)
return -1;
for (int i = 0; i < sk_X509_num(chain); i++) {
X509 *tmpcrt;
tmpcrt = sk_X509_value(chain, i);
ssl_x509_refcount_inc(tmpcrt);
SSL_CTX_add_extra_chain_cert(sslctx, tmpcrt);
if (SSL_CTX_add_extra_chain_cert(sslctx, tmpcrt) != 1)
return -1;
}
return 0;
}
/*
@ -1371,11 +1378,11 @@ ssl_key_genrsa(const int keysize)
RSA_free(rsa);
return NULL;
}
#else /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) */
#else /* OPENSSL_VERSION_NUMBER < 0x10100000L */
rsa = RSA_generate_key(keysize, 3, NULL, NULL);
if (!rsa)
return NULL;
#endif /* (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER) */
#endif /* OPENSSL_VERSION_NUMBER < 0x10100000L */
pkey = EVP_PKEY_new();
EVP_PKEY_assign_RSA(pkey, rsa); /* does not increment refcount */
return pkey;
@ -1930,7 +1937,7 @@ ssl_session_to_str(SSL_SESSION *sess)
/*
* Returns non-zero if the session timeout has not expired yet,
* zero if the session has expired or an error occured.
* zero if the session has expired or an error occurred.
*/
int
ssl_session_is_valid(SSL_SESSION *sess)
@ -1974,7 +1981,7 @@ ssl_is_ocspreq(const unsigned char *buf, size_t sz)
*
* The OpenSSL SNI API only allows to read the indicated server name at the
* time when we have to provide the server certificate. OpenSSL does not
* allow to asynchroniously read the indicated server name, wait for some
* allow to asynchronously read the indicated server name, wait for some
* unrelated event to happen, and then later to provide the server certificate
* to use and continue the handshake. Therefore we resort to parsing the
* server name from the ClientHello manually before OpenSSL gets to work on it.

@ -250,7 +250,8 @@ char * ssl_x509_to_pem(X509 *) NONNULL(1) MALLOC;
void ssl_x509_refcount_inc(X509 *) NONNULL(1);
int ssl_x509chain_load(X509 **, STACK_OF(X509) **, const char *) NONNULL(2,3);
void ssl_x509chain_use(SSL_CTX *, X509 *, STACK_OF(X509) *) NONNULL(1,2,3);
int ssl_x509chain_use(SSL_CTX *, X509 *, STACK_OF(X509) *)
NONNULL(1,2,3) WUNRES;
char * ssl_session_to_str(SSL_SESSION *) NONNULL(1) MALLOC;
int ssl_session_is_valid(SSL_SESSION *) NONNULL(1);

@ -881,7 +881,7 @@ ssl_suite(void)
tcase_add_test(tc, ssl_engine_01);
suite_add_tcase(s, tc);
#else /* OPENSSL_NO_ENGINE */
fprintf(stderr, "1 test omitted because OpenSSL has no "
fprintf(stderr, "ssl: 1 test omitted because OpenSSL has no "
"engine support\n");
#endif /* OPENSSL_NO_ENGINE */

@ -29,25 +29,25 @@
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.TH SSLPROXY 1 "15 Sep 2018"
.TH "sslproxy" "1" "03 Nov 2018" "v0.5.8" "SSLproxy"
.SH NAME
sslproxy \-\- transparent SSL/TLS proxy for decrypting and diverting network
traffic to other programs for deep SSL inspection
.SH SYNOPSIS
.na
.B sslproxy
[\fB-kCKqwWOPZdDgGsrRxeumjplILSFiMab\fP] \fB-c\fP \fIpem\fP
[\fB-kCKqwWOPZdDgGsrRxeumjplLSFXYyTIMiab\fP] \fB-c\fP \fIpem\fP
\fIproxyspecs\fP [...]
.br
.B sslproxy
[\fB-kCKqwWOPZdDgGsrRxeumjplILSFiMab\fP] \fB-c\fP \fIpem\fP \fB-t\fP \fIdir\fP
[\fB-kCKqwWOPZdDgGsrRxeumjplLSFXYyTIMiab\fP] \fB-c\fP \fIpem\fP \fB-t\fP \fIdir\fP
\fIproxyspecs\fP [...]
.br
.B sslproxy
[\fB-OPZwWdDgGsrRxeumjplILSFiMab\fP] \fB-t\fP \fIdir\fP
[\fB-OPZwWdDgGsrRxeumjplLSFXYyTIMiab\fP] \fB-t\fP \fIdir\fP
\fIproxyspecs\fP [...]
.br
.B sslproxy [\fB-kCKwWOPZdDgGsrRxeumjplILSFiM\fP] -f \fIconffile\fP
.B sslproxy [\fB-kCKwWOPZdDgGsrRxeumjplLSFXYyTIMi\fP] -f \fIconffile\fP
.br
.B sslproxy -E
.br
@ -69,7 +69,7 @@ program listening on the port given in the proxy specification. SSLproxy
inserts in the first packet the address and port it is expecting to receive the
packets back from the program. Upon receiving the packets back, SSLproxy
re-encrypts and sends them to their original destination. The return traffic
follows the same path back to the client.
follows the same path back to the client in reverse order.
.LP
This is similar in principle to divert sockets, divert(4), where the packet
filter diverts the packets to a program listening on a divert socket, and after
@ -101,10 +101,10 @@ instead of generating forged ones. SSLproxy supports NULL-prefix CN
certificates but otherwise does not implement exploits against specific
certificate verification vulnerabilities in SSL/TLS stacks.
.LP
SSLproxy implements a number of defenses against mechanisms which would
SSLproxy implements a number of defences against mechanisms which would
normally prevent MitM attacks or make them more difficult. SSLproxy can deny
OCSP requests in a generic way.
For HTTP and HTTPS connections, SSLsplit mangles headers to
For HTTP and HTTPS connections, SSLproxy mangles headers to
prevent server-instructed public key pinning (HPKP),
avoid strict transport security restrictions (HSTS),
avoid Certificate Transparency enforcement (Expect-CT) and
@ -121,7 +121,13 @@ configuration file.
SSLproxy verifies upstream certificates by default. If the verification fails,
the connection is terminated immediately. This is in contrast to SSLsplit,
because in order to maximize the chances that a connection can be successfully
split, SSLsplit accepts all certificates including self-signed ones.
split, SSLsplit accepts all certificates by default, including self-signed
ones.
.LP
Logging options include traditional SSLproxy connect and content log files as
well as PCAP files and mirroring decrypted traffic to a network interface.
Additionally, certificates, master secrets and local process information can be
logged.
.LP
SSLproxy does not automagically redirect any network traffic. To actually
implement a proxy, you also need to redirect the traffic to the system
@ -177,13 +183,14 @@ List all supported NAT engines available on the system and exit. See
NAT ENGINES for a list of NAT engines currently supported by SSLproxy.
.TP
.B \-f \fIconffile\fP
Read configuraion from \fIconffile\fP.
Read configuration from \fIconffile\fP.
.TP
.B \-F \fIlogspec\fP
Log connection content to separate log files with the given path specification
(see LOG SPECIFICATIONS below). For each connection, a log file will be
written, which will contain both directions of data as transmitted.
Information about the connection will be contained in the filename only.
Only one of \fB-F\fP, \fB-L\fP and \fB-S\fP may be used (last one wins).
.TP
.B \-g \fIpemfile\fP
Use Diffie-Hellman group parameters from \fIpemfile\fP for Ephemereal
@ -212,6 +219,11 @@ connect log and enables the respective \fB-F\fP path specification directives.
\fB-i\fP is available on Mac OS X and FreeBSD; support for other platforms has
not been implemented yet.
.TP
.B \-I \fIif\fP
Mirror connection content as emulated packets to interface \fIif\fP with
destination address given by \fB-T\fP. This option is not available if
SSLproxy was built without mirroring support.
.TP
.B \-j \fIjaildir\fP
Change the root directory to \fIjaildir\fP using chroot(2) after opening files.
Note that this has implications for \fBsni\fP \fIproxyspecs\fP.
@ -221,6 +233,9 @@ Using \fBsni\fP proxyspecs depends on name resolution.
Some operating systems require special device nodes such as \fB/dev/null\fP
to be present within the jail. Check your system's documentation for details.
.TP
.B \-J
Enable connection statistics logging.
.TP
.B \-k \fIpemfile\fP
Use CA private key from \fIpemfile\fP to sign certificates forged on-the-fly.
If \fIpemfile\fP also contains the matching CA certificate, it is also loaded,
@ -237,16 +252,14 @@ If \fB-K\fP is not given, SSLproxy will generate a random 1024-bit RSA key.
.B \-l \fIlogfile\fP
Log connections to \fIlogfile\fP in a single line per connection format,
including addresses and ports and some HTTP and SSL information, if available.
SIGUSR1 will cause \fIlogfile\fP to be re-opened.
.TP
.B \-I
Enable connection statistics logging.
SIGHUP or SIGUSR1 will cause \fIlogfile\fP to be re-opened.
.TP
.B \-L \fIlogfile\fP
Log connection content to \fIlogfile\fP. The content log will contain a
parsable log format with transmitted data, prepended with headers identifying
the connection and the data length of each logged segment.
SIGUSR1 will cause \fIlogfile\fP to be re-opened.
SIGHUP or SIGUSR1 will cause \fIlogfile\fP to be re-opened.
Only one of \fB-F\fP, \fB-L\fP and \fB-S\fP may be used (last one wins).
.TP
.B \-m
When dropping privileges using \fB-u\fP, override the target primary group
@ -257,8 +270,8 @@ Log master keys to \fIlogfile\fP in SSLKEYLOGFILE format as defined by Mozilla.
Logging master keys in this format allows for decryption of SSL/TLS traffic
using Wireshark.
Note that unlike browsers implementing this feature, setting the SSLKEYLOGFILE
environment variable has no effect on SSLsplit.
SIGUSR1 will cause \fIlogfile\fP to be re-opened.
environment variable has no effect on SSLproxy.
SIGHUP or SIGUSR1 will cause \fIlogfile\fP to be re-opened.
.TP
.B \-O
Deny all Online Certificate Status Protocol (OCSP) requests on all
@ -278,6 +291,20 @@ within the \fIcertdir\fP specified by \fB-t\fP.
Write the process ID to \fIpidfile\fP and refuse to run if the \fIpidfile\fP
is already in use by another process.
.TP
.B \-P
Passthrough SSL/TLS connections which cannot be split instead of dropping them.
Connections cannot be split if \fB-c\fP and \fB-k\fP are not given and the
site does not match any certificate loaded using \fB-t\fP, or if the connection
to the original server gives SSL/TLS errors. Specifically, this happens if the
site requests a client certificate.
In these situations, passthrough with \fB-P\fP results in uninterrupted service
for the clients, while dropping is the more secure alternative if unmonitored
connections must be prevented.
Passthrough mode currently does not apply to SSL/TLS errors in the connection
from the client, since the connection from the client cannot easily be retried.
Specifically, \fB-P\fP does not currently work for clients that do not accept
forged certificates.
.TP
.B \-q \fIcrlurl\fP
Set CRL distribution point (CDP) \fIcrlurl\fP on forged leaf certificates.
Some clients, such as some .NET applications, reject certificates that do not
@ -328,6 +355,7 @@ Log connection content to separate log files under \fIlogdir\fP. For each
connection, a log file will be written, which will contain both directions of
data as transmitted. Information about the connection will be contained in
the filename only.
Only one of \fB-F\fP, \fB-L\fP and \fB-S\fP may be used (last one wins).
.TP
.B \-t \fIcertdir\fP
Use private key, certificate and certificate chain from PEM files in
@ -343,14 +371,23 @@ loaded from \fIcertdir\fP.
Otherwise, connections matching no certificate will be dropped, or if
\fB-P\fP is given, passed through without splitting SSL/TLS.
.TP
.B \-u
.B \-T \fIaddr\fP
Mirror connection content as emulated packets to destination address \fIaddr\fP
on the interface given by \fB-I\fP. Only IPv4 target addresses are currently
supported. This option is not available if SSLproxy was built without
mirroring support.
.TP
.B \-u \fIuser\fP
Drop privileges after opening sockets and files by setting the real,
effective and stored user IDs to \fIuser\fP and loading the appropriate
primary and ancillary groups. If \fB-u\fP is not given, SSLproxy will drop
privileges to the stored UID if EUID != UID (setuid bit scenario), or to
\fBnobody\fP if running with full \fBroot\fP privileges (EUID == UID == 0).
The user needs to be allowed to make outbound TCP connections, and in some
configurations, also to perform DNS resolution.
User \fIuser\fP needs to be allowed to make outbound TCP connections, and in
some configurations, to also perform DNS resolution.
Dropping privileges enables privilege separation, which incurs latency for
certain options, such as separate per-connection log files. By using
\fB-u root\fP, SSLproxy can be run as root without dropping privileges.
Due to an Apple bug, \fB-u\fP cannot be used with \fBpf\fP proxyspecs on
Mac OS X.
.TP
@ -364,6 +401,23 @@ OpenSSL configuration.
This option is only available if built against a version of OpenSSL with engine
support.
.TP
.B \-X \fIpcapfile\fP
Log connection content to \fIpcapfile\fP in PCAP format, with emulated TCP, IP
and Ethernet headers.
SIGHUP or SIGUSR1 will cause \fIpcapfile\fP to be re-opened.
Only one of \fB-X\fP, \fB-Y\fP and \fB-y\fP may be used (last one wins).
.TP
.B \-Y \fIpcapdir\fP
Log connection content to separate PCAP files under \fIpcapdir\fP. For each
connection, a separate PCAP file will be written.
Only one of \fB-X\fP, \fB-Y\fP and \fB-y\fP may be used (last one wins).
.TP
.B \-y \fIpcapspec\fP
Log connection content to separate PCAP files with the given path specification
(see LOG SPECIFICATIONS below). For each connection, a separate PCAP file will
be written.
Only one of \fB-X\fP, \fB-Y\fP and \fB-y\fP may be used (last one wins).
.TP
.B \-V
Display version and compiled features information and exit.
.TP
@ -440,6 +494,13 @@ Plain TCP connection without SSL/TLS and without any lower level protocol
decoding; decrypted connection content is treated as opaque stream of bytes
and not modified.
.TP
\fBautossl\fP
Plain TCP connection until a Client Hello SSL/TLS message appears in the byte
stream, then automatic upgrade to SSL/TLS interception.
This is generic, protocol-independent STARTTLS support, that may erroneously
trigger on byte sequences that look like Client Hello messages even though
there was no actual STARTTLS command issued.
.TP
.I listenaddr port
IPv4 or IPv6 address and port or service name to listen on. This is the
address and port where the NAT engine should redirect connections to.
@ -448,10 +509,14 @@ address and port where the NAT engine should redirect connections to.
Port or service name that the program is listening for connections. This is the
port where the traffic should be diverted to.
.SH SIGNALS
A running \fBsslproxy\fP accepts SIGINT and SIGQUIT for a clean shutdown and
SIGUSR1 to re-open the long-living log files (\fB-l\fP and \fB-L\fP).
Per-connection log files (\fB-S\fP and \fB-F\fP) are not re-opened because
their filename is specific to the connection.
A running \fBsslproxy\fP accepts SIGINT and SIGQUIT for a clean shutdown, and
SIGHUP and SIGUSR1 to re-open the single-file log files (such as \fB-l\fP,
\fB-L\fP and \fB-X\fP). The canonical way to rotate or post-process logs is to
rename the active log file, send SIGHUP or SIGUSR1 to the PID in the PID file
given by \fB-p\fP, give SSLproxy some time to flush buffers after closing the
old file, and then post-process the renamed log file.
Per-connection log files (such as \fB-S\fP and \fB-F\fP) are not re-opened
because their filename is specific to the connection.
.SH "LOG SPECIFICATIONS"
Log specifications are composed of zero or more printf-style directives;
ordinary characters are included directly in the output path.
@ -703,6 +768,18 @@ DNS lookups are performed asynchronously.
SSLproxy uses SSL session caching on both ends to minimize the amount of full
SSL handshakes, but even then, the limiting factor in handling SSL connections
are the actual bignum computations.
.LP
For high performance and low latency and when running SSLproxy as root or
otherwise in a privilege separation mode, avoid using options which require a
privileged operation to be invoked through privilege separation for each
connection. These are currently all per-connection log types:
content log to per-stream file in dir or filespec (\fB-F\fP, \fB-S\fP),
content log to per-stream PCAP in dir or filespec (\fB-Y\fP, \fB-y\fP), and
generated or all certificates to files in directory (\fB-w\fP, \fB-W\fP).
Instead, use the respective single-file variants where available.
It is possible, albeit not recommended, to bypass the default privilege
separation when run as root by using \fB-u root\fP, thereby bypassing
privilege separation entirely.
.SH "SEE ALSO"
sslproxy.conf(5), openssl(1), ciphers(1), speed(1),
pf(4), ipfw(8), iptables(8), ip6tables(8), ip(8),
@ -721,7 +798,9 @@ chronological order of their first contribution:
Steve Wills, Landon Fuller, Wayne Jensen, Rory McNamara, Alexander Neumann,
Adam Jacob Muller, Richard Poole, Maciej Kotowicz, Eun Soo Park, Christian
Groschupp, Alexander Savchenkov, Soner Tari, Petr Vanek, Hilko Bengen,
Philip Duldig, Levente Polyak and Nick French.
Philip Duldig, Levente Polyak, Nick French and Cihan Komecoglu.
SSLsplit contains work sponsored by HackerOne.
.SH BUGS
Use Github for submission of bug reports or patches:
.LP

@ -1,99 +1,170 @@
# This is the SSLproxy configuration file
# Sample configuration for sslproxy v0.5.8
#
# Use the -f command line option to start sslproxy with a config file.
# See sslproxy.conf(5) and sslproxy(1) for documentation.
# Use CA cert (and key) to sign forged certs
# Use CA cert (and key) to sign forged certs.
# Equivalent to -c command line option.
CACert /etc/sslproxy/ca.crt
# Use CA key (and cert) to sign forged certs
# Use CA key (and cert) to sign forged certs.
# Equivalent to -k command line option.
CAKey /etc/sslproxy/ca.key
# Use cert from pemfile when destination requests client certs
# Use cert from pemfile when destination requests client certs.
# Equivalent to -a command line option.
#ClientCert /etc/sslproxy/client.crt
# Use key from pemfile when destination requests client certs
# Use key from pemfile when destination requests client certs.
# Equivalent to -b command line option.
#ClientKey /etc/sslproxy/client.key
# Use CA chain from pemfile (intermediate and root CA certs)
# Use CA chain from pemfile (intermediate and root CA certs).
# Equivalent to -C command line option.
#CAChain /etc/sslproxy/chain.crt
# Use key from pemfile for leaf certs (default: generate)
# Use key from pemfile for leaf certs.
# Equivalent to -K command line option.
# (default: generate)
#LeafCerts /etc/sslproxy/leaf.key
# Use URL as CRL distribution point for all forged certs
#CRL http://example.com
# Use URL as CRL distribution point for all forged certs.
# Equivalent to -q command line option.
#CRL http://example.com/example.crl
# Use cert+chain+key PEM files from certdir to target all sites
# matching the common names (non-matching: generate if CA)
# Use cert+chain+key PEM files from certdir to target all sites matching the
# common names (non-matching: generate if CA).
# Equivalent to -t command line option.
#TargetCertDir /etc/sslproxy/target
# Write leaf key and only generated certificates to gendir
#WriteGenCertsDir /var/run/sslproxy
# Write leaf key and only generated certificates to gendir.
# Equivalent to -w command line option.
#WriteGenCertsDir /var/log/sslproxy
# Write leaf key and all certificates to gendir
#WriteAllCertsDir /var/run/sslproxy
# Write leaf key and all certificates to gendir.
# Equivalent to -W command line option.
#WriteAllCertsDir /var/log/sslproxy
# Deny all OCSP requests on all proxyspecs
# Deny all OCSP requests on all proxyspecs.
# Equivalent to -O command line option.
#DenyOCSP yes
# Passthrough SSL connections if they cannot be split because of
# client cert auth or no matching cert and no CA (default: drop)
# Passthrough SSL connections if they cannot be split because of client cert
# auth or no matching cert and no CA.
# Equivalent to -P command line option.
# (default: drop)
#Passthrough yes
# Use DH group params from pemfile (default: keyfiles or auto)
# Use DH group params from pemfile.
# Equivalent to -g command line option.
# (default: keyfiles or auto)
#DHGroupParams /etc/sslproxy/dh.pem
# Use ECDH named curve (default: prime256v1)
# Use ECDH named curve.
# Equivalent to -G command line option.
# (default: prime256v1)
#ECDHCurve prime256v1
# Enable/disable SSL/TLS compression on all connections
# Enable/disable SSL/TLS compression on all connections.
# Equivalent to -Z command line option.
#SSLCompression no
# Force SSL/TLS protocol version only (default: all)
# Force SSL/TLS protocol version only.
# Equivalent to -r command line option.
# (default: all)
#ForceSSLProto tls12
# Disable SSL/TLS protocol version (default: none)
# Disable SSL/TLS protocol version.
# Equivalent to -R command line option.
# (default: none)
#DisableSSLProto tls10
# Use the given OpenSSL cipher suite spec (default: ALL:-aNULL)
Ciphers ALL:!RC4
# Use the given OpenSSL cipher suite spec.
# Equivalent to -s command line option.
# (default: ALL:-aNULL)
#Ciphers MEDIUM:HIGH
# OpenSSL engine to activate, either ID or full path to shared library
# Equivalent to -x command line option
#OpenSSLEngine cloudhsm
# Specify default NAT engine to use
# Specify default NAT engine to use.
# Equivalent to -e command line option.
#NATEngine netfilter
# Drop privileges to user and group (default if run as root: nobody)
User _sslproxy
Group _sslproxy
# Drop privileges to user.
# Equivalent to -u command line option.
# (default: nobody, if run as root)
#User _sslproxy
# chroot() to jaildir (impacts sni proxyspecs, see manual page)
# Drop privileges to group.
# Equivalent to -m command line option.
# (default: primary group of user)
#Group _sslproxy
# chroot() to jaildir (impacts sni proxyspecs, see sslproxy(1)).
# Equivalent to -j command line option.
#Chroot /var/run/sslproxy
# Write pid to pidfile (default: no pid file)
# Write pid to file.
# Equivalent to -p command line option.
# (default: no pid file)
PidFile /var/run/sslproxy.pid
# Connect log: log one line summary per connection to logfile
# Connect log: log one line summary per connection to logfile.
# Equivalent to -l command line option.
#ConnectLog /var/log/sslproxy/connect.log
# Content log: full data to file or named pipe (excludes ContentLogDir/ContentLogPathSpec)
# Content log: full data to file or named pipe
# (excludes ContentLogDir/ContentLogPathSpec).
# Equivalent to -L command line option.
#ContentLog /var/log/sslproxy/content.log
# Content log: full data to separate files in dir (excludes ContentLog/ContentLogPathSpec)
# Content log: full data to separate files in dir
# (excludes ContentLog/ContentLogPathSpec).
# Equivalent to -S command line option.
#ContentLogDir /var/log/sslproxy/content
# Content log: full data to sep files with % subst (excludes ContentLog/ContentLogDir)
# Content log: full data to sep files with % subst
# (excludes ContentLog/ContentLogDir).
# Equivalent to -F command line option.
#ContentLogPathSpec /var/log/sslproxy/%X/%u-%s-%d-%T.log
# Look up local process owning each connection for logging
# Look up local process owning each connection for logging.
# Equivalent to -i command line option.
#LogProcInfo yes
# Log master keys to logfile in SSLKEYLOGFILE format
# Pcap log: packets to pcapfile (excludes PcapLogDir/PcapLogPathSpec).
# Equivalent to -X command line option.
#PcapLog /var/log/sslproxy/content.pcap
# Pcap log: packets to separate files in dir
# (excludes PcapLog/PcapLogPathSpec).
# Equivalent to -Y command line option.
#PcapLogDir /var/log/sslproxy/pcap
# Pcap log: packets to sep files with % subst (excludes PcapLog/PcapLogDir).
# Equivalent to -y command line option.
#PcapLogPathSpec /var/log/sslproxy/%X/%u-%s-%d-%T.pcap
# Mirror packets to interface.
# Equivalent to -I command line option.
#MirrorIf lo
# Mirror packets to target address (used with MirrorIf).
# Equivalent to -T command line option.
#MirrorTarget 192.0.2.1
# Log master keys to logfile in SSLKEYLOGFILE format.
# Equivalent to -M command line option.
#MasterKeyLog /var/log/sslproxy/masterkeys.log
# Daemon mode: run in background, log error messages to syslog
# Daemon mode: run in background, log error messages to syslog.
# Equivalent to -d command line option.
Daemon yes
# Debug mode: run in foreground, log debug messages on stderr
# Debug mode: run in foreground, log debug messages on stderr.
# Equivalent to -D command line option.
#Debug yes
# Verbose debug level
@ -111,6 +182,7 @@ ExpiredConnCheckPeriod 10
SSLShutdownRetryDelay 100
# Log statistics to syslog
# Equivalent to -J command line option.
LogStats yes
# Log statistics every this many ExpiredConnCheckPeriod periods

@ -2,7 +2,8 @@
.\" SSLproxy - transparent SSL/TLS proxy for diverting packets to programs
.\" https://github.com/sonertari/SSLproxy
.\"
.\" Copyright (c) 2017-2018, Soner Tari <sonertari@gmail.com>
.\" Copyright (c) 2009-2018, Daniel Roethlisberger <daniel@roe.ch>.
.\" Copyright (c) 2017-2018, Soner Tari <sonertari@gmail.com>.
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
@ -24,7 +25,8 @@
.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.TH "sslproxy.conf" "5" "Sep 15, 2018" "sslproxy 0.5.7" "SSLproxy"
.\"
.TH "sslproxy.conf" "5" "03 Nov 2018" "v0.5.8" "SSLproxy"
.SH "NAME"
.LP
\fBsslproxy.conf\fR \- Configuration file for SSLproxy
@ -32,7 +34,10 @@
.LP
The file sslproxy.conf configures SSLproxy, sslproxy(1).
.SH "FILE FORMAT"
The file consists of comments and options with arguments. Each line which starts with a hash (\fB#\fR) symbol is ignored by the parser. Options and arguments are of the form \fBOption Argument\fR. The arguments are of the following types:
The file consists of comments and options with arguments. Each line which
starts with a hash (\fB#\fR) symbol is ignored by the parser. Options and
arguments are of the form \fBOption Argument\fR. The arguments are of the
following types:
.TP
\fBBOOL\fR
Boolean value (yes/no).
@ -44,73 +49,75 @@ String.
Unsigned integer.
.SH "DIRECTIVES"
.LP
When an option is not used (hashed or doesn't exist in the configuration file) sslproxy takes a default action.
When an option is not used (hashed or doesn't exist in the configuration file)
sslproxy takes a default action. If an option does not have a command line
equivalent, -o opt=val option can be used to override it on the command line.
.TP
\fBCACert STRING\fR
Use CA cert (and key) to sign forged certs.
Use CA cert (and key) to sign forged certs. Equivalent to -c command line option.
.TP
\fBCAKey STRING\fR
Use CA key (and cert) to sign forged certs.
Use CA key (and cert) to sign forged certs. Equivalent to -k command line option.
.TP
\fBClientCert STRING\fR
Use cert from pemfile when destination requests client certs.
Use cert from pemfile when destination requests client certs. Equivalent to -a command line option.
.TP
\fBClientKey STRING\fR
Use key from pemfile when destination requests client certs.
Use key from pemfile when destination requests client certs. Equivalent to -b command line option.
.TP
\fBCAChain STRING\fR
Use CA chain from pemfile (intermediate and root CA certs).
Use CA chain from pemfile (intermediate and root CA certs). Equivalent to -C command line option.
.TP
\fBLeafCerts STRING\fR
Use key from pemfile for leaf certs.
Use key from pemfile for leaf certs. Equivalent to -K command line option.
.br
Default: generate
.TP
\fBCRL STRING\fR
Use URL as CRL distribution point for all forged certs.
Use URL as CRL distribution point for all forged certs. Equivalent to -q command line option.
.TP
\fBTargetCertDir STRING\fR
Use cert+chain+key PEM files from certdir to target all sites matching the common names (non-matching: generate if CA).
Use cert+chain+key PEM files from certdir to target all sites matching the common names (non-matching: generate if CA). Equivalent to -t command line option.
.TP
\fBWriteGenCertsDir STRING\fR
Write leaf key and only generated certificates to gendir.
Write leaf key and only generated certificates to gendir. Equivalent to -w command line option.
.TP
\fBWriteAllCertsDir STRING\fR
Write leaf key and all certificates to gendir.
Write leaf key and all certificates to gendir. Equivalent to -W command line option.
.TP
\fBDenyOCSP BOOL\fR
Deny all OCSP requests on all proxyspecs.
Deny all OCSP requests on all proxyspecs. Equivalent to -O command line option.
.TP
\fBPassthrough BOOL\fR
Passthrough SSL connections if they cannot be split because of client cert auth or no matching cert and no CA.
Passthrough SSL connections if they cannot be split because of client cert auth or no matching cert and no CA. Equivalent to -P command line option.
.br
Default: drop
.TP
\fBDHGroupParams STRING\fR
Use DH group params from pemfile.
Use DH group params from pemfile. Equivalent to -g command line option.
.br
Default: keyfiles or auto
.TP
\fBECDHCurve STRING\fR
Use ECDH named curve.
Use ECDH named curve. Equivalent to -G command line option.
.br
Default: prime256v1
.TP
\fBSSLCompression BOOL\fR
Enable/disable SSL/TLS compression on all connections.
Enable/disable SSL/TLS compression on all connections. Equivalent to -Z command line option.
.TP
\fBForceSSLProto STRING\fR
Force SSL/TLS protocol version only.
Force SSL/TLS protocol version only. Equivalent to -r command line option.
.br
Default: all
.TP
\fBDisableSSLProto STRING\fR
Disable SSL/TLS protocol version.
Disable SSL/TLS protocol version. Equivalent to -R command line option.
.br
Default: none
.TP
\fBCiphers STRING\fR
Use the given OpenSSL cipher suite spec.
Use the given OpenSSL cipher suite spec. Equivalent to -s command line option.
.br
Default: ALL:-aNULL
.TP
@ -118,50 +125,66 @@ Default: ALL:-aNULL
The OpenSSL engine to activate, either the ID or the full path to the shared
library implementing the engine. If an ID is given, the engine needs to be
known to the system-wide OpenSSL configuration. Only available if built
against a version of OpenSSL with engine support.
against a version of OpenSSL with engine support. Equivalent to -x command
line option.
.TP
\fBNATEngine STRING\fR
Specify default NAT engine to use.
Specify default NAT engine to use. Equivalent to -e command line option.
.TP
\fBUser STRING\fR
Drop privileges to user.
Drop privileges to user. Equivalent to -u command line option.
.br
Default: nobody, if run as root
.TP
\fBGroup STRING\fR
Drop privileges to group.
Drop privileges to group. Equivalent to -m command line option.
.br
Default: Primary group of user
.TP
\fBChroot STRING\fR
chroot() to jaildir (impacts sni proxyspecs, see sslproxy(1)).
chroot() to jaildir (impacts sni proxyspecs, see sslproxy(1)). Equivalent to -j command line option.
.TP
\fBPidFile STRING\fR
Write pid to file.
Write pid to file. Equivalent to -p command line option.
.TP
\fBConnectLog STRING\fR
Connect log: log one line summary per connection to logfile.
Connect log: log one line summary per connection to logfile. Equivalent to -l command line option.
.TP
\fBContentLog STRING\fR
Content log: full data to file or named pipe (excludes ContentLogDir/ContentLogPathSpec).
Content log: full data to file or named pipe (excludes ContentLogDir/ContentLogPathSpec). Equivalent to -L command line option.
.TP
\fBContentLogDir STRING\fR
Content log: full data to separate files in dir (excludes ContentLog/ContentLogPathSpec).
Content log: full data to separate files in dir (excludes ContentLog/ContentLogPathSpec). Equivalent to -S command line option.
.TP
\fBContentLogPathSpec STRING\fR
Content log: full data to sep files with % subst (excludes ContentLog/ContentLogDir).
Content log: full data to sep files with % subst (excludes ContentLog/ContentLogDir). Equivalent to -F command line option.
.TP
\fBLogProcInfo BOOL\fR
Look up local process owning each connection for logging.
Look up local process owning each connection for logging. Equivalent to -i command line option.
.TP
\fBPcapLog STRING\fR
Pcap log: packets to pcapfile (excludes PcapLogDir/PcapLogPathSpec). Equivalent to -X command line option.
.TP
\fBPcapLogDir STRING\fR
Pcap log: packets to separate files in dir (excludes PcapLog/PcapLogPathSpec). Equivalent to -Y command line option.
.TP
\fBPcapLogPathSpec STRING\fR
Pcap log: packets to sep files with % subst (excludes PcapLog/PcapLogDir). Equivalent to -y command line option.
.TP
\fBMirrorIf STRING\fR
Mirror packets to interface. Equivalent to -I command line option.
.TP
\fBMirrorTarget STRING\fR
Mirror packets to target address (used with MirrorIf). Equivalent to -T command line option.
.TP
\fBMasterKeyLog STRING\fR
Log master keys to logfile in SSLKEYLOGFILE format.
Log master keys to logfile in SSLKEYLOGFILE format. Equivalent to -M command line option.
.TP
\fBDaemon BOOL\fR
Daemon mode: run in background, log error messages to syslog.
Daemon mode: run in background, log error messages to syslog. Equivalent to -d command line option.
.TP
\fBDebug BOOL\fR
Debug mode: run in foreground, log debug messages on stderr.
Debug mode: run in foreground, log debug messages on stderr. Equivalent to -D command line option.
.TP
\fBDebugLevel NUMBER\fR
Verbose debug level, 2-4.
@ -177,12 +200,14 @@ Check for expired connections every this many seconds.
Default: 10.
.TP
\fBSSLShutdownRetryDelay NUMBER\fR
Retry to shut ssl conns down after this many micro seconds. Increasing this delay may avoid dirty shutdowns on slow connections, but increases resource usage, such as file desriptors and memory.
Retry to shut ssl conns down after this many micro seconds. Increasing this
delay may avoid dirty shutdowns on slow connections, but increases resource
usage, such as file descriptors and memory.
.br
Default: 100
.TP
\fBLogStats BOOL\fR
Log statistics to syslog.
Log statistics to syslog. Equivalent to -J command line option.
.br
Default: yes
.TP
@ -207,7 +232,9 @@ Verify peer using default certificates.
Default: yes
.TP
\fBAllowWrongHost BOOL\fR
When disabled, never add the SNI to forged certificates, even if the SNI provided by the client does not match the server certificate's CN/SAN. Helps pass the wrong.host test at https://badssl.com.
When disabled, never add the SNI to forged certificates, even if the SNI
provided by the client does not match the server certificate's CN/SAN. Helps
pass the wrong.host test at https://badssl.com.
.br
Default: no
.TP
@ -218,7 +245,7 @@ Proxy specification: type listenaddr+port up:utmport. Multiple specs are allowed
/etc/sslproxy/sslproxy.conf
.SH "AUTHOR"
.LP
Soner Tari <sonertari@gmail.com>
The config file facility was added by Soner Tari <sonertari@gmail.com>.
.SH "SEE ALSO"
.LP
sslproxy(1)

185
sys.c

@ -33,10 +33,13 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <sys/stat.h>
#include <sys/file.h>
#include <sys/un.h>
#include <sys/time.h>
#include <net/if.h>
#include <netinet/in.h>
#include <netdb.h>
#include <fcntl.h>
@ -143,43 +146,98 @@ error:
if (pw) {
endpwent();
}
if (gr) {
endgrent();
}
return ret;
}
/*
* Returns 1 if username can be loaded from user database, 0 otherwise.
* If the user exists and on successful lookup, return 0 and if uid != NULL,
* write the uid of *username* to the value pointed to by uid.
* Return -1 on failure or if the user does not exist.
*/
int
sys_isuser(const char *username)
sys_uid(const char *username, uid_t *uid)
{
struct passwd *pw;
int rv;
errno = 0;
if (!getpwnam(username)) {
if (!(pw = getpwnam(username))) {
if (errno != 0 && errno != ENOENT) {
log_err_level_printf(LOG_CRIT, "Failed to load user '%s': %s (%i)\n",
username, strerror(errno), errno);
}
return 0;
rv = -1;
} else {
if (uid)
*uid = pw->pw_uid;
rv = 0;
}
endpwent();
return 1;
return rv;
}
/*
* Returns 1 if groupname can be loaded from group database, 0 otherwise.
* Returns 1 if username can be loaded from user database, 0 otherwise.
*/
int
sys_isgroup(const char *groupname)
sys_isuser(const char *username)
{
return sys_uid(username, NULL) == 0;
}
/*
* If the group exists and on successful lookup, return 0 and if gid != NULL,
* write the gid of *groupname* to the value pointed to by gid.
* Return -1 on failure or if the group does not exist.
*/
int
sys_gid(const char *groupname, gid_t *gid)
{
struct group *gr;
int rv;
errno = 0;
if (!getgrnam(groupname)) {
if (!(gr = getgrnam(groupname))) {
if (errno != 0 && errno != ENOENT) {
log_err_level_printf(LOG_CRIT, "Failed to load group '%s': %s (%i)\n",
groupname, strerror(errno), errno);
}
return 0;
rv = -1;
} else {
if (gid)
*gid = gr->gr_gid;
rv = 0;
}
return 1;
endgrent();
return rv;
}
/*
* Returns 1 if groupname can be loaded from group database, 0 otherwise.
*/
int
sys_isgroup(const char *groupname)
{
return sys_gid(groupname, NULL) == 0;
}
/*
* Returns 1 if username is equivalent to the current effective UID.
* Returns 0 otherwise.
*/
int
sys_isgeteuid(const char *username)
{
uid_t uid;
if (sys_uid(username, &uid) == -1)
return 0;
if (uid == geteuid())
return 1;
return 0;
}
/*
@ -356,7 +414,7 @@ sys_group_str(gid_t gid)
* Determine address family of addr
*/
int
sys_get_af(char *addr)
sys_get_af(const char *addr)
{
if (strstr(addr, ":"))
return AF_INET6;
@ -474,6 +532,33 @@ sys_ip46str_sanitize(const char *s)
return copy;
}
/*
* Returns the MTU of the interface with name *ifname* or 0 on errors.
*/
size_t
sys_get_mtu(const char *ifname)
{
struct ifreq ifr;
size_t ifnamelen;
int s;
ifnamelen = strlen(ifname);
if (ifnamelen > sizeof(ifr.ifr_name) + 1)
return 0;
memcpy(ifr.ifr_name, ifname, ifnamelen);
ifr.ifr_name[ifnamelen] = '\0';
s = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (s == -1)
return 0;
if (ioctl(s, SIOCGIFMTU, &ifr) == -1) {
close(s);
return 0;
}
close(s);
return ifr.ifr_mtu;
}
/*
* Returns 1 if path points to an existing directory node in the filesystem.
* Returns 0 if path is NULL, does not exist, or points to a file of some kind.
@ -542,6 +627,54 @@ sys_mkpath(const char *path, mode_t mode)
return 0;
}
/*
* Return realpath(dirname(path)) + / + basename(path) in a newly allocated
* string. Returns NULL on failure and sets errno to ENOENT if the directory
* part does not exist.
*/
char *
sys_realdir(const char *path)
{
char *sep, *udir, *rdir, *p;
int rerrno, rv;
if (path[0] == '\0') {
errno = EINVAL;
return NULL;
}
udir = strdup(path);
if (!udir)
return NULL;
sep = strrchr(udir, '/');
if (!sep) {
free(udir);
rv = asprintf(&udir, "./%s", path);
if (rv == -1)
return NULL;
sep = udir + 1;
} else if (sep == udir) {
return udir;
}
*sep = '\0';
rdir = realpath(udir, NULL);
if (!rdir) {
rerrno = errno;
free(udir);
errno = rerrno;
return NULL;
}
rv = asprintf(&p, "%s/%s", rdir, sep + 1);
rerrno = errno;
free(rdir);
free(udir);
errno = rerrno;
if (rv == -1)
return NULL;
return p;
}
/*
* Iterate over all files in a directory hierarchy, calling the callback
* cb for each file, passing the filename and arg as arguments. Files and
@ -878,5 +1011,33 @@ sys_dump_fds(void)
}
}
static int sys_rand_seeded = 0;
static void
sys_rand_seed(void) {
struct timeval seed;
if (gettimeofday(&seed, NULL) == -1) {
srandom((unsigned)time(NULL));
} else {
srandom((unsigned)(seed.tv_sec ^ seed.tv_usec));
}
sys_rand_seeded = 1;
}
uint16_t
sys_rand16(void) {
if (unlikely(!sys_rand_seeded))
sys_rand_seed();
return random();
}
uint32_t
sys_rand32(void) {
if (unlikely(!sys_rand_seeded))
sys_rand_seed();
return random();
}
/* vim: set noet ft=c: */

10
sys.h

@ -41,20 +41,25 @@ int sys_pidf_open(const char *) NONNULL(1) WUNRES;
int sys_pidf_write(int) WUNRES;
void sys_pidf_close(int, const char *) NONNULL(2);
int sys_uid(const char *, uid_t *) NONNULL(1) WUNRES;
int sys_gid(const char *, gid_t *) NONNULL(1) WUNRES;
int sys_isuser(const char *) NONNULL(1) WUNRES;
int sys_isgroup(const char *) NONNULL(1) WUNRES;
int sys_isgeteuid(const char *) NONNULL(1) WUNRES;
char * sys_user_str(uid_t) MALLOC;
char * sys_group_str(gid_t) MALLOC;
int sys_get_af(char *);
int sys_get_af(const char *);
int sys_sockaddr_parse(struct sockaddr_storage *, socklen_t *,
char *, char *, int, int) NONNULL(1,2,3,4) WUNRES;
int sys_sockaddr_str(struct sockaddr *, socklen_t,
char **, char **) NONNULL(1,3,4);
char * sys_ip46str_sanitize(const char *) NONNULL(1) MALLOC;
size_t sys_get_mtu(const char *);
int sys_isdir(const char *) NONNULL(1) WUNRES;
int sys_mkpath(const char *, mode_t) NONNULL(1) WUNRES;
char * sys_realdir(const char *) NONNULL(1) MALLOC;
typedef int (*sys_dir_eachfile_cb_t)(const char *, void *) NONNULL(1) WUNRES;
int sys_dir_eachfile(const char *, sys_dir_eachfile_cb_t, void *) NONNULL(1,2) WUNRES;
@ -66,6 +71,9 @@ ssize_t sys_recvmsgfd(int, void *, size_t, int *) NONNULL(2) WUNRES;
void sys_dump_fds(void);
uint16_t sys_rand16(void);
uint32_t sys_rand32(void);
#endif /* !SYS_H */
/* vim: set noet ft=c: */

@ -175,6 +175,49 @@ START_TEST(sys_mkpath_01)
}
END_TEST
START_TEST(sys_realdir_01)
{
char *rd;
rd = sys_realdir("./extra/../sys.t.c");
fail_unless(!!rd, "sys_realdir failed");
fail_unless(!!strstr(rd, "/sys.t.c"), "filename not found");
fail_unless(!strstr(rd, "/extra/"), "extra in path");
fail_unless(!strstr(rd, "/../"), "dot-dot in path");
free(rd);
}
END_TEST
START_TEST(sys_realdir_02)
{
char *rd;
rd = sys_realdir("/foo/bar/baz");
fail_unless(!rd, "sys_realdir did not fail");
fail_unless(errno == ENOENT, "errno not ENOENT");
}
END_TEST
START_TEST(sys_realdir_03)
{
char *rd;
rd = sys_realdir("foobarbaz");
fail_unless(!!rd, "sys_realdir failed");
fail_unless(!!strstr(rd, "/foobarbaz"), "filename not found or dir");
free(rd);
}
END_TEST
START_TEST(sys_realdir_04)
{
char *rd;
rd = sys_realdir("");
fail_unless(!rd, "sys_realdir did not fail");
}
END_TEST
int
sys_dir_eachfile_cb(UNUSED const char *fn, void *arg)
{
@ -294,6 +337,13 @@ sys_suite(void)
tcase_add_test(tc, sys_mkpath_01);
suite_add_tcase(s, tc);
tc = tcase_create("sys_realdir");
tcase_add_test(tc, sys_realdir_01);
tcase_add_test(tc, sys_realdir_02);
tcase_add_test(tc, sys_realdir_03);
tcase_add_test(tc, sys_realdir_04);
suite_add_tcase(s, tc);
tc = tcase_create("sys_dir_eachfile");
tcase_add_test(tc, sys_dir_eachfile_01);
suite_add_tcase(s, tc);

@ -33,7 +33,7 @@
#include <pthread.h>
/*
* Threadsafe, bounded-size queue based on pthreads mutex and conds.
* Thread-safe, bounded-size queue based on pthreads mutex and conds.
* Both enqueue and dequeue are available in a blocking and non-blocking
* version.
*/

@ -75,6 +75,9 @@ XNU_RELS+= 4570.1.46 # 10.13
XNU_RELS+= 4570.20.62 # 10.13.1
XNU_RELS+= 4570.31.3 # 10.13.2
XNU_RELS+= 4570.41.2 # 10.13.3
XNU_RELS+= 4570.51.1 # 10.13.4
XNU_RELS+= 4570.61.1 # 10.13.5
XNU_RELS+= 4570.71.2 # 10.13.6
# defaults
XNURL?= https://opensource.apple.com/source/xnu/

@ -0,0 +1,367 @@
APPLE PUBLIC SOURCE LICENSE
Version 2.0 - August 6, 2003
Please read this License carefully before downloading this software.
By downloading or using this software, you are agreeing to be bound by
the terms of this License. If you do not or cannot agree to the terms
of this License, please do not download or use the software.
1. General; Definitions. This License applies to any program or other
work which Apple Computer, Inc. ("Apple") makes publicly available and
which contains a notice placed by Apple identifying such program or
work as "Original Code" and stating that it is subject to the terms of
this Apple Public Source License version 2.0 ("License"). As used in
this License:
1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
the grantor of rights, (i) claims of patents that are now or hereafter
acquired, owned by or assigned to Apple and (ii) that cover subject
matter contained in the Original Code, but only to the extent
necessary to use, reproduce and/or distribute the Original Code
without infringement; and (b) in the case where You are the grantor of
rights, (i) claims of patents that are now or hereafter acquired,
owned by or assigned to You and (ii) that cover subject matter in Your
Modifications, taken alone or in combination with Original Code.
1.2 "Contributor" means any person or entity that creates or
contributes to the creation of Modifications.
1.3 "Covered Code" means the Original Code, Modifications, the
combination of Original Code and any Modifications, and/or any
respective portions thereof.
1.4 "Externally Deploy" means: (a) to sublicense, distribute or
otherwise make Covered Code available, directly or indirectly, to
anyone other than You; and/or (b) to use Covered Code, alone or as
part of a Larger Work, in any way to provide a service, including but
not limited to delivery of content, through electronic communication
with a client other than You.
1.5 "Larger Work" means a work which combines Covered Code or portions
thereof with code not governed by the terms of this License.
1.6 "Modifications" mean any addition to, deletion from, and/or change
to, the substance and/or structure of the Original Code, any previous
Modifications, the combination of Original Code and any previous
Modifications, and/or any respective portions thereof. When code is
released as a series of files, a Modification is: (a) any addition to
or deletion from the contents of a file containing Covered Code;
and/or (b) any new file or other representation of computer program
statements that contains any part of Covered Code.
1.7 "Original Code" means (a) the Source Code of a program or other
work as originally made available by Apple under this License,
including the Source Code of any updates or upgrades to such programs
or works made available by Apple under this License, and that has been
expressly identified by Apple as such in the header file(s) of such
work; and (b) the object code compiled from such Source Code and
originally made available by Apple under this License.
1.8 "Source Code" means the human readable form of a program or other
work that is suitable for making modifications to it, including all
modules it contains, plus any associated interface definition files,
scripts used to control compilation and installation of an executable
(object code).
1.9 "You" or "Your" means an individual or a legal entity exercising
rights under this License. For legal entities, "You" or "Your"
includes any entity which controls, is controlled by, or is under
common control with, You, where "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of fifty percent
(50%) or more of the outstanding shares or beneficial ownership of
such entity.
2. Permitted Uses; Conditions & Restrictions. Subject to the terms
and conditions of this License, Apple hereby grants You, effective on
the date You accept this License and download the Original Code, a
world-wide, royalty-free, non-exclusive license, to the extent of
Apple's Applicable Patent Rights and copyrights covering the Original
Code, to do the following:
2.1 Unmodified Code. You may use, reproduce, display, perform,
internally distribute within Your organization, and Externally Deploy
verbatim, unmodified copies of the Original Code, for commercial or
non-commercial purposes, provided that in each instance:
(a) You must retain and reproduce in all copies of Original Code the
copyright and other proprietary notices and disclaimers of Apple as
they appear in the Original Code, and keep intact all notices in the
Original Code that refer to this License; and
(b) You must include a copy of this License with every copy of Source
Code of Covered Code and documentation You distribute or Externally
Deploy, and You may not offer or impose any terms on such Source Code
that alter or restrict this License or the recipients' rights
hereunder, except as permitted under Section 6.
2.2 Modified Code. You may modify Covered Code and use, reproduce,
display, perform, internally distribute within Your organization, and
Externally Deploy Your Modifications and Covered Code, for commercial
or non-commercial purposes, provided that in each instance You also
meet all of these conditions:
(a) You must satisfy all the conditions of Section 2.1 with respect to
the Source Code of the Covered Code;
(b) You must duplicate, to the extent it does not already exist, the
notice in Exhibit A in each file of the Source Code of all Your
Modifications, and cause the modified files to carry prominent notices
stating that You changed the files and the date of any change; and
(c) If You Externally Deploy Your Modifications, You must make
Source Code of all Your Externally Deployed Modifications either
available to those to whom You have Externally Deployed Your
Modifications, or publicly available. Source Code of Your Externally
Deployed Modifications must be released under the terms set forth in
this License, including the license grants set forth in Section 3
below, for as long as you Externally Deploy the Covered Code or twelve
(12) months from the date of initial External Deployment, whichever is
longer. You should preferably distribute the Source Code of Your
Externally Deployed Modifications electronically (e.g. download from a
web site).
2.3 Distribution of Executable Versions. In addition, if You
Externally Deploy Covered Code (Original Code and/or Modifications) in
object code, executable form only, You must include a prominent
notice, in the code itself as well as in related documentation,
stating that Source Code of the Covered Code is available under the
terms of this License with information on how and where to obtain such
Source Code.
2.4 Third Party Rights. You expressly acknowledge and agree that
although Apple and each Contributor grants the licenses to their
respective portions of the Covered Code set forth herein, no
assurances are provided by Apple or any Contributor that the Covered
Code does not infringe the patent or other intellectual property
rights of any other entity. Apple and each Contributor disclaim any
liability to You for claims brought by any other entity based on
infringement of intellectual property rights or otherwise. As a
condition to exercising the rights and licenses granted hereunder, You
hereby assume sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party patent
license is required to allow You to distribute the Covered Code, it is
Your responsibility to acquire that license before distributing the
Covered Code.
3. Your Grants. In consideration of, and as a condition to, the
licenses granted to You under this License, You hereby grant to any
person or entity receiving or distributing Covered Code under this
License a non-exclusive, royalty-free, perpetual, irrevocable license,
under Your Applicable Patent Rights and other intellectual property
rights (other than patent) owned or controlled by You, to use,
reproduce, display, perform, modify, sublicense, distribute and
Externally Deploy Your Modifications of the same scope and extent as
Apple's licenses under Sections 2.1 and 2.2 above.
4. Larger Works. You may create a Larger Work by combining Covered
Code with other code not governed by the terms of this License and
distribute the Larger Work as a single product. In each such instance,
You must make sure the requirements of this License are fulfilled for
the Covered Code or any portion thereof.
5. Limitations on Patent License. Except as expressly stated in
Section 2, no other patent rights, express or implied, are granted by
Apple herein. Modifications and/or Larger Works may require additional
patent licenses from Apple which Apple may grant in its sole
discretion.
6. Additional Terms. You may choose to offer, and to charge a fee for,
warranty, support, indemnity or liability obligations and/or other
rights consistent with the scope of the license granted herein
("Additional Terms") to one or more recipients of Covered Code.
However, You may do so only on Your own behalf and as Your sole
responsibility, and not on behalf of Apple or any Contributor. You
must obtain the recipient's agreement that any such Additional Terms
are offered by You alone, and You hereby agree to indemnify, defend
and hold Apple and every Contributor harmless for any liability
incurred by or claims asserted against Apple or such Contributor by
reason of any such Additional Terms.
7. Versions of the License. Apple may publish revised and/or new
versions of this License from time to time. Each version will be given
a distinguishing version number. Once Original Code has been published
under a particular version of this License, You may continue to use it
under the terms of that version. You may also choose to use such
Original Code under the terms of any subsequent version of this
License published by Apple. No one other than Apple has the right to
modify the terms applicable to Covered Code created under this
License.
8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
part pre-release, untested, or not fully tested works. The Covered
Code may contain errors that could cause failures or loss of data, and
may be incomplete or contain inaccuracies. You expressly acknowledge
and agree that use of the Covered Code, or any portion thereof, is at
Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
You acknowledge that the Covered Code is not intended for use in the
operation of nuclear facilities, aircraft navigation, communication
systems, or air traffic control machines in which case the failure of
the Covered Code could lead to death, personal injury, or severe
physical or environmental damage.
9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
TO YOU. In no event shall Apple's total liability to You for all
damages (other than as may be required by applicable law) under this
License exceed the amount of fifty dollars ($50.00).
10. Trademarks. This License does not grant any rights to use the
trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
"QuickTime", "QuickTime Streaming Server" or any other trademarks,
service marks, logos or trade names belonging to Apple (collectively
"Apple Marks") or to any trademark, service mark, logo or trade name
belonging to any Contributor. You agree not to use any Apple Marks in
or as part of the name of products derived from the Original Code or
to endorse or promote products derived from the Original Code other
than as expressly permitted by and in strict compliance at all times
with Apple's third party trademark usage guidelines which are posted
at http://www.apple.com/legal/guidelinesfor3rdparties.html.
11. Ownership. Subject to the licenses granted under this License,
each Contributor retains all rights, title and interest in and to any
Modifications made by such Contributor. Apple retains all rights,
title and interest in and to the Original Code and any Modifications
made by or on behalf of Apple ("Apple Modifications"), and such Apple
Modifications will not be automatically subject to this License. Apple
may, at its sole discretion, choose to license such Apple
Modifications under this License, or on different terms from those
contained in this License or may choose not to license them at all.
12. Termination.
12.1 Termination. This License and the rights granted hereunder will
terminate:
(a) automatically without notice from Apple if You fail to comply with
any term(s) of this License and fail to cure such breach within 30
days of becoming aware of such breach;
(b) immediately in the event of the circumstances described in Section
13.5(b); or
(c) automatically without notice from Apple if You, at any time during
the term of this License, commence an action for patent infringement
against Apple; provided that Apple did not first commence
an action for patent infringement against You in that instance.
12.2 Effect of Termination. Upon termination, You agree to immediately
stop any further use, reproduction, modification, sublicensing and
distribution of the Covered Code. All sublicenses to the Covered Code
which have been properly granted prior to termination shall survive
any termination of this License. Provisions which, by their nature,
should remain in effect beyond the termination of this License shall
survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
12.2 and 13. No party will be liable to any other for compensation,
indemnity or damages of any sort solely as a result of terminating
this License in accordance with its terms, and termination of this
License will be without prejudice to any other right or remedy of
any party.
13. Miscellaneous.
13.1 Government End Users. The Covered Code is a "commercial item" as
defined in FAR 2.101. Government software and technical data rights in
the Covered Code include only those rights customarily provided to the
public as defined in this License. This customary commercial license
in technical data and software is provided in accordance with FAR
12.211 (Technical Data) and 12.212 (Computer Software) and, for
Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
Commercial Items) and 227.7202-3 (Rights in Commercial Computer
Software or Computer Software Documentation). Accordingly, all U.S.
Government End Users acquire Covered Code with only those rights set
forth herein.
13.2 Relationship of Parties. This License will not be construed as
creating an agency, partnership, joint venture or any other form of
legal association between or among You, Apple or any Contributor, and
You will not represent to the contrary, whether expressly, by
implication, appearance or otherwise.
13.3 Independent Development. Nothing in this License will impair
Apple's right to acquire, license, develop, have others develop for
it, market and/or distribute technology or products that perform the
same or similar functions as, or otherwise compete with,
Modifications, Larger Works, technology or products that You may
develop, produce, market or distribute.
13.4 Waiver; Construction. Failure by Apple or any Contributor to
enforce any provision of this License will not be deemed a waiver of
future enforcement of that or any other provision. Any law or
regulation which provides that the language of a contract shall be
construed against the drafter will not apply to this License.
13.5 Severability. (a) If for any reason a court of competent
jurisdiction finds any provision of this License, or portion thereof,
to be unenforceable, that provision of the License will be enforced to
the maximum extent permissible so as to effect the economic benefits
and intent of the parties, and the remainder of this License will
continue in full force and effect. (b) Notwithstanding the foregoing,
if applicable law prohibits or restricts You from fully and/or
specifically complying with Sections 2 and/or 3 or prevents the
enforceability of either of those Sections, this License will
immediately terminate and You must immediately discontinue any use of
the Covered Code and destroy all copies of it that are in your
possession or control.
13.6 Dispute Resolution. Any litigation or other dispute resolution
between You and Apple relating to this License shall take place in the
Northern District of California, and You and Apple hereby consent to
the personal jurisdiction of, and venue in, the state and federal
courts within that District with respect to this License. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded.
13.7 Entire Agreement; Governing Law. This License constitutes the
entire agreement between the parties with respect to the subject
matter hereof. This License shall be governed by the laws of the
United States and the State of California, except that body of
California law concerning conflicts of law.
Where You are located in the province of Quebec, Canada, the following
clause applies: The parties hereby confirm that they have requested
that this License and all related documents be drafted in English. Les
parties ont exige que le present contrat et tous les documents
connexes soient rediges en anglais.
EXHIBIT A.
"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
Reserved.
This file contains Original Code and/or Modifications of Original Code
as defined in and that are subject to the Apple Public Source License
Version 2.0 (the 'License'). You may not use this file except in
compliance with the License. Please obtain a copy of the License at
http://www.opensource.apple.com/apsl/ and read it before using this
file.
The Original Code and all software distributed under the License are
distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
Please see the License for the specific language governing rights and
limitations under the License."

@ -0,0 +1,802 @@
/*
* Copyright (c) 2009-2010 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* $NetBSD: tree.h,v 1.13 2006/08/27 22:32:38 christos Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _LIBKERN_TREE_H_
#define _LIBKERN_TREE_H_
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define RB_INITIALIZER(root) \
{ NULL }
#define RB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_PLACEHOLDER NULL
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
}
#define RB_COLOR_MASK (uintptr_t)0x1
#define RB_LEFT(elm, field) (elm)->field.rbe_left
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
#define _RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_ROOT(head) (head)->rbh_root
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(name, elm, parent, field) do { \
name##_RB_SETPARENT(elm, parent); \
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
name##_RB_SETCOLOR(elm, RB_RED); \
} while (/*CONSTCOND*/ 0)
#define RB_SET_BLACKRED(name, black, red, field) do { \
name##_RB_SETCOLOR(black, RB_BLACK); \
name##_RB_SETCOLOR(red, RB_RED); \
} while (/*CONSTCOND*/ 0)
#ifndef RB_AUGMENT
#define RB_AUGMENT(x) (void)(x)
#endif
#define RB_ROTATE_LEFT(name, head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
name##_RB_SETPARENT(RB_LEFT(tmp, field),(elm)); \
} \
RB_AUGMENT(elm); \
if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \
if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \
RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \
else \
RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_LEFT(tmp, field) = (elm); \
name##_RB_SETPARENT(elm, (tmp)); \
RB_AUGMENT(tmp); \
if ((name##_RB_GETPARENT(tmp))) \
RB_AUGMENT(name##_RB_GETPARENT(tmp)); \
} while (/*CONSTCOND*/ 0)
#define RB_ROTATE_RIGHT(name, head, elm, tmp, field) do { \
(tmp) = RB_LEFT(elm, field); \
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
name##_RB_SETPARENT(RB_RIGHT(tmp, field), (elm)); \
} \
RB_AUGMENT(elm); \
if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \
if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \
RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \
else \
RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_RIGHT(tmp, field) = (elm); \
name##_RB_SETPARENT(elm, tmp); \
RB_AUGMENT(tmp); \
if ((name##_RB_GETPARENT(tmp))) \
RB_AUGMENT(name##_RB_GETPARENT(tmp)); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define RB_PROTOTYPE(name, type, field, cmp) \
void name##_RB_INSERT_COLOR(struct name *, struct type *); \
void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
struct type *name##_RB_REMOVE(struct name *, struct type *); \
struct type *name##_RB_INSERT(struct name *, struct type *); \
struct type *name##_RB_FIND(struct name *, struct type *); \
struct type *name##_RB_NEXT(struct type *); \
struct type *name##_RB_MINMAX(struct name *, int); \
struct type *name##_RB_GETPARENT(struct type*); \
struct type *name##_RB_SETPARENT(struct type*, struct type*); \
int name##_RB_GETCOLOR(struct type*); \
void name##_RB_SETCOLOR(struct type*,int);
/* Generates prototypes (with storage class) and inline functions */
#define RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \
_sc_ void name##_RB_INSERT_COLOR(struct name *, struct type *); \
_sc_ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \
_sc_ struct type *name##_RB_REMOVE(struct name *, struct type *); \
_sc_ struct type *name##_RB_INSERT(struct name *, struct type *); \
_sc_ struct type *name##_RB_FIND(struct name *, struct type *); \
_sc_ struct type *name##_RB_NEXT(struct type *); \
_sc_ struct type *name##_RB_MINMAX(struct name *, int); \
_sc_ struct type *name##_RB_GETPARENT(struct type*); \
_sc_ struct type *name##_RB_SETPARENT(struct type*, struct type*); \
_sc_ int name##_RB_GETCOLOR(struct type*); \
_sc_ void name##_RB_SETCOLOR(struct type*,int);
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define RB_GENERATE(name, type, field, cmp) \
struct type *name##_RB_GETPARENT(struct type *elm) { \
struct type *parent = _RB_PARENT(elm, field); \
if( parent != NULL) { \
parent = (struct type*)((uintptr_t)parent & ~RB_COLOR_MASK);\
return( (struct type*) ( (parent == (struct type*) RB_PLACEHOLDER) ? NULL: parent));\
} \
return((struct type*)NULL); \
} \
int name##_RB_GETCOLOR(struct type *elm) { \
int color = 0; \
color = (int)((uintptr_t)_RB_PARENT(elm,field) & RB_COLOR_MASK);\
return(color); \
} \
void name##_RB_SETCOLOR(struct type *elm,int color) { \
struct type *parent = name##_RB_GETPARENT(elm); \
if(parent == (struct type*)NULL) \
parent = (struct type*) RB_PLACEHOLDER; \
_RB_PARENT(elm, field) = (struct type*)((uintptr_t)parent | (unsigned int)color);\
} \
struct type *name##_RB_SETPARENT(struct type *elm, struct type *parent) { \
int color = name##_RB_GETCOLOR(elm); \
_RB_PARENT(elm, field) = parent; \
if(color) name##_RB_SETCOLOR(elm, color); \
return(name##_RB_GETPARENT(elm)); \
} \
\
void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = name##_RB_GETPARENT(elm)) != NULL && \
name##_RB_GETCOLOR(parent) == RB_RED) { \
gparent = name##_RB_GETPARENT(parent); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \
name##_RB_SETCOLOR(tmp, RB_BLACK); \
RB_SET_BLACKRED(name, parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_RIGHT(parent, field) == elm) { \
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(name, parent, gparent, field); \
RB_ROTATE_RIGHT(name,head, gparent, tmp, field); \
} else { \
tmp = RB_LEFT(gparent, field); \
if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \
name##_RB_SETCOLOR(tmp, RB_BLACK); \
RB_SET_BLACKRED(name, parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_LEFT(parent, field) == elm) { \
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(name, parent, gparent, field); \
RB_ROTATE_LEFT(name, head, gparent, tmp, field); \
} \
} \
name##_RB_SETCOLOR(head->rbh_root, RB_BLACK); \
} \
\
void \
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || name##_RB_GETCOLOR(elm) == RB_BLACK) && \
elm != RB_ROOT(head)) { \
if (RB_LEFT(parent, field) == elm) { \
tmp = RB_RIGHT(parent, field); \
if (name##_RB_GETCOLOR(tmp) == RB_RED) { \
RB_SET_BLACKRED(name, tmp, parent, field); \
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\
name##_RB_SETCOLOR(tmp, RB_RED); \
elm = parent; \
parent = name##_RB_GETPARENT(elm); \
} else { \
if (RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK) {\
struct type *oleft; \
if ((oleft = RB_LEFT(tmp, field)) \
!= NULL) \
name##_RB_SETCOLOR(oleft, RB_BLACK);\
name##_RB_SETCOLOR(tmp, RB_RED); \
RB_ROTATE_RIGHT(name, head, tmp, oleft, field);\
tmp = RB_RIGHT(parent, field); \
} \
name##_RB_SETCOLOR(tmp, (name##_RB_GETCOLOR(parent)));\
name##_RB_SETCOLOR(parent, RB_BLACK); \
if (RB_RIGHT(tmp, field)) \
name##_RB_SETCOLOR(RB_RIGHT(tmp, field),RB_BLACK);\
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} else { \
tmp = RB_LEFT(parent, field); \
if (name##_RB_GETCOLOR(tmp) == RB_RED) { \
RB_SET_BLACKRED(name, tmp, parent, field); \
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\
name##_RB_SETCOLOR(tmp, RB_RED); \
elm = parent; \
parent = name##_RB_GETPARENT(elm); \
} else { \
if (RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) {\
struct type *oright; \
if ((oright = RB_RIGHT(tmp, field)) \
!= NULL) \
name##_RB_SETCOLOR(oright, RB_BLACK);\
name##_RB_SETCOLOR(tmp, RB_RED); \
RB_ROTATE_LEFT(name, head, tmp, oright, field);\
tmp = RB_LEFT(parent, field); \
} \
name##_RB_SETCOLOR(tmp,(name##_RB_GETCOLOR(parent)));\
name##_RB_SETCOLOR(parent, RB_BLACK); \
if (RB_LEFT(tmp, field)) \
name##_RB_SETCOLOR(RB_LEFT(tmp, field), RB_BLACK);\
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
name##_RB_SETCOLOR(elm, RB_BLACK); \
} \
\
struct type * \
name##_RB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (RB_LEFT(elm, field) == NULL) \
child = RB_RIGHT(elm, field); \
else if (RB_RIGHT(elm, field) == NULL) \
child = RB_LEFT(elm, field); \
else { \
struct type *left; \
elm = RB_RIGHT(elm, field); \
while ((left = RB_LEFT(elm, field)) != NULL) \
elm = left; \
child = RB_RIGHT(elm, field); \
parent = name##_RB_GETPARENT(elm); \
color = name##_RB_GETCOLOR(elm); \
if (child) \
name##_RB_SETPARENT(child, parent); \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
if (name##_RB_GETPARENT(elm) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (name##_RB_GETPARENT(old)) { \
if (RB_LEFT(name##_RB_GETPARENT(old), field) == old)\
RB_LEFT(name##_RB_GETPARENT(old), field) = elm;\
else \
RB_RIGHT(name##_RB_GETPARENT(old), field) = elm;\
RB_AUGMENT(name##_RB_GETPARENT(old)); \
} else \
RB_ROOT(head) = elm; \
name##_RB_SETPARENT(RB_LEFT(old, field), elm); \
if (RB_RIGHT(old, field)) \
name##_RB_SETPARENT(RB_RIGHT(old, field), elm); \
if (parent) { \
left = parent; \
do { \
RB_AUGMENT(left); \
} while ((left = name##_RB_GETPARENT(left)) != NULL); \
} \
goto color; \
} \
parent = name##_RB_GETPARENT(elm); \
color = name##_RB_GETCOLOR(elm); \
if (child) \
name##_RB_SETPARENT(child, parent); \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
color: \
if (color == RB_BLACK) \
name##_RB_REMOVE_COLOR(head, parent, child); \
return (old); \
} \
\
/* Inserts a node into the RB tree */ \
struct type * \
name##_RB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = RB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
RB_SET(name, elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
RB_LEFT(parent, field) = elm; \
else \
RB_RIGHT(parent, field) = elm; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = elm; \
name##_RB_INSERT_COLOR(head, elm); \
return (NULL); \
} \
\
/* Finds the node with the same key as elm */ \
struct type * \
name##_RB_FIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
} \
\
/* ARGSUSED */ \
struct type * \
name##_RB_NEXT(struct type *elm) \
{ \
if (RB_RIGHT(elm, field)) { \
elm = RB_RIGHT(elm, field); \
while (RB_LEFT(elm, field)) \
elm = RB_LEFT(elm, field); \
} else { \
if (name##_RB_GETPARENT(elm) && \
(elm == RB_LEFT(name##_RB_GETPARENT(elm), field))) \
elm = name##_RB_GETPARENT(elm); \
else { \
while (name##_RB_GETPARENT(elm) && \
(elm == RB_RIGHT(name##_RB_GETPARENT(elm), field)))\
elm = name##_RB_GETPARENT(elm); \
elm = name##_RB_GETPARENT(elm); \
} \
} \
return (elm); \
} \
\
struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = RB_LEFT(tmp, field); \
else \
tmp = RB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define RB_PROTOTYPE_PREV(name, type, field, cmp) \
RB_PROTOTYPE(name, type, field, cmp) \
struct type *name##_RB_PREV(struct type *);
#define RB_PROTOTYPE_SC_PREV(_sc_, name, type, field, cmp) \
RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \
_sc_ struct type *name##_RB_PREV(struct type *);
#define RB_GENERATE_PREV(name, type, field, cmp) \
RB_GENERATE(name, type, field, cmp) \
struct type * \
name##_RB_PREV(struct type *elm) \
{ \
if (RB_LEFT(elm, field)) { \
elm = RB_LEFT(elm, field); \
while (RB_RIGHT(elm, field)) \
elm = RB_RIGHT(elm, field); \
} else { \
if (name##_RB_GETPARENT(elm) && \
(elm == RB_RIGHT(name##_RB_GETPARENT(elm), field))) \
elm = name##_RB_GETPARENT(elm); \
else { \
while (name##_RB_GETPARENT(elm) && \
(elm == RB_LEFT(name##_RB_GETPARENT(elm), field)))\
elm = name##_RB_GETPARENT(elm); \
elm = name##_RB_GETPARENT(elm); \
} \
} \
return (elm); \
} \
#define RB_NEGINF -1
#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
#define RB_FOREACH(x, name, head) \
for ((x) = RB_MIN(name, head); \
(x) != NULL; \
(x) = name##_RB_NEXT(x))
#define RB_FOREACH_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_SAFE(x, name, head, y) \
for ((x) = RB_MIN(name, head); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#endif /* _LIBKERN_TREE_H_ */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,222 @@
/*
* Copyright (c) 2000-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1988, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)radix.h 8.2 (Berkeley) 10/31/94
* $FreeBSD: src/sys/net/radix.h,v 1.16.2.1 2000/05/03 19:17:11 wollman Exp $
*/
#ifndef _RADIX_H_
#define _RADIX_H_
#include <sys/appleapiopts.h>
#ifdef PRIVATE
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_RTABLE);
#endif
/*
* Radix search tree node layout.
*/
struct radix_node {
struct radix_mask *rn_mklist; /* list of masks contained in subtree */
struct radix_node *rn_parent; /* parent */
short rn_bit; /* bit offset; -1-index(netmask) */
char rn_bmask; /* node: mask for bit test*/
u_char rn_flags; /* enumerated next */
#define RNF_NORMAL 1 /* leaf contains normal route */
#define RNF_ROOT 2 /* leaf is root leaf for tree */
#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
union {
struct { /* leaf only data: */
caddr_t rn_Key; /* object of search */
caddr_t rn_Mask; /* netmask, if present */
struct radix_node *rn_Dupedkey;
} rn_leaf;
struct { /* node only data: */
int rn_Off; /* where to start compare */
struct radix_node *rn_L;/* progeny */
struct radix_node *rn_R;/* progeny */
} rn_node;
} rn_u;
#ifdef RN_DEBUG
int rn_info;
struct radix_node *rn_twin;
struct radix_node *rn_ybro;
#endif
#if __arm__ && (__BIGGEST_ALIGNMENT__ > 4)
/* For the newer ARMv7k ABI where 64-bit types are 64-bit aligned, but pointers
* are 32-bit:
* Aligned to 64-bit since this is cast to rtentry, which is 64-bit aligned.
*/
} __attribute__ ((aligned(8)));
#else
};
#endif
#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
#define rn_key rn_u.rn_leaf.rn_Key
#define rn_mask rn_u.rn_leaf.rn_Mask
#define rn_offset rn_u.rn_node.rn_Off
#define rn_left rn_u.rn_node.rn_L
#define rn_right rn_u.rn_node.rn_R
/*
* Annotations to tree concerning potential routes applying to subtrees.
*/
struct radix_mask {
short rm_bit; /* bit offset; -1-index(netmask) */
char rm_unused; /* cf. rn_bmask */
u_char rm_flags; /* cf. rn_flags */
struct radix_mask *rm_mklist; /* more masks to try */
union {
caddr_t rmu_mask; /* the mask */
struct radix_node *rmu_leaf; /* for normal routes */
} rm_rmu;
int rm_refs; /* # of references to this struct */
};
#define rm_mask rm_rmu.rmu_mask
#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
#define MKGet(m) {\
if (rn_mkfreelist) {\
m = rn_mkfreelist; \
rn_mkfreelist = (m)->rm_mklist; \
} else \
R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\
#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
typedef int walktree_f_t(struct radix_node *, void *);
typedef int rn_matchf_t(struct radix_node *, void *);
struct radix_node_head {
struct radix_node *rnh_treetop;
int rnh_addrsize; /* permit, but not require fixed keys */
int rnh_pktsize; /* permit, but not require fixed keys */
struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
(void *v, void *mask,
struct radix_node_head *head, struct radix_node nodes[]);
struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
(void *v, void *mask,
struct radix_node_head *head, struct radix_node nodes[]);
struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
(void *v, void *mask, struct radix_node_head *head);
struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
(void *v, void *mask, struct radix_node_head *head);
struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
(void *v, struct radix_node_head *head);
/* locate based on sockaddr and rn_matchf_t() */
struct radix_node *(*rnh_matchaddr_args)
(void *v, struct radix_node_head *head,
rn_matchf_t *f, void *w);
struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
(void *v, void *mask, struct radix_node_head *head);
/* locate based on sockaddr, mask and rn_matchf_t() */
struct radix_node *(*rnh_lookup_args)
(void *v, void *mask, struct radix_node_head *head,
rn_matchf_t *f, void *);
struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
(void *v, struct radix_node_head *head);
int (*rnh_walktree) /* traverse tree */
(struct radix_node_head *head, walktree_f_t *f, void *w);
int (*rnh_walktree_from) /* traverse tree below a */
(struct radix_node_head *head, void *a, void *m,
walktree_f_t *f, void *w);
void (*rnh_close) /* do something when the last ref drops */
(struct radix_node *rn, struct radix_node_head *head);
struct radix_node rnh_nodes[3]; /* empty tree for common case */
int rnh_cnt; /* tree dimension */
};
#ifndef KERNEL
#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n))
#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n))
#define Bzero(p, n) bzero((char *)(p), (int)(n));
#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n)))
#define R_Free(p) free((char *)p);
#else
#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n));
#define R_Malloc(p, t, n) (p = (t) _MALLOC((uint32_t)(n), M_RTABLE, M_WAITOK))
#define R_Free(p) FREE((caddr_t)p, M_RTABLE);
#endif /*KERNEL*/
void rn_init(void);
int rn_inithead(void **, int);
int rn_refines(void *, void *);
struct radix_node
*rn_addmask(void *, int, int),
*rn_addroute(void *, void *, struct radix_node_head *,
struct radix_node [2]),
*rn_delete(void *, void *, struct radix_node_head *),
*rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head),
*rn_lookup_args(void *v_arg, void *m_arg, struct radix_node_head *head,
rn_matchf_t *, void *),
*rn_match(void *, struct radix_node_head *),
*rn_match_args(void *, struct radix_node_head *, rn_matchf_t *, void *);
#endif /* PRIVATE */
#endif /* _RADIX_H_ */

@ -0,0 +1,367 @@
APPLE PUBLIC SOURCE LICENSE
Version 2.0 - August 6, 2003
Please read this License carefully before downloading this software.
By downloading or using this software, you are agreeing to be bound by
the terms of this License. If you do not or cannot agree to the terms
of this License, please do not download or use the software.
1. General; Definitions. This License applies to any program or other
work which Apple Computer, Inc. ("Apple") makes publicly available and
which contains a notice placed by Apple identifying such program or
work as "Original Code" and stating that it is subject to the terms of
this Apple Public Source License version 2.0 ("License"). As used in
this License:
1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
the grantor of rights, (i) claims of patents that are now or hereafter
acquired, owned by or assigned to Apple and (ii) that cover subject
matter contained in the Original Code, but only to the extent
necessary to use, reproduce and/or distribute the Original Code
without infringement; and (b) in the case where You are the grantor of
rights, (i) claims of patents that are now or hereafter acquired,
owned by or assigned to You and (ii) that cover subject matter in Your
Modifications, taken alone or in combination with Original Code.
1.2 "Contributor" means any person or entity that creates or
contributes to the creation of Modifications.
1.3 "Covered Code" means the Original Code, Modifications, the
combination of Original Code and any Modifications, and/or any
respective portions thereof.
1.4 "Externally Deploy" means: (a) to sublicense, distribute or
otherwise make Covered Code available, directly or indirectly, to
anyone other than You; and/or (b) to use Covered Code, alone or as
part of a Larger Work, in any way to provide a service, including but
not limited to delivery of content, through electronic communication
with a client other than You.
1.5 "Larger Work" means a work which combines Covered Code or portions
thereof with code not governed by the terms of this License.
1.6 "Modifications" mean any addition to, deletion from, and/or change
to, the substance and/or structure of the Original Code, any previous
Modifications, the combination of Original Code and any previous
Modifications, and/or any respective portions thereof. When code is
released as a series of files, a Modification is: (a) any addition to
or deletion from the contents of a file containing Covered Code;
and/or (b) any new file or other representation of computer program
statements that contains any part of Covered Code.
1.7 "Original Code" means (a) the Source Code of a program or other
work as originally made available by Apple under this License,
including the Source Code of any updates or upgrades to such programs
or works made available by Apple under this License, and that has been
expressly identified by Apple as such in the header file(s) of such
work; and (b) the object code compiled from such Source Code and
originally made available by Apple under this License.
1.8 "Source Code" means the human readable form of a program or other
work that is suitable for making modifications to it, including all
modules it contains, plus any associated interface definition files,
scripts used to control compilation and installation of an executable
(object code).
1.9 "You" or "Your" means an individual or a legal entity exercising
rights under this License. For legal entities, "You" or "Your"
includes any entity which controls, is controlled by, or is under
common control with, You, where "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of fifty percent
(50%) or more of the outstanding shares or beneficial ownership of
such entity.
2. Permitted Uses; Conditions & Restrictions. Subject to the terms
and conditions of this License, Apple hereby grants You, effective on
the date You accept this License and download the Original Code, a
world-wide, royalty-free, non-exclusive license, to the extent of
Apple's Applicable Patent Rights and copyrights covering the Original
Code, to do the following:
2.1 Unmodified Code. You may use, reproduce, display, perform,
internally distribute within Your organization, and Externally Deploy
verbatim, unmodified copies of the Original Code, for commercial or
non-commercial purposes, provided that in each instance:
(a) You must retain and reproduce in all copies of Original Code the
copyright and other proprietary notices and disclaimers of Apple as
they appear in the Original Code, and keep intact all notices in the
Original Code that refer to this License; and
(b) You must include a copy of this License with every copy of Source
Code of Covered Code and documentation You distribute or Externally
Deploy, and You may not offer or impose any terms on such Source Code
that alter or restrict this License or the recipients' rights
hereunder, except as permitted under Section 6.
2.2 Modified Code. You may modify Covered Code and use, reproduce,
display, perform, internally distribute within Your organization, and
Externally Deploy Your Modifications and Covered Code, for commercial
or non-commercial purposes, provided that in each instance You also
meet all of these conditions:
(a) You must satisfy all the conditions of Section 2.1 with respect to
the Source Code of the Covered Code;
(b) You must duplicate, to the extent it does not already exist, the
notice in Exhibit A in each file of the Source Code of all Your
Modifications, and cause the modified files to carry prominent notices
stating that You changed the files and the date of any change; and
(c) If You Externally Deploy Your Modifications, You must make
Source Code of all Your Externally Deployed Modifications either
available to those to whom You have Externally Deployed Your
Modifications, or publicly available. Source Code of Your Externally
Deployed Modifications must be released under the terms set forth in
this License, including the license grants set forth in Section 3
below, for as long as you Externally Deploy the Covered Code or twelve
(12) months from the date of initial External Deployment, whichever is
longer. You should preferably distribute the Source Code of Your
Externally Deployed Modifications electronically (e.g. download from a
web site).
2.3 Distribution of Executable Versions. In addition, if You
Externally Deploy Covered Code (Original Code and/or Modifications) in
object code, executable form only, You must include a prominent
notice, in the code itself as well as in related documentation,
stating that Source Code of the Covered Code is available under the
terms of this License with information on how and where to obtain such
Source Code.
2.4 Third Party Rights. You expressly acknowledge and agree that
although Apple and each Contributor grants the licenses to their
respective portions of the Covered Code set forth herein, no
assurances are provided by Apple or any Contributor that the Covered
Code does not infringe the patent or other intellectual property
rights of any other entity. Apple and each Contributor disclaim any
liability to You for claims brought by any other entity based on
infringement of intellectual property rights or otherwise. As a
condition to exercising the rights and licenses granted hereunder, You
hereby assume sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party patent
license is required to allow You to distribute the Covered Code, it is
Your responsibility to acquire that license before distributing the
Covered Code.
3. Your Grants. In consideration of, and as a condition to, the
licenses granted to You under this License, You hereby grant to any
person or entity receiving or distributing Covered Code under this
License a non-exclusive, royalty-free, perpetual, irrevocable license,
under Your Applicable Patent Rights and other intellectual property
rights (other than patent) owned or controlled by You, to use,
reproduce, display, perform, modify, sublicense, distribute and
Externally Deploy Your Modifications of the same scope and extent as
Apple's licenses under Sections 2.1 and 2.2 above.
4. Larger Works. You may create a Larger Work by combining Covered
Code with other code not governed by the terms of this License and
distribute the Larger Work as a single product. In each such instance,
You must make sure the requirements of this License are fulfilled for
the Covered Code or any portion thereof.
5. Limitations on Patent License. Except as expressly stated in
Section 2, no other patent rights, express or implied, are granted by
Apple herein. Modifications and/or Larger Works may require additional
patent licenses from Apple which Apple may grant in its sole
discretion.
6. Additional Terms. You may choose to offer, and to charge a fee for,
warranty, support, indemnity or liability obligations and/or other
rights consistent with the scope of the license granted herein
("Additional Terms") to one or more recipients of Covered Code.
However, You may do so only on Your own behalf and as Your sole
responsibility, and not on behalf of Apple or any Contributor. You
must obtain the recipient's agreement that any such Additional Terms
are offered by You alone, and You hereby agree to indemnify, defend
and hold Apple and every Contributor harmless for any liability
incurred by or claims asserted against Apple or such Contributor by
reason of any such Additional Terms.
7. Versions of the License. Apple may publish revised and/or new
versions of this License from time to time. Each version will be given
a distinguishing version number. Once Original Code has been published
under a particular version of this License, You may continue to use it
under the terms of that version. You may also choose to use such
Original Code under the terms of any subsequent version of this
License published by Apple. No one other than Apple has the right to
modify the terms applicable to Covered Code created under this
License.
8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
part pre-release, untested, or not fully tested works. The Covered
Code may contain errors that could cause failures or loss of data, and
may be incomplete or contain inaccuracies. You expressly acknowledge
and agree that use of the Covered Code, or any portion thereof, is at
Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
You acknowledge that the Covered Code is not intended for use in the
operation of nuclear facilities, aircraft navigation, communication
systems, or air traffic control machines in which case the failure of
the Covered Code could lead to death, personal injury, or severe
physical or environmental damage.
9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
TO YOU. In no event shall Apple's total liability to You for all
damages (other than as may be required by applicable law) under this
License exceed the amount of fifty dollars ($50.00).
10. Trademarks. This License does not grant any rights to use the
trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
"QuickTime", "QuickTime Streaming Server" or any other trademarks,
service marks, logos or trade names belonging to Apple (collectively
"Apple Marks") or to any trademark, service mark, logo or trade name
belonging to any Contributor. You agree not to use any Apple Marks in
or as part of the name of products derived from the Original Code or
to endorse or promote products derived from the Original Code other
than as expressly permitted by and in strict compliance at all times
with Apple's third party trademark usage guidelines which are posted
at http://www.apple.com/legal/guidelinesfor3rdparties.html.
11. Ownership. Subject to the licenses granted under this License,
each Contributor retains all rights, title and interest in and to any
Modifications made by such Contributor. Apple retains all rights,
title and interest in and to the Original Code and any Modifications
made by or on behalf of Apple ("Apple Modifications"), and such Apple
Modifications will not be automatically subject to this License. Apple
may, at its sole discretion, choose to license such Apple
Modifications under this License, or on different terms from those
contained in this License or may choose not to license them at all.
12. Termination.
12.1 Termination. This License and the rights granted hereunder will
terminate:
(a) automatically without notice from Apple if You fail to comply with
any term(s) of this License and fail to cure such breach within 30
days of becoming aware of such breach;
(b) immediately in the event of the circumstances described in Section
13.5(b); or
(c) automatically without notice from Apple if You, at any time during
the term of this License, commence an action for patent infringement
against Apple; provided that Apple did not first commence
an action for patent infringement against You in that instance.
12.2 Effect of Termination. Upon termination, You agree to immediately
stop any further use, reproduction, modification, sublicensing and
distribution of the Covered Code. All sublicenses to the Covered Code
which have been properly granted prior to termination shall survive
any termination of this License. Provisions which, by their nature,
should remain in effect beyond the termination of this License shall
survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
12.2 and 13. No party will be liable to any other for compensation,
indemnity or damages of any sort solely as a result of terminating
this License in accordance with its terms, and termination of this
License will be without prejudice to any other right or remedy of
any party.
13. Miscellaneous.
13.1 Government End Users. The Covered Code is a "commercial item" as
defined in FAR 2.101. Government software and technical data rights in
the Covered Code include only those rights customarily provided to the
public as defined in this License. This customary commercial license
in technical data and software is provided in accordance with FAR
12.211 (Technical Data) and 12.212 (Computer Software) and, for
Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
Commercial Items) and 227.7202-3 (Rights in Commercial Computer
Software or Computer Software Documentation). Accordingly, all U.S.
Government End Users acquire Covered Code with only those rights set
forth herein.
13.2 Relationship of Parties. This License will not be construed as
creating an agency, partnership, joint venture or any other form of
legal association between or among You, Apple or any Contributor, and
You will not represent to the contrary, whether expressly, by
implication, appearance or otherwise.
13.3 Independent Development. Nothing in this License will impair
Apple's right to acquire, license, develop, have others develop for
it, market and/or distribute technology or products that perform the
same or similar functions as, or otherwise compete with,
Modifications, Larger Works, technology or products that You may
develop, produce, market or distribute.
13.4 Waiver; Construction. Failure by Apple or any Contributor to
enforce any provision of this License will not be deemed a waiver of
future enforcement of that or any other provision. Any law or
regulation which provides that the language of a contract shall be
construed against the drafter will not apply to this License.
13.5 Severability. (a) If for any reason a court of competent
jurisdiction finds any provision of this License, or portion thereof,
to be unenforceable, that provision of the License will be enforced to
the maximum extent permissible so as to effect the economic benefits
and intent of the parties, and the remainder of this License will
continue in full force and effect. (b) Notwithstanding the foregoing,
if applicable law prohibits or restricts You from fully and/or
specifically complying with Sections 2 and/or 3 or prevents the
enforceability of either of those Sections, this License will
immediately terminate and You must immediately discontinue any use of
the Covered Code and destroy all copies of it that are in your
possession or control.
13.6 Dispute Resolution. Any litigation or other dispute resolution
between You and Apple relating to this License shall take place in the
Northern District of California, and You and Apple hereby consent to
the personal jurisdiction of, and venue in, the state and federal
courts within that District with respect to this License. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded.
13.7 Entire Agreement; Governing Law. This License constitutes the
entire agreement between the parties with respect to the subject
matter hereof. This License shall be governed by the laws of the
United States and the State of California, except that body of
California law concerning conflicts of law.
Where You are located in the province of Quebec, Canada, the following
clause applies: The parties hereby confirm that they have requested
that this License and all related documents be drafted in English. Les
parties ont exige que le present contrat et tous les documents
connexes soient rediges en anglais.
EXHIBIT A.
"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
Reserved.
This file contains Original Code and/or Modifications of Original Code
as defined in and that are subject to the Apple Public Source License
Version 2.0 (the 'License'). You may not use this file except in
compliance with the License. Please obtain a copy of the License at
http://www.opensource.apple.com/apsl/ and read it before using this
file.
The Original Code and all software distributed under the License are
distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
Please see the License for the specific language governing rights and
limitations under the License."

@ -0,0 +1,802 @@
/*
* Copyright (c) 2009-2010 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* $NetBSD: tree.h,v 1.13 2006/08/27 22:32:38 christos Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _LIBKERN_TREE_H_
#define _LIBKERN_TREE_H_
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define RB_INITIALIZER(root) \
{ NULL }
#define RB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_PLACEHOLDER NULL
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
}
#define RB_COLOR_MASK (uintptr_t)0x1
#define RB_LEFT(elm, field) (elm)->field.rbe_left
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
#define _RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_ROOT(head) (head)->rbh_root
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(name, elm, parent, field) do { \
name##_RB_SETPARENT(elm, parent); \
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
name##_RB_SETCOLOR(elm, RB_RED); \
} while (/*CONSTCOND*/ 0)
#define RB_SET_BLACKRED(name, black, red, field) do { \
name##_RB_SETCOLOR(black, RB_BLACK); \
name##_RB_SETCOLOR(red, RB_RED); \
} while (/*CONSTCOND*/ 0)
#ifndef RB_AUGMENT
#define RB_AUGMENT(x) (void)(x)
#endif
#define RB_ROTATE_LEFT(name, head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
name##_RB_SETPARENT(RB_LEFT(tmp, field),(elm)); \
} \
RB_AUGMENT(elm); \
if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \
if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \
RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \
else \
RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_LEFT(tmp, field) = (elm); \
name##_RB_SETPARENT(elm, (tmp)); \
RB_AUGMENT(tmp); \
if ((name##_RB_GETPARENT(tmp))) \
RB_AUGMENT(name##_RB_GETPARENT(tmp)); \
} while (/*CONSTCOND*/ 0)
#define RB_ROTATE_RIGHT(name, head, elm, tmp, field) do { \
(tmp) = RB_LEFT(elm, field); \
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
name##_RB_SETPARENT(RB_RIGHT(tmp, field), (elm)); \
} \
RB_AUGMENT(elm); \
if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \
if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \
RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \
else \
RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_RIGHT(tmp, field) = (elm); \
name##_RB_SETPARENT(elm, tmp); \
RB_AUGMENT(tmp); \
if ((name##_RB_GETPARENT(tmp))) \
RB_AUGMENT(name##_RB_GETPARENT(tmp)); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define RB_PROTOTYPE(name, type, field, cmp) \
void name##_RB_INSERT_COLOR(struct name *, struct type *); \
void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
struct type *name##_RB_REMOVE(struct name *, struct type *); \
struct type *name##_RB_INSERT(struct name *, struct type *); \
struct type *name##_RB_FIND(struct name *, struct type *); \
struct type *name##_RB_NEXT(struct type *); \
struct type *name##_RB_MINMAX(struct name *, int); \
struct type *name##_RB_GETPARENT(struct type*); \
struct type *name##_RB_SETPARENT(struct type*, struct type*); \
int name##_RB_GETCOLOR(struct type*); \
void name##_RB_SETCOLOR(struct type*,int);
/* Generates prototypes (with storage class) and inline functions */
#define RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \
_sc_ void name##_RB_INSERT_COLOR(struct name *, struct type *); \
_sc_ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \
_sc_ struct type *name##_RB_REMOVE(struct name *, struct type *); \
_sc_ struct type *name##_RB_INSERT(struct name *, struct type *); \
_sc_ struct type *name##_RB_FIND(struct name *, struct type *); \
_sc_ struct type *name##_RB_NEXT(struct type *); \
_sc_ struct type *name##_RB_MINMAX(struct name *, int); \
_sc_ struct type *name##_RB_GETPARENT(struct type*); \
_sc_ struct type *name##_RB_SETPARENT(struct type*, struct type*); \
_sc_ int name##_RB_GETCOLOR(struct type*); \
_sc_ void name##_RB_SETCOLOR(struct type*,int);
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define RB_GENERATE(name, type, field, cmp) \
struct type *name##_RB_GETPARENT(struct type *elm) { \
struct type *parent = _RB_PARENT(elm, field); \
if( parent != NULL) { \
parent = (struct type*)((uintptr_t)parent & ~RB_COLOR_MASK);\
return( (struct type*) ( (parent == (struct type*) RB_PLACEHOLDER) ? NULL: parent));\
} \
return((struct type*)NULL); \
} \
int name##_RB_GETCOLOR(struct type *elm) { \
int color = 0; \
color = (int)((uintptr_t)_RB_PARENT(elm,field) & RB_COLOR_MASK);\
return(color); \
} \
void name##_RB_SETCOLOR(struct type *elm,int color) { \
struct type *parent = name##_RB_GETPARENT(elm); \
if(parent == (struct type*)NULL) \
parent = (struct type*) RB_PLACEHOLDER; \
_RB_PARENT(elm, field) = (struct type*)((uintptr_t)parent | (unsigned int)color);\
} \
struct type *name##_RB_SETPARENT(struct type *elm, struct type *parent) { \
int color = name##_RB_GETCOLOR(elm); \
_RB_PARENT(elm, field) = parent; \
if(color) name##_RB_SETCOLOR(elm, color); \
return(name##_RB_GETPARENT(elm)); \
} \
\
void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = name##_RB_GETPARENT(elm)) != NULL && \
name##_RB_GETCOLOR(parent) == RB_RED) { \
gparent = name##_RB_GETPARENT(parent); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \
name##_RB_SETCOLOR(tmp, RB_BLACK); \
RB_SET_BLACKRED(name, parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_RIGHT(parent, field) == elm) { \
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(name, parent, gparent, field); \
RB_ROTATE_RIGHT(name,head, gparent, tmp, field); \
} else { \
tmp = RB_LEFT(gparent, field); \
if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \
name##_RB_SETCOLOR(tmp, RB_BLACK); \
RB_SET_BLACKRED(name, parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_LEFT(parent, field) == elm) { \
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(name, parent, gparent, field); \
RB_ROTATE_LEFT(name, head, gparent, tmp, field); \
} \
} \
name##_RB_SETCOLOR(head->rbh_root, RB_BLACK); \
} \
\
void \
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || name##_RB_GETCOLOR(elm) == RB_BLACK) && \
elm != RB_ROOT(head)) { \
if (RB_LEFT(parent, field) == elm) { \
tmp = RB_RIGHT(parent, field); \
if (name##_RB_GETCOLOR(tmp) == RB_RED) { \
RB_SET_BLACKRED(name, tmp, parent, field); \
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\
name##_RB_SETCOLOR(tmp, RB_RED); \
elm = parent; \
parent = name##_RB_GETPARENT(elm); \
} else { \
if (RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK) {\
struct type *oleft; \
if ((oleft = RB_LEFT(tmp, field)) \
!= NULL) \
name##_RB_SETCOLOR(oleft, RB_BLACK);\
name##_RB_SETCOLOR(tmp, RB_RED); \
RB_ROTATE_RIGHT(name, head, tmp, oleft, field);\
tmp = RB_RIGHT(parent, field); \
} \
name##_RB_SETCOLOR(tmp, (name##_RB_GETCOLOR(parent)));\
name##_RB_SETCOLOR(parent, RB_BLACK); \
if (RB_RIGHT(tmp, field)) \
name##_RB_SETCOLOR(RB_RIGHT(tmp, field),RB_BLACK);\
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} else { \
tmp = RB_LEFT(parent, field); \
if (name##_RB_GETCOLOR(tmp) == RB_RED) { \
RB_SET_BLACKRED(name, tmp, parent, field); \
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\
name##_RB_SETCOLOR(tmp, RB_RED); \
elm = parent; \
parent = name##_RB_GETPARENT(elm); \
} else { \
if (RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) {\
struct type *oright; \
if ((oright = RB_RIGHT(tmp, field)) \
!= NULL) \
name##_RB_SETCOLOR(oright, RB_BLACK);\
name##_RB_SETCOLOR(tmp, RB_RED); \
RB_ROTATE_LEFT(name, head, tmp, oright, field);\
tmp = RB_LEFT(parent, field); \
} \
name##_RB_SETCOLOR(tmp,(name##_RB_GETCOLOR(parent)));\
name##_RB_SETCOLOR(parent, RB_BLACK); \
if (RB_LEFT(tmp, field)) \
name##_RB_SETCOLOR(RB_LEFT(tmp, field), RB_BLACK);\
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
name##_RB_SETCOLOR(elm, RB_BLACK); \
} \
\
struct type * \
name##_RB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (RB_LEFT(elm, field) == NULL) \
child = RB_RIGHT(elm, field); \
else if (RB_RIGHT(elm, field) == NULL) \
child = RB_LEFT(elm, field); \
else { \
struct type *left; \
elm = RB_RIGHT(elm, field); \
while ((left = RB_LEFT(elm, field)) != NULL) \
elm = left; \
child = RB_RIGHT(elm, field); \
parent = name##_RB_GETPARENT(elm); \
color = name##_RB_GETCOLOR(elm); \
if (child) \
name##_RB_SETPARENT(child, parent); \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
if (name##_RB_GETPARENT(elm) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (name##_RB_GETPARENT(old)) { \
if (RB_LEFT(name##_RB_GETPARENT(old), field) == old)\
RB_LEFT(name##_RB_GETPARENT(old), field) = elm;\
else \
RB_RIGHT(name##_RB_GETPARENT(old), field) = elm;\
RB_AUGMENT(name##_RB_GETPARENT(old)); \
} else \
RB_ROOT(head) = elm; \
name##_RB_SETPARENT(RB_LEFT(old, field), elm); \
if (RB_RIGHT(old, field)) \
name##_RB_SETPARENT(RB_RIGHT(old, field), elm); \
if (parent) { \
left = parent; \
do { \
RB_AUGMENT(left); \
} while ((left = name##_RB_GETPARENT(left)) != NULL); \
} \
goto color; \
} \
parent = name##_RB_GETPARENT(elm); \
color = name##_RB_GETCOLOR(elm); \
if (child) \
name##_RB_SETPARENT(child, parent); \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
color: \
if (color == RB_BLACK) \
name##_RB_REMOVE_COLOR(head, parent, child); \
return (old); \
} \
\
/* Inserts a node into the RB tree */ \
struct type * \
name##_RB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = RB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
RB_SET(name, elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
RB_LEFT(parent, field) = elm; \
else \
RB_RIGHT(parent, field) = elm; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = elm; \
name##_RB_INSERT_COLOR(head, elm); \
return (NULL); \
} \
\
/* Finds the node with the same key as elm */ \
struct type * \
name##_RB_FIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
} \
\
/* ARGSUSED */ \
struct type * \
name##_RB_NEXT(struct type *elm) \
{ \
if (RB_RIGHT(elm, field)) { \
elm = RB_RIGHT(elm, field); \
while (RB_LEFT(elm, field)) \
elm = RB_LEFT(elm, field); \
} else { \
if (name##_RB_GETPARENT(elm) && \
(elm == RB_LEFT(name##_RB_GETPARENT(elm), field))) \
elm = name##_RB_GETPARENT(elm); \
else { \
while (name##_RB_GETPARENT(elm) && \
(elm == RB_RIGHT(name##_RB_GETPARENT(elm), field)))\
elm = name##_RB_GETPARENT(elm); \
elm = name##_RB_GETPARENT(elm); \
} \
} \
return (elm); \
} \
\
struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = RB_LEFT(tmp, field); \
else \
tmp = RB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define RB_PROTOTYPE_PREV(name, type, field, cmp) \
RB_PROTOTYPE(name, type, field, cmp) \
struct type *name##_RB_PREV(struct type *);
#define RB_PROTOTYPE_SC_PREV(_sc_, name, type, field, cmp) \
RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \
_sc_ struct type *name##_RB_PREV(struct type *);
#define RB_GENERATE_PREV(name, type, field, cmp) \
RB_GENERATE(name, type, field, cmp) \
struct type * \
name##_RB_PREV(struct type *elm) \
{ \
if (RB_LEFT(elm, field)) { \
elm = RB_LEFT(elm, field); \
while (RB_RIGHT(elm, field)) \
elm = RB_RIGHT(elm, field); \
} else { \
if (name##_RB_GETPARENT(elm) && \
(elm == RB_RIGHT(name##_RB_GETPARENT(elm), field))) \
elm = name##_RB_GETPARENT(elm); \
else { \
while (name##_RB_GETPARENT(elm) && \
(elm == RB_LEFT(name##_RB_GETPARENT(elm), field)))\
elm = name##_RB_GETPARENT(elm); \
elm = name##_RB_GETPARENT(elm); \
} \
} \
return (elm); \
} \
#define RB_NEGINF -1
#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
#define RB_FOREACH(x, name, head) \
for ((x) = RB_MIN(name, head); \
(x) != NULL; \
(x) = name##_RB_NEXT(x))
#define RB_FOREACH_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_SAFE(x, name, head, y) \
for ((x) = RB_MIN(name, head); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#endif /* _LIBKERN_TREE_H_ */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,222 @@
/*
* Copyright (c) 2000-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1988, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)radix.h 8.2 (Berkeley) 10/31/94
* $FreeBSD: src/sys/net/radix.h,v 1.16.2.1 2000/05/03 19:17:11 wollman Exp $
*/
#ifndef _RADIX_H_
#define _RADIX_H_
#include <sys/appleapiopts.h>
#ifdef PRIVATE
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_RTABLE);
#endif
/*
* Radix search tree node layout.
*/
struct radix_node {
struct radix_mask *rn_mklist; /* list of masks contained in subtree */
struct radix_node *rn_parent; /* parent */
short rn_bit; /* bit offset; -1-index(netmask) */
char rn_bmask; /* node: mask for bit test*/
u_char rn_flags; /* enumerated next */
#define RNF_NORMAL 1 /* leaf contains normal route */
#define RNF_ROOT 2 /* leaf is root leaf for tree */
#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
union {
struct { /* leaf only data: */
caddr_t rn_Key; /* object of search */
caddr_t rn_Mask; /* netmask, if present */
struct radix_node *rn_Dupedkey;
} rn_leaf;
struct { /* node only data: */
int rn_Off; /* where to start compare */
struct radix_node *rn_L;/* progeny */
struct radix_node *rn_R;/* progeny */
} rn_node;
} rn_u;
#ifdef RN_DEBUG
int rn_info;
struct radix_node *rn_twin;
struct radix_node *rn_ybro;
#endif
#if __arm__ && (__BIGGEST_ALIGNMENT__ > 4)
/* For the newer ARMv7k ABI where 64-bit types are 64-bit aligned, but pointers
* are 32-bit:
* Aligned to 64-bit since this is cast to rtentry, which is 64-bit aligned.
*/
} __attribute__ ((aligned(8)));
#else
};
#endif
#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
#define rn_key rn_u.rn_leaf.rn_Key
#define rn_mask rn_u.rn_leaf.rn_Mask
#define rn_offset rn_u.rn_node.rn_Off
#define rn_left rn_u.rn_node.rn_L
#define rn_right rn_u.rn_node.rn_R
/*
* Annotations to tree concerning potential routes applying to subtrees.
*/
struct radix_mask {
short rm_bit; /* bit offset; -1-index(netmask) */
char rm_unused; /* cf. rn_bmask */
u_char rm_flags; /* cf. rn_flags */
struct radix_mask *rm_mklist; /* more masks to try */
union {
caddr_t rmu_mask; /* the mask */
struct radix_node *rmu_leaf; /* for normal routes */
} rm_rmu;
int rm_refs; /* # of references to this struct */
};
#define rm_mask rm_rmu.rmu_mask
#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
#define MKGet(m) {\
if (rn_mkfreelist) {\
m = rn_mkfreelist; \
rn_mkfreelist = (m)->rm_mklist; \
} else \
R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\
#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
typedef int walktree_f_t(struct radix_node *, void *);
typedef int rn_matchf_t(struct radix_node *, void *);
struct radix_node_head {
struct radix_node *rnh_treetop;
int rnh_addrsize; /* permit, but not require fixed keys */
int rnh_pktsize; /* permit, but not require fixed keys */
struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
(void *v, void *mask,
struct radix_node_head *head, struct radix_node nodes[]);
struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
(void *v, void *mask,
struct radix_node_head *head, struct radix_node nodes[]);
struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
(void *v, void *mask, struct radix_node_head *head);
struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
(void *v, void *mask, struct radix_node_head *head);
struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
(void *v, struct radix_node_head *head);
/* locate based on sockaddr and rn_matchf_t() */
struct radix_node *(*rnh_matchaddr_args)
(void *v, struct radix_node_head *head,
rn_matchf_t *f, void *w);
struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
(void *v, void *mask, struct radix_node_head *head);
/* locate based on sockaddr, mask and rn_matchf_t() */
struct radix_node *(*rnh_lookup_args)
(void *v, void *mask, struct radix_node_head *head,
rn_matchf_t *f, void *);
struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
(void *v, struct radix_node_head *head);
int (*rnh_walktree) /* traverse tree */
(struct radix_node_head *head, walktree_f_t *f, void *w);
int (*rnh_walktree_from) /* traverse tree below a */
(struct radix_node_head *head, void *a, void *m,
walktree_f_t *f, void *w);
void (*rnh_close) /* do something when the last ref drops */
(struct radix_node *rn, struct radix_node_head *head);
struct radix_node rnh_nodes[3]; /* empty tree for common case */
int rnh_cnt; /* tree dimension */
};
#ifndef KERNEL
#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n))
#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n))
#define Bzero(p, n) bzero((char *)(p), (int)(n));
#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n)))
#define R_Free(p) free((char *)p);
#else
#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n));
#define R_Malloc(p, t, n) (p = (t) _MALLOC((uint32_t)(n), M_RTABLE, M_WAITOK))
#define R_Free(p) FREE((caddr_t)p, M_RTABLE);
#endif /*KERNEL*/
void rn_init(void);
int rn_inithead(void **, int);
int rn_refines(void *, void *);
struct radix_node
*rn_addmask(void *, int, int),
*rn_addroute(void *, void *, struct radix_node_head *,
struct radix_node [2]),
*rn_delete(void *, void *, struct radix_node_head *),
*rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head),
*rn_lookup_args(void *v_arg, void *m_arg, struct radix_node_head *head,
rn_matchf_t *, void *),
*rn_match(void *, struct radix_node_head *),
*rn_match_args(void *, struct radix_node_head *, rn_matchf_t *, void *);
#endif /* PRIVATE */
#endif /* _RADIX_H_ */

@ -0,0 +1,367 @@
APPLE PUBLIC SOURCE LICENSE
Version 2.0 - August 6, 2003
Please read this License carefully before downloading this software.
By downloading or using this software, you are agreeing to be bound by
the terms of this License. If you do not or cannot agree to the terms
of this License, please do not download or use the software.
1. General; Definitions. This License applies to any program or other
work which Apple Computer, Inc. ("Apple") makes publicly available and
which contains a notice placed by Apple identifying such program or
work as "Original Code" and stating that it is subject to the terms of
this Apple Public Source License version 2.0 ("License"). As used in
this License:
1.1 "Applicable Patent Rights" mean: (a) in the case where Apple is
the grantor of rights, (i) claims of patents that are now or hereafter
acquired, owned by or assigned to Apple and (ii) that cover subject
matter contained in the Original Code, but only to the extent
necessary to use, reproduce and/or distribute the Original Code
without infringement; and (b) in the case where You are the grantor of
rights, (i) claims of patents that are now or hereafter acquired,
owned by or assigned to You and (ii) that cover subject matter in Your
Modifications, taken alone or in combination with Original Code.
1.2 "Contributor" means any person or entity that creates or
contributes to the creation of Modifications.
1.3 "Covered Code" means the Original Code, Modifications, the
combination of Original Code and any Modifications, and/or any
respective portions thereof.
1.4 "Externally Deploy" means: (a) to sublicense, distribute or
otherwise make Covered Code available, directly or indirectly, to
anyone other than You; and/or (b) to use Covered Code, alone or as
part of a Larger Work, in any way to provide a service, including but
not limited to delivery of content, through electronic communication
with a client other than You.
1.5 "Larger Work" means a work which combines Covered Code or portions
thereof with code not governed by the terms of this License.
1.6 "Modifications" mean any addition to, deletion from, and/or change
to, the substance and/or structure of the Original Code, any previous
Modifications, the combination of Original Code and any previous
Modifications, and/or any respective portions thereof. When code is
released as a series of files, a Modification is: (a) any addition to
or deletion from the contents of a file containing Covered Code;
and/or (b) any new file or other representation of computer program
statements that contains any part of Covered Code.
1.7 "Original Code" means (a) the Source Code of a program or other
work as originally made available by Apple under this License,
including the Source Code of any updates or upgrades to such programs
or works made available by Apple under this License, and that has been
expressly identified by Apple as such in the header file(s) of such
work; and (b) the object code compiled from such Source Code and
originally made available by Apple under this License.
1.8 "Source Code" means the human readable form of a program or other
work that is suitable for making modifications to it, including all
modules it contains, plus any associated interface definition files,
scripts used to control compilation and installation of an executable
(object code).
1.9 "You" or "Your" means an individual or a legal entity exercising
rights under this License. For legal entities, "You" or "Your"
includes any entity which controls, is controlled by, or is under
common control with, You, where "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of fifty percent
(50%) or more of the outstanding shares or beneficial ownership of
such entity.
2. Permitted Uses; Conditions & Restrictions. Subject to the terms
and conditions of this License, Apple hereby grants You, effective on
the date You accept this License and download the Original Code, a
world-wide, royalty-free, non-exclusive license, to the extent of
Apple's Applicable Patent Rights and copyrights covering the Original
Code, to do the following:
2.1 Unmodified Code. You may use, reproduce, display, perform,
internally distribute within Your organization, and Externally Deploy
verbatim, unmodified copies of the Original Code, for commercial or
non-commercial purposes, provided that in each instance:
(a) You must retain and reproduce in all copies of Original Code the
copyright and other proprietary notices and disclaimers of Apple as
they appear in the Original Code, and keep intact all notices in the
Original Code that refer to this License; and
(b) You must include a copy of this License with every copy of Source
Code of Covered Code and documentation You distribute or Externally
Deploy, and You may not offer or impose any terms on such Source Code
that alter or restrict this License or the recipients' rights
hereunder, except as permitted under Section 6.
2.2 Modified Code. You may modify Covered Code and use, reproduce,
display, perform, internally distribute within Your organization, and
Externally Deploy Your Modifications and Covered Code, for commercial
or non-commercial purposes, provided that in each instance You also
meet all of these conditions:
(a) You must satisfy all the conditions of Section 2.1 with respect to
the Source Code of the Covered Code;
(b) You must duplicate, to the extent it does not already exist, the
notice in Exhibit A in each file of the Source Code of all Your
Modifications, and cause the modified files to carry prominent notices
stating that You changed the files and the date of any change; and
(c) If You Externally Deploy Your Modifications, You must make
Source Code of all Your Externally Deployed Modifications either
available to those to whom You have Externally Deployed Your
Modifications, or publicly available. Source Code of Your Externally
Deployed Modifications must be released under the terms set forth in
this License, including the license grants set forth in Section 3
below, for as long as you Externally Deploy the Covered Code or twelve
(12) months from the date of initial External Deployment, whichever is
longer. You should preferably distribute the Source Code of Your
Externally Deployed Modifications electronically (e.g. download from a
web site).
2.3 Distribution of Executable Versions. In addition, if You
Externally Deploy Covered Code (Original Code and/or Modifications) in
object code, executable form only, You must include a prominent
notice, in the code itself as well as in related documentation,
stating that Source Code of the Covered Code is available under the
terms of this License with information on how and where to obtain such
Source Code.
2.4 Third Party Rights. You expressly acknowledge and agree that
although Apple and each Contributor grants the licenses to their
respective portions of the Covered Code set forth herein, no
assurances are provided by Apple or any Contributor that the Covered
Code does not infringe the patent or other intellectual property
rights of any other entity. Apple and each Contributor disclaim any
liability to You for claims brought by any other entity based on
infringement of intellectual property rights or otherwise. As a
condition to exercising the rights and licenses granted hereunder, You
hereby assume sole responsibility to secure any other intellectual
property rights needed, if any. For example, if a third party patent
license is required to allow You to distribute the Covered Code, it is
Your responsibility to acquire that license before distributing the
Covered Code.
3. Your Grants. In consideration of, and as a condition to, the
licenses granted to You under this License, You hereby grant to any
person or entity receiving or distributing Covered Code under this
License a non-exclusive, royalty-free, perpetual, irrevocable license,
under Your Applicable Patent Rights and other intellectual property
rights (other than patent) owned or controlled by You, to use,
reproduce, display, perform, modify, sublicense, distribute and
Externally Deploy Your Modifications of the same scope and extent as
Apple's licenses under Sections 2.1 and 2.2 above.
4. Larger Works. You may create a Larger Work by combining Covered
Code with other code not governed by the terms of this License and
distribute the Larger Work as a single product. In each such instance,
You must make sure the requirements of this License are fulfilled for
the Covered Code or any portion thereof.
5. Limitations on Patent License. Except as expressly stated in
Section 2, no other patent rights, express or implied, are granted by
Apple herein. Modifications and/or Larger Works may require additional
patent licenses from Apple which Apple may grant in its sole
discretion.
6. Additional Terms. You may choose to offer, and to charge a fee for,
warranty, support, indemnity or liability obligations and/or other
rights consistent with the scope of the license granted herein
("Additional Terms") to one or more recipients of Covered Code.
However, You may do so only on Your own behalf and as Your sole
responsibility, and not on behalf of Apple or any Contributor. You
must obtain the recipient's agreement that any such Additional Terms
are offered by You alone, and You hereby agree to indemnify, defend
and hold Apple and every Contributor harmless for any liability
incurred by or claims asserted against Apple or such Contributor by
reason of any such Additional Terms.
7. Versions of the License. Apple may publish revised and/or new
versions of this License from time to time. Each version will be given
a distinguishing version number. Once Original Code has been published
under a particular version of this License, You may continue to use it
under the terms of that version. You may also choose to use such
Original Code under the terms of any subsequent version of this
License published by Apple. No one other than Apple has the right to
modify the terms applicable to Covered Code created under this
License.
8. NO WARRANTY OR SUPPORT. The Covered Code may contain in whole or in
part pre-release, untested, or not fully tested works. The Covered
Code may contain errors that could cause failures or loss of data, and
may be incomplete or contain inaccuracies. You expressly acknowledge
and agree that use of the Covered Code, or any portion thereof, is at
Your sole and entire risk. THE COVERED CODE IS PROVIDED "AS IS" AND
WITHOUT WARRANTY, UPGRADES OR SUPPORT OF ANY KIND AND APPLE AND
APPLE'S LICENSOR(S) (COLLECTIVELY REFERRED TO AS "APPLE" FOR THE
PURPOSES OF SECTIONS 8 AND 9) AND ALL CONTRIBUTORS EXPRESSLY DISCLAIM
ALL WARRANTIES AND/OR CONDITIONS, EXPRESS OR IMPLIED, INCLUDING, BUT
NOT LIMITED TO, THE IMPLIED WARRANTIES AND/OR CONDITIONS OF
MERCHANTABILITY, OF SATISFACTORY QUALITY, OF FITNESS FOR A PARTICULAR
PURPOSE, OF ACCURACY, OF QUIET ENJOYMENT, AND NONINFRINGEMENT OF THIRD
PARTY RIGHTS. APPLE AND EACH CONTRIBUTOR DOES NOT WARRANT AGAINST
INTERFERENCE WITH YOUR ENJOYMENT OF THE COVERED CODE, THAT THE
FUNCTIONS CONTAINED IN THE COVERED CODE WILL MEET YOUR REQUIREMENTS,
THAT THE OPERATION OF THE COVERED CODE WILL BE UNINTERRUPTED OR
ERROR-FREE, OR THAT DEFECTS IN THE COVERED CODE WILL BE CORRECTED. NO
ORAL OR WRITTEN INFORMATION OR ADVICE GIVEN BY APPLE, AN APPLE
AUTHORIZED REPRESENTATIVE OR ANY CONTRIBUTOR SHALL CREATE A WARRANTY.
You acknowledge that the Covered Code is not intended for use in the
operation of nuclear facilities, aircraft navigation, communication
systems, or air traffic control machines in which case the failure of
the Covered Code could lead to death, personal injury, or severe
physical or environmental damage.
9. LIMITATION OF LIABILITY. TO THE EXTENT NOT PROHIBITED BY LAW, IN NO
EVENT SHALL APPLE OR ANY CONTRIBUTOR BE LIABLE FOR ANY INCIDENTAL,
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING
TO THIS LICENSE OR YOUR USE OR INABILITY TO USE THE COVERED CODE, OR
ANY PORTION THEREOF, WHETHER UNDER A THEORY OF CONTRACT, WARRANTY,
TORT (INCLUDING NEGLIGENCE), PRODUCTS LIABILITY OR OTHERWISE, EVEN IF
APPLE OR SUCH CONTRIBUTOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES AND NOTWITHSTANDING THE FAILURE OF ESSENTIAL PURPOSE OF ANY
REMEDY. SOME JURISDICTIONS DO NOT ALLOW THE LIMITATION OF LIABILITY OF
INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS LIMITATION MAY NOT APPLY
TO YOU. In no event shall Apple's total liability to You for all
damages (other than as may be required by applicable law) under this
License exceed the amount of fifty dollars ($50.00).
10. Trademarks. This License does not grant any rights to use the
trademarks or trade names "Apple", "Apple Computer", "Mac", "Mac OS",
"QuickTime", "QuickTime Streaming Server" or any other trademarks,
service marks, logos or trade names belonging to Apple (collectively
"Apple Marks") or to any trademark, service mark, logo or trade name
belonging to any Contributor. You agree not to use any Apple Marks in
or as part of the name of products derived from the Original Code or
to endorse or promote products derived from the Original Code other
than as expressly permitted by and in strict compliance at all times
with Apple's third party trademark usage guidelines which are posted
at http://www.apple.com/legal/guidelinesfor3rdparties.html.
11. Ownership. Subject to the licenses granted under this License,
each Contributor retains all rights, title and interest in and to any
Modifications made by such Contributor. Apple retains all rights,
title and interest in and to the Original Code and any Modifications
made by or on behalf of Apple ("Apple Modifications"), and such Apple
Modifications will not be automatically subject to this License. Apple
may, at its sole discretion, choose to license such Apple
Modifications under this License, or on different terms from those
contained in this License or may choose not to license them at all.
12. Termination.
12.1 Termination. This License and the rights granted hereunder will
terminate:
(a) automatically without notice from Apple if You fail to comply with
any term(s) of this License and fail to cure such breach within 30
days of becoming aware of such breach;
(b) immediately in the event of the circumstances described in Section
13.5(b); or
(c) automatically without notice from Apple if You, at any time during
the term of this License, commence an action for patent infringement
against Apple; provided that Apple did not first commence
an action for patent infringement against You in that instance.
12.2 Effect of Termination. Upon termination, You agree to immediately
stop any further use, reproduction, modification, sublicensing and
distribution of the Covered Code. All sublicenses to the Covered Code
which have been properly granted prior to termination shall survive
any termination of this License. Provisions which, by their nature,
should remain in effect beyond the termination of this License shall
survive, including but not limited to Sections 3, 5, 8, 9, 10, 11,
12.2 and 13. No party will be liable to any other for compensation,
indemnity or damages of any sort solely as a result of terminating
this License in accordance with its terms, and termination of this
License will be without prejudice to any other right or remedy of
any party.
13. Miscellaneous.
13.1 Government End Users. The Covered Code is a "commercial item" as
defined in FAR 2.101. Government software and technical data rights in
the Covered Code include only those rights customarily provided to the
public as defined in this License. This customary commercial license
in technical data and software is provided in accordance with FAR
12.211 (Technical Data) and 12.212 (Computer Software) and, for
Department of Defense purchases, DFAR 252.227-7015 (Technical Data --
Commercial Items) and 227.7202-3 (Rights in Commercial Computer
Software or Computer Software Documentation). Accordingly, all U.S.
Government End Users acquire Covered Code with only those rights set
forth herein.
13.2 Relationship of Parties. This License will not be construed as
creating an agency, partnership, joint venture or any other form of
legal association between or among You, Apple or any Contributor, and
You will not represent to the contrary, whether expressly, by
implication, appearance or otherwise.
13.3 Independent Development. Nothing in this License will impair
Apple's right to acquire, license, develop, have others develop for
it, market and/or distribute technology or products that perform the
same or similar functions as, or otherwise compete with,
Modifications, Larger Works, technology or products that You may
develop, produce, market or distribute.
13.4 Waiver; Construction. Failure by Apple or any Contributor to
enforce any provision of this License will not be deemed a waiver of
future enforcement of that or any other provision. Any law or
regulation which provides that the language of a contract shall be
construed against the drafter will not apply to this License.
13.5 Severability. (a) If for any reason a court of competent
jurisdiction finds any provision of this License, or portion thereof,
to be unenforceable, that provision of the License will be enforced to
the maximum extent permissible so as to effect the economic benefits
and intent of the parties, and the remainder of this License will
continue in full force and effect. (b) Notwithstanding the foregoing,
if applicable law prohibits or restricts You from fully and/or
specifically complying with Sections 2 and/or 3 or prevents the
enforceability of either of those Sections, this License will
immediately terminate and You must immediately discontinue any use of
the Covered Code and destroy all copies of it that are in your
possession or control.
13.6 Dispute Resolution. Any litigation or other dispute resolution
between You and Apple relating to this License shall take place in the
Northern District of California, and You and Apple hereby consent to
the personal jurisdiction of, and venue in, the state and federal
courts within that District with respect to this License. The
application of the United Nations Convention on Contracts for the
International Sale of Goods is expressly excluded.
13.7 Entire Agreement; Governing Law. This License constitutes the
entire agreement between the parties with respect to the subject
matter hereof. This License shall be governed by the laws of the
United States and the State of California, except that body of
California law concerning conflicts of law.
Where You are located in the province of Quebec, Canada, the following
clause applies: The parties hereby confirm that they have requested
that this License and all related documents be drafted in English. Les
parties ont exige que le present contrat et tous les documents
connexes soient rediges en anglais.
EXHIBIT A.
"Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights
Reserved.
This file contains Original Code and/or Modifications of Original Code
as defined in and that are subject to the Apple Public Source License
Version 2.0 (the 'License'). You may not use this file except in
compliance with the License. Please obtain a copy of the License at
http://www.opensource.apple.com/apsl/ and read it before using this
file.
The Original Code and all software distributed under the License are
distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
Please see the License for the specific language governing rights and
limitations under the License."

@ -0,0 +1,802 @@
/*
* Copyright (c) 2009-2010 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/* $NetBSD: tree.h,v 1.13 2006/08/27 22:32:38 christos Exp $ */
/* $OpenBSD: tree.h,v 1.7 2002/10/17 21:51:54 art Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _LIBKERN_TREE_H_
#define _LIBKERN_TREE_H_
/*
* This file defines data structures for different types of trees:
* splay trees and red-black trees.
*
* A splay tree is a self-organizing data structure. Every operation
* on the tree causes a splay to happen. The splay moves the requested
* node to the root of the tree and partly rebalances it.
*
* This has the benefit that request locality causes faster lookups as
* the requested nodes move to the top of the tree. On the other hand,
* every lookup causes memory writes.
*
* The Balance Theorem bounds the total access time for m operations
* and n inserts on an initially empty tree as O((m + n)lg n). The
* amortized cost for a sequence of m accesses to a splay tree is O(lg n);
*
* A red-black tree is a binary search tree with the node color as an
* extra attribute. It fulfills a set of conditions:
* - every search path from the root to a leaf consists of the
* same number of black nodes,
* - each red node (except for the root) has a black parent,
* - each leaf node is black.
*
* Every operation on a red-black tree is bounded as O(lg n).
* The maximum height of a red-black tree is 2lg (n+1).
*/
#define SPLAY_HEAD(name, type) \
struct name { \
struct type *sph_root; /* root of the tree */ \
}
#define SPLAY_INITIALIZER(root) \
{ NULL }
#define SPLAY_INIT(root) do { \
(root)->sph_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ENTRY(type) \
struct { \
struct type *spe_left; /* left element */ \
struct type *spe_right; /* right element */ \
}
#define SPLAY_LEFT(elm, field) (elm)->field.spe_left
#define SPLAY_RIGHT(elm, field) (elm)->field.spe_right
#define SPLAY_ROOT(head) (head)->sph_root
#define SPLAY_EMPTY(head) (SPLAY_ROOT(head) == NULL)
/* SPLAY_ROTATE_{LEFT,RIGHT} expect that tmp hold SPLAY_{RIGHT,LEFT} */
#define SPLAY_ROTATE_RIGHT(head, tmp, field) do { \
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(tmp, field); \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ROTATE_LEFT(head, tmp, field) do { \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(tmp, field); \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
(head)->sph_root = tmp; \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKLEFT(head, tmp, field) do { \
SPLAY_LEFT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_LINKRIGHT(head, tmp, field) do { \
SPLAY_RIGHT(tmp, field) = (head)->sph_root; \
tmp = (head)->sph_root; \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field); \
} while (/*CONSTCOND*/ 0)
#define SPLAY_ASSEMBLE(head, node, left, right, field) do { \
SPLAY_RIGHT(left, field) = SPLAY_LEFT((head)->sph_root, field); \
SPLAY_LEFT(right, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT((head)->sph_root, field) = SPLAY_RIGHT(node, field); \
SPLAY_RIGHT((head)->sph_root, field) = SPLAY_LEFT(node, field); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define SPLAY_PROTOTYPE(name, type, field, cmp) \
void name##_SPLAY(struct name *, struct type *); \
void name##_SPLAY_MINMAX(struct name *, int); \
struct type *name##_SPLAY_INSERT(struct name *, struct type *); \
struct type *name##_SPLAY_REMOVE(struct name *, struct type *); \
\
/* Finds the node with the same key as elm */ \
static __inline struct type * \
name##_SPLAY_FIND(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) \
return(NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) \
return (head->sph_root); \
return (NULL); \
} \
\
static __inline struct type * \
name##_SPLAY_NEXT(struct name *head, struct type *elm) \
{ \
name##_SPLAY(head, elm); \
if (SPLAY_RIGHT(elm, field) != NULL) { \
elm = SPLAY_RIGHT(elm, field); \
while (SPLAY_LEFT(elm, field) != NULL) { \
elm = SPLAY_LEFT(elm, field); \
} \
} else \
elm = NULL; \
return (elm); \
} \
\
static __inline struct type * \
name##_SPLAY_MIN_MAX(struct name *head, int val) \
{ \
name##_SPLAY_MINMAX(head, val); \
return (SPLAY_ROOT(head)); \
}
/* Main splay operation.
* Moves node close to the key of elm to top
*/
#define SPLAY_GENERATE(name, type, field, cmp) \
struct type * \
name##_SPLAY_INSERT(struct name *head, struct type *elm) \
{ \
if (SPLAY_EMPTY(head)) { \
SPLAY_LEFT(elm, field) = SPLAY_RIGHT(elm, field) = NULL; \
} else { \
int __comp; \
name##_SPLAY(head, elm); \
__comp = (cmp)(elm, (head)->sph_root); \
if(__comp < 0) { \
SPLAY_LEFT(elm, field) = SPLAY_LEFT((head)->sph_root, field);\
SPLAY_RIGHT(elm, field) = (head)->sph_root; \
SPLAY_LEFT((head)->sph_root, field) = NULL; \
} else if (__comp > 0) { \
SPLAY_RIGHT(elm, field) = SPLAY_RIGHT((head)->sph_root, field);\
SPLAY_LEFT(elm, field) = (head)->sph_root; \
SPLAY_RIGHT((head)->sph_root, field) = NULL; \
} else \
return ((head)->sph_root); \
} \
(head)->sph_root = (elm); \
return (NULL); \
} \
\
struct type * \
name##_SPLAY_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *__tmp; \
if (SPLAY_EMPTY(head)) \
return (NULL); \
name##_SPLAY(head, elm); \
if ((cmp)(elm, (head)->sph_root) == 0) { \
if (SPLAY_LEFT((head)->sph_root, field) == NULL) { \
(head)->sph_root = SPLAY_RIGHT((head)->sph_root, field);\
} else { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
(head)->sph_root = SPLAY_LEFT((head)->sph_root, field);\
name##_SPLAY(head, elm); \
SPLAY_RIGHT((head)->sph_root, field) = __tmp; \
} \
return (elm); \
} \
return (NULL); \
} \
\
void \
name##_SPLAY(struct name *head, struct type *elm) \
{ \
struct type __node, *__left, *__right, *__tmp; \
int __comp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while ((__comp = (cmp)(elm, (head)->sph_root)) != 0) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if ((cmp)(elm, __tmp) > 0){ \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
} \
\
/* Splay with either the minimum or the maximum element \
* Used to find minimum or maximum element in tree. \
*/ \
void name##_SPLAY_MINMAX(struct name *head, int __comp) \
{ \
struct type __node, *__left, *__right, *__tmp; \
\
SPLAY_LEFT(&__node, field) = SPLAY_RIGHT(&__node, field) = NULL;\
__left = __right = &__node; \
\
while (1) { \
if (__comp < 0) { \
__tmp = SPLAY_LEFT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp < 0){ \
SPLAY_ROTATE_RIGHT(head, __tmp, field); \
if (SPLAY_LEFT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKLEFT(head, __right, field); \
} else if (__comp > 0) { \
__tmp = SPLAY_RIGHT((head)->sph_root, field); \
if (__tmp == NULL) \
break; \
if (__comp > 0) { \
SPLAY_ROTATE_LEFT(head, __tmp, field); \
if (SPLAY_RIGHT((head)->sph_root, field) == NULL)\
break; \
} \
SPLAY_LINKRIGHT(head, __left, field); \
} \
} \
SPLAY_ASSEMBLE(head, &__node, __left, __right, field); \
}
#define SPLAY_NEGINF -1
#define SPLAY_INF 1
#define SPLAY_INSERT(name, x, y) name##_SPLAY_INSERT(x, y)
#define SPLAY_REMOVE(name, x, y) name##_SPLAY_REMOVE(x, y)
#define SPLAY_FIND(name, x, y) name##_SPLAY_FIND(x, y)
#define SPLAY_NEXT(name, x, y) name##_SPLAY_NEXT(x, y)
#define SPLAY_MIN(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_NEGINF))
#define SPLAY_MAX(name, x) (SPLAY_EMPTY(x) ? NULL \
: name##_SPLAY_MIN_MAX(x, SPLAY_INF))
#define SPLAY_FOREACH(x, name, head) \
for ((x) = SPLAY_MIN(name, head); \
(x) != NULL; \
(x) = SPLAY_NEXT(name, head, x))
/* Macros that define a red-black tree */
#define RB_HEAD(name, type) \
struct name { \
struct type *rbh_root; /* root of the tree */ \
}
#define RB_INITIALIZER(root) \
{ NULL }
#define RB_INIT(root) do { \
(root)->rbh_root = NULL; \
} while (/*CONSTCOND*/ 0)
#define RB_BLACK 0
#define RB_RED 1
#define RB_PLACEHOLDER NULL
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
}
#define RB_COLOR_MASK (uintptr_t)0x1
#define RB_LEFT(elm, field) (elm)->field.rbe_left
#define RB_RIGHT(elm, field) (elm)->field.rbe_right
#define _RB_PARENT(elm, field) (elm)->field.rbe_parent
#define RB_ROOT(head) (head)->rbh_root
#define RB_EMPTY(head) (RB_ROOT(head) == NULL)
#define RB_SET(name, elm, parent, field) do { \
name##_RB_SETPARENT(elm, parent); \
RB_LEFT(elm, field) = RB_RIGHT(elm, field) = NULL; \
name##_RB_SETCOLOR(elm, RB_RED); \
} while (/*CONSTCOND*/ 0)
#define RB_SET_BLACKRED(name, black, red, field) do { \
name##_RB_SETCOLOR(black, RB_BLACK); \
name##_RB_SETCOLOR(red, RB_RED); \
} while (/*CONSTCOND*/ 0)
#ifndef RB_AUGMENT
#define RB_AUGMENT(x) (void)(x)
#endif
#define RB_ROTATE_LEFT(name, head, elm, tmp, field) do { \
(tmp) = RB_RIGHT(elm, field); \
if ((RB_RIGHT(elm, field) = RB_LEFT(tmp, field)) != NULL) { \
name##_RB_SETPARENT(RB_LEFT(tmp, field),(elm)); \
} \
RB_AUGMENT(elm); \
if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \
if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \
RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \
else \
RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_LEFT(tmp, field) = (elm); \
name##_RB_SETPARENT(elm, (tmp)); \
RB_AUGMENT(tmp); \
if ((name##_RB_GETPARENT(tmp))) \
RB_AUGMENT(name##_RB_GETPARENT(tmp)); \
} while (/*CONSTCOND*/ 0)
#define RB_ROTATE_RIGHT(name, head, elm, tmp, field) do { \
(tmp) = RB_LEFT(elm, field); \
if ((RB_LEFT(elm, field) = RB_RIGHT(tmp, field)) != NULL) { \
name##_RB_SETPARENT(RB_RIGHT(tmp, field), (elm)); \
} \
RB_AUGMENT(elm); \
if (name##_RB_SETPARENT(tmp, name##_RB_GETPARENT(elm)) != NULL) { \
if ((elm) == RB_LEFT(name##_RB_GETPARENT(elm), field)) \
RB_LEFT(name##_RB_GETPARENT(elm), field) = (tmp); \
else \
RB_RIGHT(name##_RB_GETPARENT(elm), field) = (tmp); \
} else \
(head)->rbh_root = (tmp); \
RB_RIGHT(tmp, field) = (elm); \
name##_RB_SETPARENT(elm, tmp); \
RB_AUGMENT(tmp); \
if ((name##_RB_GETPARENT(tmp))) \
RB_AUGMENT(name##_RB_GETPARENT(tmp)); \
} while (/*CONSTCOND*/ 0)
/* Generates prototypes and inline functions */
#define RB_PROTOTYPE(name, type, field, cmp) \
void name##_RB_INSERT_COLOR(struct name *, struct type *); \
void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *);\
struct type *name##_RB_REMOVE(struct name *, struct type *); \
struct type *name##_RB_INSERT(struct name *, struct type *); \
struct type *name##_RB_FIND(struct name *, struct type *); \
struct type *name##_RB_NEXT(struct type *); \
struct type *name##_RB_MINMAX(struct name *, int); \
struct type *name##_RB_GETPARENT(struct type*); \
struct type *name##_RB_SETPARENT(struct type*, struct type*); \
int name##_RB_GETCOLOR(struct type*); \
void name##_RB_SETCOLOR(struct type*,int);
/* Generates prototypes (with storage class) and inline functions */
#define RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \
_sc_ void name##_RB_INSERT_COLOR(struct name *, struct type *); \
_sc_ void name##_RB_REMOVE_COLOR(struct name *, struct type *, struct type *); \
_sc_ struct type *name##_RB_REMOVE(struct name *, struct type *); \
_sc_ struct type *name##_RB_INSERT(struct name *, struct type *); \
_sc_ struct type *name##_RB_FIND(struct name *, struct type *); \
_sc_ struct type *name##_RB_NEXT(struct type *); \
_sc_ struct type *name##_RB_MINMAX(struct name *, int); \
_sc_ struct type *name##_RB_GETPARENT(struct type*); \
_sc_ struct type *name##_RB_SETPARENT(struct type*, struct type*); \
_sc_ int name##_RB_GETCOLOR(struct type*); \
_sc_ void name##_RB_SETCOLOR(struct type*,int);
/* Main rb operation.
* Moves node close to the key of elm to top
*/
#define RB_GENERATE(name, type, field, cmp) \
struct type *name##_RB_GETPARENT(struct type *elm) { \
struct type *parent = _RB_PARENT(elm, field); \
if( parent != NULL) { \
parent = (struct type*)((uintptr_t)parent & ~RB_COLOR_MASK);\
return( (struct type*) ( (parent == (struct type*) RB_PLACEHOLDER) ? NULL: parent));\
} \
return((struct type*)NULL); \
} \
int name##_RB_GETCOLOR(struct type *elm) { \
int color = 0; \
color = (int)((uintptr_t)_RB_PARENT(elm,field) & RB_COLOR_MASK);\
return(color); \
} \
void name##_RB_SETCOLOR(struct type *elm,int color) { \
struct type *parent = name##_RB_GETPARENT(elm); \
if(parent == (struct type*)NULL) \
parent = (struct type*) RB_PLACEHOLDER; \
_RB_PARENT(elm, field) = (struct type*)((uintptr_t)parent | (unsigned int)color);\
} \
struct type *name##_RB_SETPARENT(struct type *elm, struct type *parent) { \
int color = name##_RB_GETCOLOR(elm); \
_RB_PARENT(elm, field) = parent; \
if(color) name##_RB_SETCOLOR(elm, color); \
return(name##_RB_GETPARENT(elm)); \
} \
\
void \
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
{ \
struct type *parent, *gparent, *tmp; \
while ((parent = name##_RB_GETPARENT(elm)) != NULL && \
name##_RB_GETCOLOR(parent) == RB_RED) { \
gparent = name##_RB_GETPARENT(parent); \
if (parent == RB_LEFT(gparent, field)) { \
tmp = RB_RIGHT(gparent, field); \
if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \
name##_RB_SETCOLOR(tmp, RB_BLACK); \
RB_SET_BLACKRED(name, parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_RIGHT(parent, field) == elm) { \
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(name, parent, gparent, field); \
RB_ROTATE_RIGHT(name,head, gparent, tmp, field); \
} else { \
tmp = RB_LEFT(gparent, field); \
if (tmp && name##_RB_GETCOLOR(tmp) == RB_RED) { \
name##_RB_SETCOLOR(tmp, RB_BLACK); \
RB_SET_BLACKRED(name, parent, gparent, field);\
elm = gparent; \
continue; \
} \
if (RB_LEFT(parent, field) == elm) { \
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
tmp = parent; \
parent = elm; \
elm = tmp; \
} \
RB_SET_BLACKRED(name, parent, gparent, field); \
RB_ROTATE_LEFT(name, head, gparent, tmp, field); \
} \
} \
name##_RB_SETCOLOR(head->rbh_root, RB_BLACK); \
} \
\
void \
name##_RB_REMOVE_COLOR(struct name *head, struct type *parent, struct type *elm) \
{ \
struct type *tmp; \
while ((elm == NULL || name##_RB_GETCOLOR(elm) == RB_BLACK) && \
elm != RB_ROOT(head)) { \
if (RB_LEFT(parent, field) == elm) { \
tmp = RB_RIGHT(parent, field); \
if (name##_RB_GETCOLOR(tmp) == RB_RED) { \
RB_SET_BLACKRED(name, tmp, parent, field); \
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
tmp = RB_RIGHT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\
name##_RB_SETCOLOR(tmp, RB_RED); \
elm = parent; \
parent = name##_RB_GETPARENT(elm); \
} else { \
if (RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK) {\
struct type *oleft; \
if ((oleft = RB_LEFT(tmp, field)) \
!= NULL) \
name##_RB_SETCOLOR(oleft, RB_BLACK);\
name##_RB_SETCOLOR(tmp, RB_RED); \
RB_ROTATE_RIGHT(name, head, tmp, oleft, field);\
tmp = RB_RIGHT(parent, field); \
} \
name##_RB_SETCOLOR(tmp, (name##_RB_GETCOLOR(parent)));\
name##_RB_SETCOLOR(parent, RB_BLACK); \
if (RB_RIGHT(tmp, field)) \
name##_RB_SETCOLOR(RB_RIGHT(tmp, field),RB_BLACK);\
RB_ROTATE_LEFT(name, head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} else { \
tmp = RB_LEFT(parent, field); \
if (name##_RB_GETCOLOR(tmp) == RB_RED) { \
RB_SET_BLACKRED(name, tmp, parent, field); \
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
tmp = RB_LEFT(parent, field); \
} \
if ((RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) &&\
(RB_RIGHT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_RIGHT(tmp, field)) == RB_BLACK)) {\
name##_RB_SETCOLOR(tmp, RB_RED); \
elm = parent; \
parent = name##_RB_GETPARENT(elm); \
} else { \
if (RB_LEFT(tmp, field) == NULL || \
name##_RB_GETCOLOR(RB_LEFT(tmp, field)) == RB_BLACK) {\
struct type *oright; \
if ((oright = RB_RIGHT(tmp, field)) \
!= NULL) \
name##_RB_SETCOLOR(oright, RB_BLACK);\
name##_RB_SETCOLOR(tmp, RB_RED); \
RB_ROTATE_LEFT(name, head, tmp, oright, field);\
tmp = RB_LEFT(parent, field); \
} \
name##_RB_SETCOLOR(tmp,(name##_RB_GETCOLOR(parent)));\
name##_RB_SETCOLOR(parent, RB_BLACK); \
if (RB_LEFT(tmp, field)) \
name##_RB_SETCOLOR(RB_LEFT(tmp, field), RB_BLACK);\
RB_ROTATE_RIGHT(name, head, parent, tmp, field);\
elm = RB_ROOT(head); \
break; \
} \
} \
} \
if (elm) \
name##_RB_SETCOLOR(elm, RB_BLACK); \
} \
\
struct type * \
name##_RB_REMOVE(struct name *head, struct type *elm) \
{ \
struct type *child, *parent, *old = elm; \
int color; \
if (RB_LEFT(elm, field) == NULL) \
child = RB_RIGHT(elm, field); \
else if (RB_RIGHT(elm, field) == NULL) \
child = RB_LEFT(elm, field); \
else { \
struct type *left; \
elm = RB_RIGHT(elm, field); \
while ((left = RB_LEFT(elm, field)) != NULL) \
elm = left; \
child = RB_RIGHT(elm, field); \
parent = name##_RB_GETPARENT(elm); \
color = name##_RB_GETCOLOR(elm); \
if (child) \
name##_RB_SETPARENT(child, parent); \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
if (name##_RB_GETPARENT(elm) == old) \
parent = elm; \
(elm)->field = (old)->field; \
if (name##_RB_GETPARENT(old)) { \
if (RB_LEFT(name##_RB_GETPARENT(old), field) == old)\
RB_LEFT(name##_RB_GETPARENT(old), field) = elm;\
else \
RB_RIGHT(name##_RB_GETPARENT(old), field) = elm;\
RB_AUGMENT(name##_RB_GETPARENT(old)); \
} else \
RB_ROOT(head) = elm; \
name##_RB_SETPARENT(RB_LEFT(old, field), elm); \
if (RB_RIGHT(old, field)) \
name##_RB_SETPARENT(RB_RIGHT(old, field), elm); \
if (parent) { \
left = parent; \
do { \
RB_AUGMENT(left); \
} while ((left = name##_RB_GETPARENT(left)) != NULL); \
} \
goto color; \
} \
parent = name##_RB_GETPARENT(elm); \
color = name##_RB_GETCOLOR(elm); \
if (child) \
name##_RB_SETPARENT(child, parent); \
if (parent) { \
if (RB_LEFT(parent, field) == elm) \
RB_LEFT(parent, field) = child; \
else \
RB_RIGHT(parent, field) = child; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = child; \
color: \
if (color == RB_BLACK) \
name##_RB_REMOVE_COLOR(head, parent, child); \
return (old); \
} \
\
/* Inserts a node into the RB tree */ \
struct type * \
name##_RB_INSERT(struct name *head, struct type *elm) \
{ \
struct type *tmp; \
struct type *parent = NULL; \
int comp = 0; \
tmp = RB_ROOT(head); \
while (tmp) { \
parent = tmp; \
comp = (cmp)(elm, parent); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
RB_SET(name, elm, parent, field); \
if (parent != NULL) { \
if (comp < 0) \
RB_LEFT(parent, field) = elm; \
else \
RB_RIGHT(parent, field) = elm; \
RB_AUGMENT(parent); \
} else \
RB_ROOT(head) = elm; \
name##_RB_INSERT_COLOR(head, elm); \
return (NULL); \
} \
\
/* Finds the node with the same key as elm */ \
struct type * \
name##_RB_FIND(struct name *head, struct type *elm) \
{ \
struct type *tmp = RB_ROOT(head); \
int comp; \
while (tmp) { \
comp = cmp(elm, tmp); \
if (comp < 0) \
tmp = RB_LEFT(tmp, field); \
else if (comp > 0) \
tmp = RB_RIGHT(tmp, field); \
else \
return (tmp); \
} \
return (NULL); \
} \
\
/* ARGSUSED */ \
struct type * \
name##_RB_NEXT(struct type *elm) \
{ \
if (RB_RIGHT(elm, field)) { \
elm = RB_RIGHT(elm, field); \
while (RB_LEFT(elm, field)) \
elm = RB_LEFT(elm, field); \
} else { \
if (name##_RB_GETPARENT(elm) && \
(elm == RB_LEFT(name##_RB_GETPARENT(elm), field))) \
elm = name##_RB_GETPARENT(elm); \
else { \
while (name##_RB_GETPARENT(elm) && \
(elm == RB_RIGHT(name##_RB_GETPARENT(elm), field)))\
elm = name##_RB_GETPARENT(elm); \
elm = name##_RB_GETPARENT(elm); \
} \
} \
return (elm); \
} \
\
struct type * \
name##_RB_MINMAX(struct name *head, int val) \
{ \
struct type *tmp = RB_ROOT(head); \
struct type *parent = NULL; \
while (tmp) { \
parent = tmp; \
if (val < 0) \
tmp = RB_LEFT(tmp, field); \
else \
tmp = RB_RIGHT(tmp, field); \
} \
return (parent); \
}
#define RB_PROTOTYPE_PREV(name, type, field, cmp) \
RB_PROTOTYPE(name, type, field, cmp) \
struct type *name##_RB_PREV(struct type *);
#define RB_PROTOTYPE_SC_PREV(_sc_, name, type, field, cmp) \
RB_PROTOTYPE_SC(_sc_, name, type, field, cmp) \
_sc_ struct type *name##_RB_PREV(struct type *);
#define RB_GENERATE_PREV(name, type, field, cmp) \
RB_GENERATE(name, type, field, cmp) \
struct type * \
name##_RB_PREV(struct type *elm) \
{ \
if (RB_LEFT(elm, field)) { \
elm = RB_LEFT(elm, field); \
while (RB_RIGHT(elm, field)) \
elm = RB_RIGHT(elm, field); \
} else { \
if (name##_RB_GETPARENT(elm) && \
(elm == RB_RIGHT(name##_RB_GETPARENT(elm), field))) \
elm = name##_RB_GETPARENT(elm); \
else { \
while (name##_RB_GETPARENT(elm) && \
(elm == RB_LEFT(name##_RB_GETPARENT(elm), field)))\
elm = name##_RB_GETPARENT(elm); \
elm = name##_RB_GETPARENT(elm); \
} \
} \
return (elm); \
} \
#define RB_NEGINF -1
#define RB_INF 1
#define RB_INSERT(name, x, y) name##_RB_INSERT(x, y)
#define RB_REMOVE(name, x, y) name##_RB_REMOVE(x, y)
#define RB_FIND(name, x, y) name##_RB_FIND(x, y)
#define RB_NEXT(name, x, y) name##_RB_NEXT(y)
#define RB_PREV(name, x, y) name##_RB_PREV(y)
#define RB_MIN(name, x) name##_RB_MINMAX(x, RB_NEGINF)
#define RB_MAX(name, x) name##_RB_MINMAX(x, RB_INF)
#define RB_FOREACH(x, name, head) \
for ((x) = RB_MIN(name, head); \
(x) != NULL; \
(x) = name##_RB_NEXT(x))
#define RB_FOREACH_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_REVERSE_FROM(x, name, y) \
for ((x) = (y); \
((x) != NULL) && ((y) = name##_RB_PREV(x), (x) != NULL); \
(x) = (y))
#define RB_FOREACH_SAFE(x, name, head, y) \
for ((x) = RB_MIN(name, head); \
((x) != NULL) && ((y) = name##_RB_NEXT(x), (x) != NULL); \
(x) = (y))
#endif /* _LIBKERN_TREE_H_ */

File diff suppressed because it is too large Load Diff

@ -0,0 +1,222 @@
/*
* Copyright (c) 2000-2008 Apple Inc. All rights reserved.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. The rights granted to you under the License
* may not be used to create, or enable the creation or redistribution of,
* unlawful or unlicensed copies of an Apple operating system, or to
* circumvent, violate, or enable the circumvention or violation of, any
* terms of an Apple operating system software license agreement.
*
* Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
*/
/*
* Copyright (c) 1988, 1989, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)radix.h 8.2 (Berkeley) 10/31/94
* $FreeBSD: src/sys/net/radix.h,v 1.16.2.1 2000/05/03 19:17:11 wollman Exp $
*/
#ifndef _RADIX_H_
#define _RADIX_H_
#include <sys/appleapiopts.h>
#ifdef PRIVATE
#ifdef MALLOC_DECLARE
MALLOC_DECLARE(M_RTABLE);
#endif
/*
* Radix search tree node layout.
*/
struct radix_node {
struct radix_mask *rn_mklist; /* list of masks contained in subtree */
struct radix_node *rn_parent; /* parent */
short rn_bit; /* bit offset; -1-index(netmask) */
char rn_bmask; /* node: mask for bit test*/
u_char rn_flags; /* enumerated next */
#define RNF_NORMAL 1 /* leaf contains normal route */
#define RNF_ROOT 2 /* leaf is root leaf for tree */
#define RNF_ACTIVE 4 /* This node is alive (for rtfree) */
union {
struct { /* leaf only data: */
caddr_t rn_Key; /* object of search */
caddr_t rn_Mask; /* netmask, if present */
struct radix_node *rn_Dupedkey;
} rn_leaf;
struct { /* node only data: */
int rn_Off; /* where to start compare */
struct radix_node *rn_L;/* progeny */
struct radix_node *rn_R;/* progeny */
} rn_node;
} rn_u;
#ifdef RN_DEBUG
int rn_info;
struct radix_node *rn_twin;
struct radix_node *rn_ybro;
#endif
#if __arm__ && (__BIGGEST_ALIGNMENT__ > 4)
/* For the newer ARMv7k ABI where 64-bit types are 64-bit aligned, but pointers
* are 32-bit:
* Aligned to 64-bit since this is cast to rtentry, which is 64-bit aligned.
*/
} __attribute__ ((aligned(8)));
#else
};
#endif
#define rn_dupedkey rn_u.rn_leaf.rn_Dupedkey
#define rn_key rn_u.rn_leaf.rn_Key
#define rn_mask rn_u.rn_leaf.rn_Mask
#define rn_offset rn_u.rn_node.rn_Off
#define rn_left rn_u.rn_node.rn_L
#define rn_right rn_u.rn_node.rn_R
/*
* Annotations to tree concerning potential routes applying to subtrees.
*/
struct radix_mask {
short rm_bit; /* bit offset; -1-index(netmask) */
char rm_unused; /* cf. rn_bmask */
u_char rm_flags; /* cf. rn_flags */
struct radix_mask *rm_mklist; /* more masks to try */
union {
caddr_t rmu_mask; /* the mask */
struct radix_node *rmu_leaf; /* for normal routes */
} rm_rmu;
int rm_refs; /* # of references to this struct */
};
#define rm_mask rm_rmu.rmu_mask
#define rm_leaf rm_rmu.rmu_leaf /* extra field would make 32 bytes */
#define MKGet(m) {\
if (rn_mkfreelist) {\
m = rn_mkfreelist; \
rn_mkfreelist = (m)->rm_mklist; \
} else \
R_Malloc(m, struct radix_mask *, sizeof (*(m))); }\
#define MKFree(m) { (m)->rm_mklist = rn_mkfreelist; rn_mkfreelist = (m);}
typedef int walktree_f_t(struct radix_node *, void *);
typedef int rn_matchf_t(struct radix_node *, void *);
struct radix_node_head {
struct radix_node *rnh_treetop;
int rnh_addrsize; /* permit, but not require fixed keys */
int rnh_pktsize; /* permit, but not require fixed keys */
struct radix_node *(*rnh_addaddr) /* add based on sockaddr */
(void *v, void *mask,
struct radix_node_head *head, struct radix_node nodes[]);
struct radix_node *(*rnh_addpkt) /* add based on packet hdr */
(void *v, void *mask,
struct radix_node_head *head, struct radix_node nodes[]);
struct radix_node *(*rnh_deladdr) /* remove based on sockaddr */
(void *v, void *mask, struct radix_node_head *head);
struct radix_node *(*rnh_delpkt) /* remove based on packet hdr */
(void *v, void *mask, struct radix_node_head *head);
struct radix_node *(*rnh_matchaddr) /* locate based on sockaddr */
(void *v, struct radix_node_head *head);
/* locate based on sockaddr and rn_matchf_t() */
struct radix_node *(*rnh_matchaddr_args)
(void *v, struct radix_node_head *head,
rn_matchf_t *f, void *w);
struct radix_node *(*rnh_lookup) /* locate based on sockaddr */
(void *v, void *mask, struct radix_node_head *head);
/* locate based on sockaddr, mask and rn_matchf_t() */
struct radix_node *(*rnh_lookup_args)
(void *v, void *mask, struct radix_node_head *head,
rn_matchf_t *f, void *);
struct radix_node *(*rnh_matchpkt) /* locate based on packet hdr */
(void *v, struct radix_node_head *head);
int (*rnh_walktree) /* traverse tree */
(struct radix_node_head *head, walktree_f_t *f, void *w);
int (*rnh_walktree_from) /* traverse tree below a */
(struct radix_node_head *head, void *a, void *m,
walktree_f_t *f, void *w);
void (*rnh_close) /* do something when the last ref drops */
(struct radix_node *rn, struct radix_node_head *head);
struct radix_node rnh_nodes[3]; /* empty tree for common case */
int rnh_cnt; /* tree dimension */
};
#ifndef KERNEL
#define Bcmp(a, b, n) bcmp(((char *)(a)), ((char *)(b)), (n))
#define Bcopy(a, b, n) bcopy(((char *)(a)), ((char *)(b)), (unsigned)(n))
#define Bzero(p, n) bzero((char *)(p), (int)(n));
#define R_Malloc(p, t, n) (p = (t) malloc((unsigned int)(n)))
#define R_Free(p) free((char *)p);
#else
#define Bcmp(a, b, n) bcmp(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
#define Bcopy(a, b, n) bcopy(((caddr_t)(a)), ((caddr_t)(b)), (unsigned)(n))
#define Bzero(p, n) bzero((caddr_t)(p), (unsigned)(n));
#define R_Malloc(p, t, n) (p = (t) _MALLOC((uint32_t)(n), M_RTABLE, M_WAITOK))
#define R_Free(p) FREE((caddr_t)p, M_RTABLE);
#endif /*KERNEL*/
void rn_init(void);
int rn_inithead(void **, int);
int rn_refines(void *, void *);
struct radix_node
*rn_addmask(void *, int, int),
*rn_addroute(void *, void *, struct radix_node_head *,
struct radix_node [2]),
*rn_delete(void *, void *, struct radix_node_head *),
*rn_lookup(void *v_arg, void *m_arg, struct radix_node_head *head),
*rn_lookup_args(void *v_arg, void *m_arg, struct radix_node_head *head,
rn_matchf_t *, void *),
*rn_match(void *, struct radix_node_head *),
*rn_match_args(void *, struct radix_node_head *, rn_matchf_t *, void *);
#endif /* PRIVATE */
#endif /* _RADIX_H_ */
Loading…
Cancel
Save