pax_global_header00006660000000000000000000000064137362724540014527gustar00rootroot0000000000000052 comment=0f17505ab99ffbf7821e7b51a846ab0a953781e7 re-1.1.0/000077500000000000000000000000001373627245400121345ustar00rootroot00000000000000re-1.1.0/.gitignore000066400000000000000000000001311373627245400141170ustar00rootroot00000000000000build* test test.c test.d test.o *stamp *.previous libre.a libre.dylib libre.pc libre.so re-1.1.0/.travis.yml000066400000000000000000000041131373627245400142440ustar00rootroot00000000000000language: c os: - linux - osx compiler: - clang - gcc jobs: include: - os: linux stage: ccheck addons: apt: update: false script: - wget "https://raw.githubusercontent.com/baresip/baresip/master/test/ccheck.py" - python3 ccheck.py - name: OpenSSL 1.0.2g os: linux dist: xenial stage: openssl - name: OpenSSL 1.1.1f os: linux dist: focal stage: openssl - name: macOS 10.15.5 os: osx osx_image: xcode12.0 stage: openssl - name: LibreSSL os: linux dist: xenial script: - wget "https://github.com/baresip/ci/releases/download/v0.1/assets.tar.gz" - tar -xf assets.tar.gz - make EXTRA_CFLAGS="-Iassets/libressl/include -Werror" EXTRA_LFLAGS="-Lassets/libressl" CCACHE=; stage: openssl - name: OpenSSL 1.1.1g no-deprecated os: linux dist: xenial script: - wget "https://github.com/baresip/ci/releases/download/v0.1/assets.tar.gz" - tar -xf assets.tar.gz - make EXTRA_CFLAGS="-Iassets/openssl/include -Werror" EXTRA_LFLAGS="-Lassets/openssl" CCACHE=; stage: openssl stages: - ccheck - test - openssl addons: apt: packages: libssl-dev script: - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then make EXTRA_CFLAGS=-Werror CCACHE=; fi - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then cd ..; rm re/libre.so; git clone https://github.com/creytiv/rem; make -C rem librem.a; git clone https://github.com/baresip/retest.git; cd retest && make STATIC=1 LIBRE_SO=../re && ./retest -r; fi - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make EXTRA_CFLAGS="-I/usr/local/opt/openssl/include -Werror" EXTRA_LFLAGS="-L/usr/local/opt/openssl/lib" CCACHE=; fi re-1.1.0/CHANGELOG.md000066400000000000000000000051621373627245400137510ustar00rootroot00000000000000# libre Changelog All notable changes to libre will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [v1.1.0] - 2020-10-04 ### Added - tls: functions to get the certificate issuer and subject [#18] - uri: Added path field to struct uri and its decode to uri_decode [#22] - tcp: add tcp_connect_bind [#24] - http: support bind to laddr in http_request [#24] - sipreg: support Cisco REGISTER keep-alives [#19] - sip: websocket support [#26] ### Fixed - tls/openssl: fix X509_NAME win32/wincrypt.h conflict - dns: listen on IPv4 and IPv6 socket [#27] - main: fix/optimize windows file descriptors [#25] ### Contributors (many thanks) - Alfred E. Heggestad - Christian Spielberger - Christoph Huber - Franz Auernigg - Juha Heinanen - Sebastian Reimers ## [v1.0.0] - 2020-09-08 ### Added - sip: add trace - sdp: sdp_media_disabled API function [#2] - tls: add tls_set_selfsigned_rsa [#6] - tls: add functions to verify server cert, purpose and hostname [#10] - http: client should set SNI [#10] - http: client should use tls functions to verify server certs, purpose and hostname [#10] - sipreg: add proxy expires field and get function [#13] - sipreg: make re-register interval configurable [#13] ### Changed - debian: Automatic cleanup after building debian package ### Fixed - Set SDK path (SYSROOT) using xcrun (fix building on macOS 10.14) - tcp: close socket on windows if connection is aborted or reset [#1] - rtmp: Fix URL path parsing (creytiv#245) - ice: various fixes [baresip/baresip#925] - openssl/tls: replace deprecated openssl 1.1.0 functions [#5] ### Contributors (many thanks) - Alfred E. Heggestad - Christian Spielberger - Christoph Huber - Franz Auernigg - juha-h - Juha Heinanen - Richard Aas - Sebastian Reimers [#25]: https://github.com/baresip/re/pull/25 [#27]: https://github.com/baresip/re/pull/27 [#26]: https://github.com/baresip/re/pull/26 [#19]: https://github.com/baresip/re/pull/19 [#24]: https://github.com/baresip/re/pull/24 [#22]: https://github.com/baresip/re/pull/22 [#18]: https://github.com/baresip/re/pull/18 [#13]: https://github.com/baresip/re/pull/13 [#10]: https://github.com/baresip/re/pull/10 [#6]: https://github.com/baresip/re/pull/6 [#5]: https://github.com/baresip/re/pull/5 [#2]: https://github.com/baresip/re/pull/2 [#1]: https://github.com/baresip/re/pull/1 [v1.0.0]: https://github.com/baresip/re/compare/v0.6.1...v1.0.0 [v1.1.0]: https://github.com/baresip/re/compare/v1.0.0...v1.1.0 [Unreleased]: https://github.com/baresip/re/compare/v1.1.0...HEAD re-1.1.0/Makefile000066400000000000000000000060711373627245400136000ustar00rootroot00000000000000# # Makefile # # Copyright (C) 2010 Creytiv.com # # Master version number VER_MAJOR := 1 VER_MINOR := 1 VER_PATCH := 0 PROJECT := re VERSION := $(VER_MAJOR).$(VER_MINOR).$(VER_PATCH) MK := mk/re.mk include $(MK) # List of modules MODULES += sip sipevent sipreg sipsess MODULES += uri http httpauth msg websock MODULES += stun turn ice MODULES += natbd MODULES += rtp sdp jbuf telev MODULES += dns MODULES += md5 crc32 sha hmac base64 MODULES += udp sa net tcp tls MODULES += list mbuf hash MODULES += fmt tmr main mem dbg sys lock mqueue MODULES += mod conf MODULES += bfcp MODULES += aes srtp MODULES += odict MODULES += json MODULES += rtmp INSTALL := install ifeq ($(DESTDIR),) PREFIX ?= /usr/local else PREFIX ?= /usr endif ifeq ($(LIBDIR),) LIBDIR := $(PREFIX)/lib endif INCDIR := $(PREFIX)/include/re MKDIR := $(PREFIX)/share/re CFLAGS += -Iinclude MODMKS := $(patsubst %,src/%/mod.mk,$(MODULES)) SHARED := libre$(LIB_SUFFIX) STATIC := libre.a include $(MODMKS) OBJS ?= $(patsubst %.c,$(BUILD)/%.o,$(SRCS)) all: $(SHARED) $(STATIC) -include $(OBJS:.o=.d) $(SHARED): $(OBJS) @echo " LD $@" @$(LD) $(LFLAGS) $(SH_LFLAGS) $^ $(LIBS) -o $@ $(STATIC): $(OBJS) @echo " AR $@" @$(AR) $(AFLAGS) $@ $^ ifneq ($(RANLIB),) @$(RANLIB) $@ endif libre.pc: @echo 'prefix='$(PREFIX) > libre.pc @echo 'exec_prefix=$${prefix}' >> libre.pc @echo 'libdir=$(LIBDIR)' >> libre.pc @echo 'includedir=$${prefix}/include/re' >> libre.pc @echo '' >> libre.pc @echo 'Name: libre' >> libre.pc @echo 'Description: ' >> libre.pc @echo 'Version: '$(VERSION) >> libre.pc @echo 'URL: http://creytiv.com/re.html' >> libre.pc @echo 'Libs: -L$${libdir} -lre' >> libre.pc @echo 'Libs.private: -L$${libdir} -lre ${LIBS}' >> libre.pc @echo 'Cflags: -I$${includedir}' >> libre.pc $(BUILD)/%.o: src/%.c $(BUILD) Makefile $(MK) $(MODMKS) @echo " CC $@" @$(CC) $(CFLAGS) -c $< -o $@ $(DFLAGS) $(BUILD): Makefile $(MK) $(MODMKS) @mkdir -p $(patsubst %,$(BUILD)/%,$(sort $(dir $(SRCS)))) @touch $@ .PHONY: clean clean: @rm -rf $(SHARED) $(STATIC) libre.pc test.d test.o test $(BUILD) install: $(SHARED) $(STATIC) libre.pc @mkdir -p $(DESTDIR)$(LIBDIR) $(DESTDIR)$(LIBDIR)/pkgconfig \ $(DESTDIR)$(INCDIR) $(DESTDIR)$(MKDIR) $(INSTALL) -m 0644 $(shell find include -name "*.h") \ $(DESTDIR)$(INCDIR) $(INSTALL) -m 0755 $(SHARED) $(DESTDIR)$(LIBDIR) $(INSTALL) -m 0755 $(STATIC) $(DESTDIR)$(LIBDIR) $(INSTALL) -m 0644 libre.pc $(DESTDIR)$(LIBDIR)/pkgconfig $(INSTALL) -m 0644 $(MK) $(DESTDIR)$(MKDIR) uninstall: @rm -rf $(DESTDIR)$(INCDIR) @rm -rf $(DESTDIR)$(MKDIR) @rm -f $(DESTDIR)$(LIBDIR)/$(SHARED) @rm -f $(DESTDIR)$(LIBDIR)/$(STATIC) @rm -f $(DESTDIR)$(LIBDIR)/pkgconfig/libre.pc -include test.d test.o: test.c Makefile $(MK) @echo " CC $@" @$(CC) $(CFLAGS) -c $< -o $@ $(DFLAGS) test$(BIN_SUFFIX): test.o $(SHARED) $(STATIC) @echo " LD $@" @$(LD) $(LFLAGS) $< -L. -lre $(LIBS) -o $@ sym: $(SHARED) @nm $(SHARED) | grep " U " | perl -pe 's/\s*U\s+(.*)/$${1}/' \ > docs/symbols.txt @echo "$(SHARED) is using `cat docs/symbols.txt | wc -l ` symbols" re-1.1.0/README.md000066400000000000000000000274141373627245400134230ustar00rootroot00000000000000libre README ============ libre is a Generic library for real-time communications with async IO support. Copyright (C) 2010 - 2020 Creytiv.com [![Build Status](https://travis-ci.org/baresip/re.svg?branch=master)](https://travis-ci.org/baresip/re) ## Features * SIP Stack ([RFC 3261](https://tools.ietf.org/html/rfc3261)) * SDP * RTP and RTCP * SRTP and SRTCP (Secure RTP) * DNS-Client * STUN/TURN/ICE stack * BFCP * HTTP-stack with client/server * Websockets * Jitter-buffer * Async I/O (poll, epoll, select, kqueue) * UDP/TCP/TLS/DTLS transport * JSON parser * Real Time Messaging Protocol (RTMP) ## Building libre is using GNU makefiles. Make and OpenSSL development headers must be installed before building. ### Build with debug enabled ``` $ make $ sudo make install $ sudo ldconfig ``` ### Build with release ``` $ make RELEASE=1 $ sudo make RELEASE=1 install $ sudo ldconfig ``` ### Build with clang compiler ``` $ make CC=clang $ sudo make CC=clang install $ sudo ldconfig ``` ## Documentation The online documentation generated with doxygen is available in the main [website](http://creytiv.com/doxygen/re-dox/html/) ### Examples Coding examples are available from the [redemo](http://creytiv.com/pub/redemo-0.5.0.tar.gz) project ## License The libre project is using the BSD license. ## Contributing Patches can sent via Github [Pull-Requests](https://github.com/baresip/re/pulls) or to the RE devel [mailing-list](http://lists.creytiv.com/mailman/listinfo/re-devel). Currently we only accept small patches. Please send private feedback to libre [at] creytiv.com ## Design goals * Portable POSIX source code (ANSI C89 and ISO C99 standard) * Robust, fast, low memory footprint * RFC compliance * IPv4 and IPv6 support ## Modules | Name | Status | Description | |----------|----------|------------------------------------------------| | aes | unstable | AES (Advanced Encryption Standard) | | base64 | testing | Base-64 encoding/decoding functions | | bfcp | unstable | The Binary Floor Control Protocol (BFCP) | | conf | testing | Configuration file parser | | crc32 | testing | 32-bit CRC defined in ITU V.42 | | dbg | testing | Debug printing | | dns | stable | DNS resolving (NAPTR, SRV, A) | | fmt | testing | Formatted printing and regular expression | | hash | testing | Hashmap table | | hmac | testing | HMAC: Keyed-Hashing for Message Authentication | | http | unstable | HTTP parser (RFC 2616) | | httpauth | testing | HTTP-based Authentication (RFC 2617) | | ice | unstable | Interactive Connectivity Establishment (ICE) | | jbuf | testing | Jitter buffer | | json | unstable | JavaScript Object Notation (JSON) | | list | stable | Sortable doubly-linked list handling | | lock | testing | Resource locking functions | | main | testing | Main poll loop | | mbuf | stable | Linear memory buffers | | md5 | stable | The MD5 Message-Digest Algorithm (RFC 1321) | | mem | stable | Memory referencing | | mod | testing | Run-time module loading | | mqueue | testing | Thread-safe message queue | | msg | unstable | Generic message component library | | natbd | unstable | NAT Behavior Discovery using STUN | | net | testing | Networking routines | | odict | unstable | Ordered Dictionary | | rtmp | unstable | Real Time Messaging Protocol | | rtp | testing | Real-time Transport Protocol | | sa | stable | Socket Address functions | | sdp | testing | Session Description Protocol | | sha | testing | Secure Hash Standard, NIST, FIPS PUB 180-1 | | sip | stable | Core SIP library | | sipevent | testing | SIP Event framework | | sipreg | stable | SIP register client | | sipsess | stable | SIP Sessions | | srtp | unstable | Secure Real-time Transport Protocol (SRTP) | | stun | stable | Session Traversal Utilities for NAT (STUN) | | sys | testing | System information | | tcp | testing | TCP transport | | telev | testing | Telephony Events (RFC 4733) | | tls | unstable | Transport Layer Security | | tmr | stable | Timer handling | | turn | stable | Obtaining Relay Addresses from STUN (TURN) | | udp | testing | UDP transport | | uri | testing | Generic URI library | | websock | unstable | WebSocket Client and Server | legend: * *stable* - code complete; stable code and stable API * *testing* - code complete, but API might change * *unstable* - code complete but not completely tested * *development* - code is under development ## Features * [RFC 1321](https://tools.ietf.org/html/rfc1321) - The MD5 Message-Digest Algorithm * [RFC 1886](https://tools.ietf.org/html/rfc1886) - DNS Extensions to support IP version 6 * [RFC 2032](https://tools.ietf.org/html/rfc2032) - RTP Payload Format for H.261 Video Streams * [RFC 2616](https://tools.ietf.org/html/rfc2616) - Hypertext Transfer Protocol -- HTTP/1.1 * [RFC 2617](https://tools.ietf.org/html/rfc2617) - HTTP Authentication: Basic and Digest Access Authentication * [RFC 2782](https://tools.ietf.org/html/rfc2782) - A DNS RR for Specifying the Location of Services (DNS SRV) * [RFC 2915](https://tools.ietf.org/html/rfc2915) - The Naming Authority Pointer (NAPTR) DNS Resource Record * [RFC 3261](https://tools.ietf.org/html/rfc3261) - SIP: Session Initiation Protocol * [RFC 3263](https://tools.ietf.org/html/rfc3263) - Locating SIP Servers * [RFC 3264](https://tools.ietf.org/html/rfc3264) - An Offer/Answer Model with SDP * [RFC 3265](https://tools.ietf.org/html/rfc3265) - SIP-Specific Event Notification * [RFC 3327](https://tools.ietf.org/html/rfc3327) - SIP Extension Header Field for Registering Non-Adjacent Contacts * [RFC 3428](https://tools.ietf.org/html/rfc3428) - SIP Extension for Instant Messaging * [RFC 3489](https://tools.ietf.org/html/rfc3489) - STUN - Simple Traversal of UDP Through NATs * [RFC 3515](https://tools.ietf.org/html/rfc3515) - The SIP Refer Method * [RFC 3550](https://tools.ietf.org/html/rfc3550) - RTP: A Transport Protocol for Real-Time Applications * [RFC 3551](https://tools.ietf.org/html/rfc3551) - RTP Profile for Audio and Video Conferences with Minimal Control * [RFC 3555](https://tools.ietf.org/html/rfc3555) - MIME Type Registration of RTP Payload Formats * [RFC 3556](https://tools.ietf.org/html/rfc3556) - SDP Bandwidth Modifiers for RTCP Bandwidth * [RFC 3581](https://tools.ietf.org/html/rfc3581) - An Extension to SIP for Symmetric Response Routing * [RFC 3605](https://tools.ietf.org/html/rfc3605) - RTCP attribute in SDP * [RFC 3711](https://tools.ietf.org/html/rfc3711) - The Secure Real-time Transport Protocol (SRTP) * [RFC 3969](https://tools.ietf.org/html/rfc3969) - The IANA URI Parameter Registry for SIP * [RFC 3994](https://tools.ietf.org/html/rfc3994) - Indication of Message Composition for Instant Messaging * [RFC 4346](https://tools.ietf.org/html/rfc4346) - The TLS Protocol Version 1.1 * [RFC 4566](https://tools.ietf.org/html/rfc4566) - SDP: Session Description Protocol * [RFC 4582](https://tools.ietf.org/html/rfc4582) - The Binary Floor Control Protocol (BFCP) * [RFC 4582bis](https://tools.ietf.org/html/draft-ietf-bfcpbis-rfc4582bis-08) - The Binary Floor Control Protocol (BFCP) * [RFC 4585](https://tools.ietf.org/html/rfc4585) - Extended RTP Profile for RTCP-Based Feedback * [RFC 4733](https://tools.ietf.org/html/rfc4733) - RTP Payload for DTMF Digits, Telephony Tones, and Teleph. Signals * [RFC 4961](https://tools.ietf.org/html/rfc4961) - Symmetric RTP / RTP Control Protocol (RTCP) * [RFC 5118](https://tools.ietf.org/html/rfc5118) - SIP Torture Test Messages for IPv6 * [RFC 5245](https://tools.ietf.org/html/rfc5245) - Interactive Connectivity Establishment (ICE) * [RFC 5389](https://tools.ietf.org/html/rfc5389) - Session Traversal Utilities for NAT (STUN) * [RFC 5626](https://tools.ietf.org/html/rfc5626) - Managing Client-Initiated Connections in SIP * [RFC 5761](https://tools.ietf.org/html/rfc5761) - Multiplexing RTP Data and Control Packets on a Single Port * [RFC 5766](https://tools.ietf.org/html/rfc5766) - Traversal Using Relays around NAT (TURN) * [RFC 5768](https://tools.ietf.org/html/rfc5768) - Indicating Support for ICE in SIP * [RFC 5769](https://tools.ietf.org/html/rfc5769) - Test vectors for STUN * [RFC 5780](https://tools.ietf.org/html/rfc5780) - NAT Behaviour Discovery Using STUN * [RFC 6026](https://tools.ietf.org/html/rfc6026) - Correct Transaction Handling for 2xx Resp. to SIP INVITE Requests * [RFC 6156](https://tools.ietf.org/html/rfc6156) - TURN Extension for IPv6 * [RFC 6188](https://tools.ietf.org/html/rfc6188) - The Use of AES-192 and AES-256 in Secure RTP * [RFC 6455](https://tools.ietf.org/html/rfc6455) - The WebSocket Protocol * [RFC 7159](https://tools.ietf.org/html/rfc7159) - JavaScript Object Notation (JSON) * [RFC 7350](https://tools.ietf.org/html/rfc7350) - DTLS as Transport for STUN * [RFC 7714](https://tools.ietf.org/html/rfc7714) - AES-GCM Authenticated Encryption in SRTP ## Supported platforms * Linux * FreeBSD * OpenBSD * NetBSD * Solaris 11 * Windows * Apple Mac OS X and iOS * Android (5.0 or later) ### Supported versions of C Standard library * Android bionic * BSD libc * GNU C Library (glibc) * Windows C Run-Time Libraries (CRT) * uClibc ### Supported compilers: * gcc 3.x * gcc 4.x * gcc 5.x * gcc 6.x * ms vc2003 compiler * clang ### Supported versions of OpenSSL * OpenSSL version 1.0.1 (Not supported) * OpenSSL version 1.0.2 * OpenSSL version 1.1.0 * OpenSSL version 1.1.1 * LibreSSL version 2.x ## Coding guidelines * Use enum for constants where appropriate * Use const as much as possible (where appropriate) * Use C99 data types (intN_t, uintN_t, bool) * Hide data-types in .c files where possible (use struct foo) * Avoid malloc/free, use mem_alloc/mem_deref instead * CVS/svn/git tags are NOT allowed in the code! * Avoid bit-fields in structs which are not portable * Use dummy handlers for timing-critical callbacks * return err, return alloced objects as pointer-pointers * in allocating functions, first arg is always double pointer * Use POSIX error-codes; EINVAL for invalid args, EBADMSG for parse errors and EPROTO for protocol errors ## Transport protocols | | TCP | UDP | TLS | DTLS| |:--------|:---:|:---:|:---:|:---:| | BFCP | - | yes | - | - | | DNS | yes | yes | - | - | | HTTP | yes | n/a | yes | n/a | | ICE | - | yes | - | - | | RTP | - | yes | - | - | | RTCP | - | yes | - | - | | RTMP | yes | - | yes | - | | SIP | yes | yes | yes | - | | STUN | yes | yes | yes | yes | | TURN | yes | yes | yes | yes | | WEBSOCK | yes | n/a | yes | n/a | ## Related projects * [librem](https://github.com/creytiv/rem) * [retest](https://github.com/baresip/retest) * [baresip](https://github.com/baresip/baresip) * [restund](http://creytiv.com/restund.html) ## References http://creytiv.com/re.html https://github.com/creytiv/re http://lists.creytiv.com/mailman/listinfo/re-devel re-1.1.0/debian/000077500000000000000000000000001373627245400133565ustar00rootroot00000000000000re-1.1.0/debian/changelog000066400000000000000000000140511373627245400152310ustar00rootroot00000000000000libre (1.1.0) unstable; urgency=medium * version 1.1.0 -- Sebastian Reimers Thu, 04 Oct 2020 09:00:00 +0200 libre (1.0.0) unstable; urgency=medium * version 1.0.0 -- Sebastian Reimers Thu, 08 Sep 2020 08:00:00 +0200 libre (0.6.1-3) unstable; urgency=medium * version 0.6.1-3 -- Richard Aas Thu, 23 Apr 2020 13:00:00 +0200 libre (0.6.1-2) unstable; urgency=medium * version 0.6.1-2 -- Richard Aas Wed, 4 Mar 2020 14:00:00 +0100 libre (0.6.1-1) unstable; urgency=medium * version 0.6.1-1 -- Richard Aas Tue, 25 Feb 2020 09:00:00 +0100 libre (0.6.1) unstable; urgency=medium * version 0.6.1 -- Alfred E. Heggestad Sat, 7 Sep 2019 08:00:00 +0200 libre (0.6.0-6) unstable; urgency=medium * version 0.6.0-6 -- Richard Aas Thu, 04 Jul 2019 12:00:00 +0200 libre (0.6.0-4) unstable; urgency=medium * version 0.6.0-4 -- Richard Aas Fri, 26 Apr 2019 12:00:00 +0200 libre (0.6.0-3) unstable; urgency=medium * version 0.6.0-3 -- Richard Aas Fri, 1 Feb 2019 12:00:00 +0100 libre (0.6.0-2) unstable; urgency=medium * version 0.6.0-2 -- Richard Aas Tue, 11 Dec 2018 10:00:00 +0100 libre (0.6.0-1) unstable; urgency=medium * version 0.6.0-1 -- Richard Aas Fri, 30 Nov 2018 10:00:00 +0100 libre (0.6.0) unstable; urgency=medium * version 0.6.0 -- Alfred E. Heggestad Sat, 24 Nov 2018 10:00:00 +0100 libre (0.5.9) unstable; urgency=medium * version 0.5.9 -- Alfred E. Heggestad Sat, 1 Sep 2018 10:00:00 +0200 libre (0.5.8) unstable; urgency=medium * version 0.5.8 -- Alfred E. Heggestad Fri, 20 Apr 2018 16:00:00 +0200 libre (0.5.7) unstable; urgency=medium * version 0.5.7 -- Alfred E. Heggestad Fri, 12 Jan 2018 18:00:00 +0100 libre (0.5.6) unstable; urgency=medium * version 0.5.6 -- Alfred E. Heggestad Mon, 6 Nov 2017 15:00:00 +0100 libre (0.5.5) unstable; urgency=medium * version 0.5.5 -- Alfred E. Heggestad Tue, 5 Sep 2017 12:00:00 +0200 libre (0.5.4) unstable; urgency=medium * version 0.5.4 -- Alfred E. Heggestad Sat, 24 Jun 2017 12:00:00 +0200 libre (0.5.3) unstable; urgency=medium * version 0.5.3 -- Alfred E. Heggestad Sat, 13 May 2017 12:00:00 +0200 libre (0.5.2) unstable; urgency=medium * version 0.5.2 -- Alfred E. Heggestad Sat, 08 Apr 2017 12:00:00 +0200 libre (0.5.1) unstable; urgency=medium * version 0.5.1 -- Alfred E. Heggestad Sat, 04 Feb 2017 12:00:00 +0100 libre (0.5.0) unstable; urgency=medium * version 0.5.0 -- Alfred E. Heggestad Fri, 25 Nov 2016 18:00:00 +0100 libre (0.4.17) unstable; urgency=medium * version 0.4.17 -- Alfred E. Heggestad Fri, 24 Jun 2016 15:00:00 +0100 libre (0.4.16) unstable; urgency=medium * version 0.4.16 -- Alfred E. Heggestad Wed, 27 Apr 2016 20:00:00 +0100 libre (0.4.15) unstable; urgency=medium * version 0.4.15 -- Alfred E. Heggestad Sat, 06 Feb 2016 04:15:00 +0100 libre (0.4.14) unstable; urgency=low * version 0.4.14 -- Alfred E. Heggestad Sat, 24 Oct 2015 20:00:00 +0100 libre (0.4.13-1) unstable; urgency=low * version 0.4.13-1 -- Richard Aas Thu, 08 Oct 2015 10:35:00 +0100 libre (0.4.13) unstable; urgency=low * version 0.4.13 -- Alfred E. Heggestad Wed, 01 July 2015 20:00:00 +0100 libre (0.4.12) unstable; urgency=low * version 0.4.12 -- Alfred E. Heggestad Mon, 16 Mar 2015 20:00:00 +0100 libre (0.4.11) unstable; urgency=low * version 0.4.11 -- Alfred E. Heggestad Tue, 9 Dec 2014 20:00:00 +0100 libre (0.4.10-2) unstable; urgency=low * version 0.4.10-2 -- Richard Aas Mon, 17 Nov 2014 09:09:00 +0100 libre (0.4.10-1) unstable; urgency=low * version 0.4.10-1 -- Richard Aas Fri, 14 Nov 2014 09:09:00 +0100 libre (0.4.10) unstable; urgency=low * version 0.4.10 -- Alfred E. Heggestad Sun, 19 Oct 2014 19:00:00 +0100 libre (0.4.9) unstable; urgency=low * version 0.4.9 -- Alfred E. Heggestad Wed, 18 Jun 2014 19:00:00 +0100 libre (0.4.8) unstable; urgency=low * version 0.4.8 -- Alfred E. Heggestad Fri, 11 Apr 2014 19:00:00 +0100 libre (0.4.7) unstable; urgency=low * version 0.4.7 -- Alfred E. Heggestad Sun, 5 Jan 2014 20:00:00 +0100 libre (0.4.6) unstable; urgency=low * version 0.4.6 -- Alfred E. Heggestad Tue, 12 Nov 2013 20:00:00 +0100 libre (0.4.5) unstable; urgency=low * version 0.4.5 -- Alfred E. Heggestad Thu, 3 Oct 2013 20:00:00 +0100 libre (0.4.4) unstable; urgency=low * version 0.4.4 -- Alfred E. Heggestad Tue, 27 Aug 2013 20:00:00 +0100 libre (0.4.3) unstable; urgency=low * version 0.4.3 -- Alfred E. Heggestad Sun, 5 May 2013 15:00:00 +0100 libre (0.4.2) unstable; urgency=low * version 0.4.2 -- Alfred E. Heggestad Fri, 10 Aug 2012 10:08:00 +0100 libre (0.4.1) unstable; urgency=low * version 0.4.1 -- Alfred E. Heggestad Sat, 21 Apr 2012 21:04:00 +0100 libre (0.4.0) unstable; urgency=low * version 0.4.0 -- Alfred E. Heggestad Sun, 25 Dec 2011 12:25:00 +0100 libre (0.3.0) unstable; urgency=low * version 0.3.0 -- Alfred E. Heggestad Wed, 7 Sept 2011 07:11:00 +0100 libre (0.2.0) unstable; urgency=low * version 0.2.0 -- Alfred E. Heggestad Fri, 20 May 2011 20:05:00 +0100 libre (0.1.0) unstable; urgency=low * version 0.1.0 -- Alfred E. Heggestad Fri, 5 Nov 2010 05:11:10 +0100 re-1.1.0/debian/compat000066400000000000000000000000021373627245400145540ustar00rootroot000000000000009 re-1.1.0/debian/control000066400000000000000000000036621373627245400147700ustar00rootroot00000000000000Source: libre Section: comm Priority: optional Maintainer: Alfred E. Heggestad Standards-Version: 3.9.5 Build-Depends: debhelper (>= 9.20120311) Homepage: http://www.creytiv.com/ Package: libre Architecture: any Section: libs Depends: ${shlibs:Depends}, ${misc:Depends} Description: Generic library for real-time communications with async IO support Libre is a portable and generic library for real-time communications with async IO support and a complete SIP stack with support for protocols such as SDP, RTP/RTCP, STUN/TURN/ICE, BFCP, HTTP and DNS Client. . The current features are: . SIP Stack (RFC 3261) SDP parser RTP and RTCP stack DNS-Client STUN/TURN/ICE stack BFCP Jitter-buffer Async I/O (poll, epoll, select) UDP/TCP/TLS/DTLS transport HTTP Client and Server Websocket Client and Server JSON parser SRTP and SRTCP (Secure RTP) . Design goals: . Portable POSIX source code (ANSI C89 and ISO C99 standard) Robust, fast, low memory footprint RFC compliance IPv4 and IPv6 support Package: libre-dev Architecture: any Section: libdevel Depends: libre (= ${binary:Version}), ${misc:Depends} Description: Generic library for real-time communications (development files) Libre is a portable and generic library for real-time communications with async IO support and a complete SIP stack with support for protocols such as SDP, RTP/RTCP, STUN/TURN/ICE, BFCP, HTTP and DNS Client. . The current features are: . SIP Stack (RFC 3261) SDP parser RTP and RTCP stack DNS-Client STUN/TURN/ICE stack BFCP Jitter-buffer Async I/O (poll, epoll, select) UDP/TCP/TLS/DTLS transport HTTP Client and Server Websocket Client and Server JSON parser SRTP and SRTCP (Secure RTP) . Design goals: . Portable POSIX source code (ANSI C89 and ISO C99 standard) Robust, fast, low memory footprint RFC compliance IPv4 and IPv6 support . This package is only the library development files. re-1.1.0/debian/copyright000066400000000000000000000031611373627245400153120ustar00rootroot00000000000000This package was debianized by Alfred E. Heggestad It was downloaded from http://www.creytiv.com/ Copyright (c) 2010 - 2018, Alfred E. Heggestad Copyright (c) 2010 - 2018, Richard Aas Copyright (c) 2010 - 2018, Creytiv.com 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. Neither the name of the Creytiv.com 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 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. re-1.1.0/debian/docs000066400000000000000000000000241373627245400142250ustar00rootroot00000000000000README.md docs/TODO re-1.1.0/debian/libre-dev.dirs000066400000000000000000000000361373627245400161110ustar00rootroot00000000000000usr/include usr/lib usr/share re-1.1.0/debian/libre-dev.files000066400000000000000000000000511373627245400162470ustar00rootroot00000000000000usr/include usr/lib/libre.a usr/share/re re-1.1.0/debian/libre.dirs000066400000000000000000000000101373627245400153250ustar00rootroot00000000000000usr/lib re-1.1.0/debian/libre.files000066400000000000000000000000221373627245400154710ustar00rootroot00000000000000/usr/lib/libre.so re-1.1.0/debian/rules000077500000000000000000000037721373627245400144470ustar00rootroot00000000000000#!/usr/bin/make -f # -*- makefile -*- # Sample debian/rules that uses debhelper. # This file was originally written by Joey Hess and Craig Small. # As a special exception, when this file is copied by dh-make into a # dh-make output file, you may use that output file without restriction. # This special exception was added by Craig Small in version 0.37 of dh-make. # Uncomment this to turn on verbose mode. #export DH_VERBOSE=1 EXTRA_CFLAGS:="$(shell dpkg-buildflags --get CFLAGS | sed -e 's/-O2//')" EXTRA_LFLAGS:="$(shell dpkg-buildflags --get LDFLAGS) -Wl,-soname,libre.so" configure: configure-stamp configure-stamp: dh_testdir # Add here commands to configure the package. touch configure-stamp build: build-stamp build-stamp: configure-stamp dh_testdir # Add here commands to compile the package. $(MAKE) RELEASE=1 \ EXTRA_CFLAGS=$(EXTRA_CFLAGS) \ EXTRA_LFLAGS=$(EXTRA_LFLAGS) touch $@ clean: dh_testdir dh_testroot rm -f build-stamp configure-stamp # Add here commands to clean up after the build process. $(MAKE) clean dh_clean install: build dh_testdir dh_testroot dh_prep dh_installdirs # Add here commands to install the package into debian/tmp mkdir $(CURDIR)/debian/tmp $(MAKE) install DESTDIR=$(CURDIR)/debian/tmp dh_movefiles # Build architecture-independent files here. binary-indep: build install # We have nothing to do by default. # Build architecture-dependent files here. binary-arch: build install dh_testdir dh_testroot dh_installchangelogs dh_installdocs dh_installexamples # dh_install # dh_installmenu # dh_installdebconf # dh_installlogrotate # dh_installemacsen # dh_installpam # dh_installmime # dh_installinit # dh_installcron # dh_installinfo dh_installman dh_link dh_strip dh_compress dh_fixperms # dh_perl # dh_python dh_makeshlibs dh_installdeb dh_shlibdeps dh_gencontrol dh_md5sums dh_builddeb build-arch: build build-indep: build binary: binary-indep binary-arch .PHONY: build clean binary-indep binary-arch binary install configure re-1.1.0/debian/source/000077500000000000000000000000001373627245400146565ustar00rootroot00000000000000re-1.1.0/debian/source/format000066400000000000000000000000041373627245400160630ustar00rootroot000000000000001.0 re-1.1.0/docs/000077500000000000000000000000001373627245400130645ustar00rootroot00000000000000re-1.1.0/docs/COPYING000066400000000000000000000027771373627245400141340ustar00rootroot00000000000000Copyright (c) 2010 - 2019, Alfred E. Heggestad Copyright (c) 2010 - 2019, Richard Aas Copyright (c) 2010 - 2019, Creytiv.com 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. Neither the name of the Creytiv.com 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 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. re-1.1.0/docs/ChangeLog000066400000000000000000000427741373627245400146540ustar00rootroot000000000000002019-09-07 Alfred E. Heggestad * Version 0.6.1 Aleksei (1): Update MSVS project (Remove uri_cmp) (#209) Alfred E. Heggestad (10): rtmp: update timestamp after complete packet (#172) rtmp: add rtmp_meta() to send metadata on stream (#173) remove gai_strerror stub (#174) rtmp: add function to set handlers (#180) rtmp: proper handling of extended timestamps (#184) docs: update copyright year (#186) debian: fix some warnings and add version to SONAME (#192) uri: remove uri_cmp() (#199) rtmp: add rtmps (tls) support (#195) Update README.md (#205) Andrey Semashev (1): Fix libdir in pkgconfig (#185) Lennart Grahl (1): Raise FD_EXCEPT on EPOLLHUP (fixes closed pipes) (#159) Richard Aas (9): debian release 0.6.0-1 debian release 0.6.0-2 debian release 0.6.0-3 dns: fix dname decode buffer checks (#197) regex: fix character buffer check (#198) Debian release 0.6.0-4 tls: don't close connection on SSL_ERROR_ZERO_RETURN error (#217) DNS TXT resource record support (#219) debian release 0.6.0-6 Steffen Vogel (1): rpm: add missing file to %install section (#178) 2018-11-24 Alfred E. Heggestad * Version 0.6.0 * Project URL: https://github.com/creytiv/re * build: add major,minor,patch versions to CFLAGS * odict: add high-level odict helper functions * rtmp: new module for Real Time Messaging Protocol (RTMP) * uri: add uri_decode_hostport 2018-09-01 Alfred E. Heggestad * Version 0.5.9 * Project URL: https://github.com/creytiv/re * build: Added support for 64-bit MINGW (#131) (thanks Alexander Ushakov) fixed inline issue when compiling VS as C++ (#143) (thanks TheSil) * jbuf: zero out jbuf_stat on jbuf flush (#147) (thanks Christian Spielberger) * net: remove net_conn api (old and unused) (#145) fix bug in net_if_getname (#144) * sip: get local TCP address in establish handler (#146) * tls: add AES-GCM to DTLS-SRTP (#141) 2018-04-20 Alfred E. Heggestad * Version 0.5.8 * Project URL: https://github.com/creytiv/re * build: update win32 files (thanks Encamy) * aes: add support for AES-GCM (Galois Counter Mode) * fmt: json/utf8: fix unescaping of unicode code points (#127) add utf8_byteseq * mqueue: set non-blocking mode for read/write file descriptors (#122) * srtp: add support for AES-GCM cipher suite (RFC 7714) 2018-01-12 Alfred E. Heggestad * Version 0.5.7 * Project URL: https://github.com/creytiv/re * build: remove support for Cygwin (#95) remove support for splint (#96) * mem: add secure memory functions (#102) * net: larger buffer for net_if_list (#100) * sipreg: add from_name (Display Name) (#104) * tls: use per connection bio_method (fixes issue #92) (#93) 2017-11-06 Alfred E. Heggestad * Version 0.5.6 * Project URL: https://github.com/creytiv/re * build: Update ar flags; use deterministic mode (#79) * http: added support for chunked transfer encoding (#90) * ice: Added functions to get selected candidates. (#72) (thanks Joachim Bauch) * json: improved performance for mypower10 (#88) (thanks Chris Owen) * mqueue: Pack struct of mqueue messages. (#62) (thanks Joachim Bauch) * odict: use int instead of enum to avoid vararg promotion (#81) * tls: add dtls_recv_packet() (#89) 2017-09-05 Alfred E. Heggestad * Version 0.5.5 * Project URL: https://github.com/creytiv/re * ice: move gathering to application * mod: add accessor function to module list * sipreg: Added function sipreg_laddr() * sys: optimize rand_str() and rand_char() 2017-06-24 Alfred E. Heggestad * Version 0.5.4 * Project URL: https://github.com/creytiv/re * rtp: add extension bit to the api 2017-05-13 Alfred E. Heggestad * Version 0.5.3 * Project URL: https://github.com/creytiv/re * build: upgrade windows project to VS2015 (thanks Mikhail Barg) makefile improvements (thanks Lennart Grahl) * ice: remove session object "struct ice" * telev: add telev_set_srate (thanks Jan Hoffmann) 2017-04-08 Alfred E. Heggestad * Version 0.5.2 * Project URL: https://github.com/creytiv/re * build: add Debian/kFreeBSD+Hurd (thanks Vasudev Kamath) * ice: make enum ice_role type public * main: fix build for Solaris 11 * srtcp: use unsigned for encrypted bit * tls: add tls_openssl_context() accessor function 2017-02-04 Alfred E. Heggestad * Version 0.5.1 * Project URL: https://github.com/creytiv/re * fmt: print directly to stream using handler (#38) * http: HTTP client improvements (#36) - http client connection reuse - retry failed requests using fresh connections - Handle Connection: close response header 2016-11-25 Alfred E. Heggestad * Version 0.5.0 * Project URL: https://github.com/creytiv/re * build: add Dragonfly BSD (thanks Dmitrij D. Czarkoff) remove support for Symbian OS * aes: add support for OpenSSL version 1.1.0 * dns: dns/resolv cleanup (#11) (thanks Dmitrij D. Czarkoff) * hmac: add support for OpenSSL version 1.1.0 * main: remove support for ActiveScheduler (SymbianOS) * tls: add support for OpenSSL version 1.1.0 add tls_set_certificate_pem() add tls_set_certificate_der() add dtls_peer() add dtls_set_peer() add tls_flush_error to dump openssl errors (thanks to Lennart Grahl) * udp: add udp_helper_find() 2016-06-24 Alfred E. Heggestad * Version 0.4.17 * build: add USE_OPENSSL_AES and USE_OPENSSL_HMAC * dns: add key to dns_rrlist_sort() add dns_rrlist_sort_addr * tls: add tls_set_ciphers() add tls_set_servername() * sip: fix for stateless SIP requests sort DNS RR entries by a fixed key * stun: fix bug with 8-bit and 16-bit attributes on certain platforms, such as MIPS 2016-04-27 Alfred E. Heggestad * Version 0.4.16 * build: fix warnings about DEFAULT_SOURCE with new glibc * lock: fix debian build without HAVE_PTHREAD_RWLOCK * rand: add arc4random (based on patch from Dmitrij D. Czarkoff) * rtcp: adjust mbuf positions for RTCP_PSFB_AFB decoding * tls: add tls_cipher_name() add dtls_set_mtu() 2016-02-06 Alfred E. Heggestad * Version 0.4.15 * build: fix warnings about DEFAULT_SOURCE with new glibc fix compile error for mingw32 (thanks Dmitrij D. Czarkoff) * aes: handle buffers with zero length * dns: add support for multiple DNS name-servers on Android * hmac: add support for HMAC-SHA256 * rtp: fix packet-loss calc when first packet has seq=0 * srtp: split error code in ENOSR and ENOMEM * stun: keepalive: handle method BINDING only * telev: add a maximum queue size * uri: fix a potential read buffer overflow 2015-10-24 Alfred E. Heggestad * Version 0.4.14 * New modules: json and odict * build: add pkg-config file (thanks to William King) * re_types: added a portable __REFUNC__ * fmt: add utf8_encode/decode, used by JSON module * hash: add hash_fast() function * json: new JavaScript Object Notation (JSON) module * main: fix order of kqueue setting events (WRITE,READ) * odict: new Ordered Dictionary module * sip: reverse order of transport enumeration for SRV-records * udp,tcp,net: add __USE_XOPEN2K (thanks Dmitrij D. Czarkoff) 2015-07-01 Alfred E. Heggestad * Version 0.4.13 * aes: added support for CommonCrypto API * fmt: pl_float() handles negative numbers now * hmac: added support for CommonCrypto API * main: added support for async I/O method `kqueue' this is now the default on platforms like OSX/iOS, FreeBSD, NetBSD and OpenBSD. * mem: added mem_reallocarray(), inspired by OpenBSD * net: added net_default_gateway_get() * tls: use RSA_generate_key_ex() instead of deprecated functions 2015-03-16 Alfred E. Heggestad * Version 0.4.12 * ice: added ice_ prefix to some functions and types fix bug in priority calculations (thanks to Daniel Ma) * mqueue: fix bug with leaking sockets on Windows-32 * rtp: fix bug with RTCP timestamp calculation * sip: export sip_transp_laddr() * tls: added more TLS methods 2014-12-09 Alfred E. Heggestad * Version 0.4.11 * build: export USE_TLS and USE_DTLS flags in re.mk makefile detect sysctl.h and epoll.h for multi-arch platforms dont use libresolv for openbsd * main: check that maxfds is less than FD_SETSIZE (for select method) * dtls: added udp-socket accessor and function to set handlers * stun: added support for DTLS-transport added doxygen comments * tls: added function to set certificate from a string * turn: added support for DTLS-transport 2014-10-19 Alfred E. Heggestad * Version 0.4.10 * dns: added support for using multi-threaded libresolv (thanks to Thomas Klausner) (thanks to Dmitrij D. Czarkoff for testing on OpenBSD) * dtls: added support for sending DTLS over e.g. TURN (this is done by adding 4 bytes of headroom in the packet) * ice: added ice_set_conf() continue checklist if send fails (thanks to SnakE) * mbuf: added mbuf_shift() * sdp: added sdp_media_session_rattr() added extmap decoding RFC 5285 (thanks to Jose Carlos Pujol) * sip: added struct sip_contact and related functions * sipevent: added support for URI in contact-user, used for GRUU (thanks to Juha Heinanen) * sipreg: added "gruu" to list of Supported extensions * sipsess: added support for URI in contact-user, used for GRUU (thanks to Juha Heinanen) 2014-06-18 Alfred E. Heggestad * Version 0.4.9 * aes: clear openssl error queue in error cases * bfcp: disabled support for DTLS-transport * hmac: clear openssl error queue in error cases * http: make response parsing a bit more robust * main: make use of openssl's multi-threading API * rtcp: added Round-Trip Time (RTT) field to struct rtcp_stats fix some rounding errors * sa: added padding buffer to struct sa union * sdp: improved handling of unsupported transport protocols and sub-sequence offer/answer exchanges * srtp: added support for Secure Real-time Transport Protocol (SRTP) (RFC 3711 and RFC 6188) * sys: rand -- clear openssl error queue in error cases * tls: added support for generating self-signed certificates added support for the SRTP-extension using openssl clear openssl error queue in error cases improved DTLS api 2014-04-11 Alfred E. Heggestad * Version 0.4.8 * build: added support for Apple in cmake * debian: update package * aes: added AES (Advanced Encryption Standard) wrapper * hmac: added a stateful HMAC wrapper * http: added a HTTP client * ice: minor API improvements * msg: added a Generic message component library (shared by SIP module and HTTP module) * sdp: added sdp_media_laddr() to get local transport address * sip: change struct sip_msg to use new struct msg_ctype fixed a bug in parsing of Via headers * sipsess: added sipsess_set_close_headers() to set any additional SIP headers for BYE or BYE-response * net: fixed a bug in net_rt_list() for darwin * websock: added WebSocket Protocol (RFC 6455) 2014-01-05 Alfred E. Heggestad * Version 0.4.7 * build: added support for LLVM clang compiler * dns: added support for getting Android nameserver address * ice: minor debug tuning * sipsess: only send INFO when dialog is established 2013-11-12 Alfred E. Heggestad * Version 0.4.6 * bfcp: fix bitwise operator for bool (thanks Tomasz Ostrowski) * dns: do not connect the UDP socket * ice: fix deref of NULL-pointer (thanks Tomasz Ostrowski) * rtp: add support for RTCP AFB (Application-layer Feedback) make RTCP decoding more robust * udp: udp_connect() -- add peer address add udp_error_handler_set() 2013-10-03 Alfred E. Heggestad * Version 0.4.5 * udp: add functions for joining/leaving multicast groups * fmt: re_regex() fix va_end robustness * rtp: set sequence number in range 0-32767 * sa: sa_print_addr() fix build when HAVE_INET6 is not set 2013-08-27 Alfred E. Heggestad * Version 0.4.4 * base64: added base64_print() * http: added HTTP (Hypertext Transfer Protocol) parser * ice: cleanup and minor bug fixes * main: added external mutex for re_main() loop * sdp: added sdp_media_set_alt_protos() added sdp_media_proto() * stun: make API compatible with C++ fix endianess-bug in STUN attributes * sys: sys_rel_get() detect new kernels * tls: fingerprint: add SHA-256 proper error handling, call ERR_clear_error() 2013-05-05 Alfred E. Heggestad * Version 0.4.3 * bfcp: added udp * dns: added doxygen comments * fmt: added str_cmp() * mbuf: make mbuf_get_left() and mbuf_get_space() more robust added mbuf_fill() * mod: fixed a bug in mod_find() * mqueue: move handler to mqueue_alloc() * sa: fix building on some Windows platforms * sdp: added sdp_session_lbandwidth() added sdp_media_set_fmt_ignore() * stun: make stun_msg_vencode() public unlink element from attribute-list in destructor * tcp: make fd handling more robust * tls: added tls_get_remote_fingerprint() 2012-08-10 Alfred E. Heggestad * Version 0.4.2 * added debian build * build: fix building for Ubuntu 12.04 * re_types: increase ERRNO values * fmt: re_printf() add support for %m to print errno description added str_error() * hash: added hash_clear() * list: added list_clear() * net: added net_if_getlinklocal() * rtp: added rtcp_set_srate_tx/rx() rtcp_msg_print(): add all types * sa: added sa_print_addr() * sdp: added media encode handler sdp_format_add(): added fmtp encode handler (breaks API) * sip: handle merged SIP requests (482 Loop Detected) added doxygen comments * sipevent: fix bug in handler argument * sys: added sys_username() added fs_mkdir() and fs_gethome() * tcp: added tcp_conn_txqsz() fix enqueue buffer size handle scopeid for IPv6 linklocal * tmr: added tmr_status %H handler * udp: handle scopeid for IPv6 linklocal 2012-04-21 Alfred E. Heggestad * Version 0.4.1 * updated doxygen comments for sdp and tls * dns: dnsc_srv_set: copy DNS servers to fixed-size array * fmt: added str_isset() added fmt_param_exists() * rtp: fix lock protection of RTCP txstat during read * sdp: sdp_media_align_formats: move unsupported codecs to end of list * sip: limit startline to max 8192 bytes limit tcp buffersize to max 65536 bytes * tcp: limit the size of the tcp send queue 2011-12-25 Alfred E. Heggestad * Version 0.4.0 * updated doxygen comments * build: add support for CMake (thanks to Stefan Radomski) clean up OS and ARCH detection * dns: fix potential infinite loop in dname decode * sip: change struct sip_via transp to enum sip_transp (breaks API) added sip_transp_isladdr() and sip_transp_port() added sip_dialog_fork(), sip_dialog_lseq(), sip_dialog_established(), sip_dialog_cmp_half() * sipevent: new module for SIP Event framework (RFC 3265, RFC 3515) * sys: add portable sys_usleep() and sys_msleep() * tcp: add tcp_send_helper() * tls: add support for DTLSv1 (Datagram TLS) tls_alloc: add tls_method and layer (breaks API) tls_tcp: use custom BIO to send data * tmr: optimize tmr_start() where delay == 0 * turn: add stun_msg to turnc handler (breaks API) * udp: add udp_send_helper() 2011-09-07 Alfred E. Heggestad * Version 0.3.0 * build support for native mingw32 (thanks to Michael Erskine) * bfcp: new module for The Binary Floor Control Protocol (RFC 4582) * g711: module moved to librem * sipreg: fix a bug in failwait() calculation * stun: add support for STUNS (secure STUN) * tcp: added tcp_set_handlers() * turn: added send/recv functions 2011-05-20 Alfred E. Heggestad * Version 0.2.0 * updated doxygen comments * conf: added conf_get_bool() * dns: fixed a bug in get_resolv_dns() * fmt: added pl_x64() pl_float() fmt_gmtime() * hash: added hash_valid_size() * httpauth: clean up API * ice: many improvements and bugfixes * main: fix a bug if re_main() fails * mbuf: added mbuf_debug() * natbd: fixed some race conditions and memory leaks * rtp: added rtcp_enable_mux() (RFC 5761; RTP and RTCP multiplexing) * sdp: fixed setting RTCP port if RTP port is zero * sip: added support for SIP Outbound (RFC 5626) added sip_msg_hdr_count() sip_msg_xhdr_count() added sip_msg_hdr_has_value() sip_msg_xhdr_has_value() added sip_auth_reset() handle multiple authenticate headers with equal realm value fixed a bug with loose-routing in Route header fixed decoding of Via header * sipreg: added support for SIP Outbound (breaks API compatibility) * sipsess: fix a bug in sipsess_reject() if fmt is NULL * tcp: update tcp_register_helper() (breaks API) * tmr: removed tmrl from 'struct tmr' (breaks ABI) added tmr_isrunning() * udp: update udp_register_helper() (breaks API) * uri: fix optional username in uri_decode() 2010-11-05 Alfred E. Heggestad * Version 0.1.0 * Initial Release re-1.1.0/docs/TODO000066400000000000000000000003401373627245400135510ustar00rootroot00000000000000TODO ------------------------------------------------------------------------------- Version v0.x.y tmr: scaling using binary heap or hash ------------------------------------------------------------------------------- re-1.1.0/docs/main.dox000066400000000000000000000002361373627245400145250ustar00rootroot00000000000000/** * \mainpage libre Development Documentation * * Development documentation for libre * * * \include README.md * * * \section modules modules */ re-1.1.0/docs/symbols.txt000066400000000000000000000065271373627245400153270ustar00rootroot00000000000000BIO_new@@OPENSSL_0.9.8 BIO_new_socket@@OPENSSL_0.9.8 BIO_s_mem@@OPENSSL_0.9.8 BIO_write@@OPENSSL_0.9.8 ERR_free_strings@@OPENSSL_0.9.8 ERR_get_error@@OPENSSL_0.9.8 EVP_sha1@@OPENSSL_0.9.8 HMAC@@OPENSSL_0.9.8 MD5@@OPENSSL_0.9.8 RAND_bytes@@OPENSSL_0.9.8 SSL_CTX_free@@OPENSSL_0.9.8 SSL_CTX_load_verify_locations@@OPENSSL_0.9.8 SSL_CTX_new@@OPENSSL_0.9.8 SSL_CTX_set_default_passwd_cb@@OPENSSL_0.9.8 SSL_CTX_set_default_passwd_cb_userdata@@OPENSSL_0.9.8 SSL_CTX_use_PrivateKey_file@@OPENSSL_0.9.8 SSL_CTX_use_certificate_chain_file@@OPENSSL_0.9.8 SSL_accept@@OPENSSL_0.9.8 SSL_connect@@OPENSSL_0.9.8 SSL_free@@OPENSSL_0.9.8 SSL_get_error@@OPENSSL_0.9.8 SSL_get_peer_certificate@@OPENSSL_0.9.8 SSL_get_verify_result@@OPENSSL_0.9.8 SSL_library_init@@OPENSSL_0.9.8 SSL_load_error_strings@@OPENSSL_0.9.8 SSL_new@@OPENSSL_0.9.8 SSL_read@@OPENSSL_0.9.8 SSL_set_bio@@OPENSSL_0.9.8 SSL_shutdown@@OPENSSL_0.9.8 SSL_state@@OPENSSL_0.9.8 SSL_write@@OPENSSL_0.9.8 SSLv23_method@@OPENSSL_0.9.8 X509_NAME_get_text_by_NID@@OPENSSL_0.9.8 X509_get_subject_name@@OPENSSL_0.9.8 __ctype_b_loc@@GLIBC_2.3 __errno_location@@GLIBC_2.2.5 __isinf@@GLIBC_2.2.5 __isnan@@GLIBC_2.2.5 __isoc99_fscanf@@GLIBC_2.7 __res_close@@GLIBC_2.2.5 __res_init@@GLIBC_2.2.5 __res_state@@GLIBC_2.2.5 __sysv_signal@@GLIBC_2.2.5 accept@@GLIBC_2.2.5 atoi@@GLIBC_2.2.5 bind@@GLIBC_2.2.5 chdir@@GLIBC_2.2.5 close@@GLIBC_2.2.5 connect@@GLIBC_2.2.5 crc32 ctime@@GLIBC_2.2.5 dlclose@@GLIBC_2.2.5 dlerror@@GLIBC_2.2.5 dlopen@@GLIBC_2.2.5 dlsym@@GLIBC_2.2.5 epoll_create@@GLIBC_2.3.2 epoll_ctl@@GLIBC_2.3.2 epoll_wait@@GLIBC_2.3.2 exit@@GLIBC_2.2.5 fclose@@GLIBC_2.2.5 fcntl@@GLIBC_2.2.5 fflush@@GLIBC_2.2.5 fopen@@GLIBC_2.2.5 fork@@GLIBC_2.2.5 free@@GLIBC_2.2.5 freeaddrinfo@@GLIBC_2.2.5 freeifaddrs@@GLIBC_2.3 freopen@@GLIBC_2.2.5 fwrite@@GLIBC_2.2.5 gai_strerror@@GLIBC_2.2.5 getaddrinfo@@GLIBC_2.2.5 gethostbyname@@GLIBC_2.2.5 gethostname@@GLIBC_2.2.5 getifaddrs@@GLIBC_2.3 getpeername@@GLIBC_2.2.5 getpid@@GLIBC_2.2.5 getsockname@@GLIBC_2.2.5 getsockopt@@GLIBC_2.2.5 gettimeofday@@GLIBC_2.2.5 htonl@@GLIBC_2.2.5 htons@@GLIBC_2.2.5 if_indextoname@@GLIBC_2.2.5 inet_ntop@@GLIBC_2.2.5 inet_pton@@GLIBC_2.2.5 ioctl@@GLIBC_2.2.5 listen@@GLIBC_2.2.5 malloc@@GLIBC_2.2.5 memcmp@@GLIBC_2.2.5 memcpy@@GLIBC_2.2.5 memmove@@GLIBC_2.2.5 memset@@GLIBC_2.2.5 ntohl@@GLIBC_2.2.5 ntohs@@GLIBC_2.2.5 open@@GLIBC_2.2.5 pipe@@GLIBC_2.2.5 poll@@GLIBC_2.2.5 pthread_getspecific@@GLIBC_2.2.5 pthread_key_create@@GLIBC_2.2.5 pthread_mutex_init@@GLIBC_2.2.5 pthread_mutex_lock@@GLIBC_2.2.5 pthread_mutex_unlock@@GLIBC_2.2.5 pthread_once@@GLIBC_2.2.5 pthread_rwlock_destroy@@GLIBC_2.2.5 pthread_rwlock_init@@GLIBC_2.2.5 pthread_rwlock_rdlock@@GLIBC_2.2.5 pthread_rwlock_tryrdlock@@GLIBC_2.2.5 pthread_rwlock_trywrlock@@GLIBC_2.2.5 pthread_rwlock_unlock@@GLIBC_2.2.5 pthread_rwlock_wrlock@@GLIBC_2.2.5 pthread_self@@GLIBC_2.2.5 pthread_setspecific@@GLIBC_2.2.5 read@@GLIBC_2.2.5 realloc@@GLIBC_2.2.5 recv@@GLIBC_2.2.5 recvfrom@@GLIBC_2.2.5 select@@GLIBC_2.2.5 send@@GLIBC_2.2.5 sendto@@GLIBC_2.2.5 setrlimit@@GLIBC_2.2.5 setsid@@GLIBC_2.2.5 setsockopt@@GLIBC_2.2.5 socket@@GLIBC_2.2.5 srand@@GLIBC_2.2.5 stderr@@GLIBC_2.2.5 stdin@@GLIBC_2.2.5 stdout@@GLIBC_2.2.5 strcasecmp@@GLIBC_2.2.5 strcmp@@GLIBC_2.2.5 strerror@@GLIBC_2.2.5 strlen@@GLIBC_2.2.5 strncasecmp@@GLIBC_2.2.5 strncpy@@GLIBC_2.2.5 time@@GLIBC_2.2.5 tolower@@GLIBC_2.2.5 umask@@GLIBC_2.2.5 uname@@GLIBC_2.2.5 write@@GLIBC_2.2.5 re-1.1.0/include/000077500000000000000000000000001373627245400135575ustar00rootroot00000000000000re-1.1.0/include/re.h000066400000000000000000000022721373627245400143410ustar00rootroot00000000000000/** * @file re.h Wrapper for all header files * * Copyright (C) 2010 Creytiv.com */ #ifndef RE_H__ #define RE_H__ #ifdef __cplusplus extern "C" { #endif /* Basic types */ #include "re_types.h" #include "re_fmt.h" #include "re_mbuf.h" #include "re_msg.h" #include "re_list.h" #include "re_sa.h" /* Library modules */ #include "re_aes.h" #include "re_base64.h" #include "re_bfcp.h" #include "re_conf.h" #include "re_crc32.h" #include "re_dns.h" #include "re_hash.h" #include "re_hmac.h" #include "re_http.h" #include "re_httpauth.h" #include "re_ice.h" #include "re_jbuf.h" #include "re_lock.h" #include "re_main.h" #include "re_md5.h" #include "re_mem.h" #include "re_mod.h" #include "re_mqueue.h" #include "re_net.h" #include "re_odict.h" #include "re_json.h" #include "re_rtmp.h" #include "re_rtp.h" #include "re_sdp.h" #include "re_uri.h" #include "re_sip.h" #include "re_sipevent.h" #include "re_sipreg.h" #include "re_sipsess.h" #include "re_stun.h" #include "re_natbd.h" #include "re_srtp.h" #include "re_sys.h" #include "re_tcp.h" #include "re_telev.h" #include "re_tmr.h" #include "re_tls.h" #include "re_turn.h" #include "re_udp.h" #include "re_websock.h" #ifdef __cplusplus } #endif #endif re-1.1.0/include/re_aes.h000066400000000000000000000014341373627245400151700ustar00rootroot00000000000000/** * @file re_aes.h Interface to AES (Advanced Encryption Standard) * * Copyright (C) 2010 Creytiv.com */ #ifndef AES_BLOCK_SIZE #define AES_BLOCK_SIZE 16 #endif /** AES mode */ enum aes_mode { AES_MODE_CTR, /**< AES Counter mode (CTR) */ AES_MODE_GCM, /**< AES Galois Counter Mode (GCM) */ }; struct aes; int aes_alloc(struct aes **stp, enum aes_mode mode, const uint8_t *key, size_t key_bits, const uint8_t *iv); void aes_set_iv(struct aes *aes, const uint8_t *iv); int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len); int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len); int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen); int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen); re-1.1.0/include/re_base64.h000066400000000000000000000005251373627245400155040ustar00rootroot00000000000000/** * @file re_base64.h Interface to Base64 encoding/decoding functions * * Copyright (C) 2010 Creytiv.com */ int base64_encode(const uint8_t *in, size_t ilen, char *out, size_t *olen); int base64_print(struct re_printf *pf, const uint8_t *ptr, size_t len); int base64_decode(const char *in, size_t ilen, uint8_t *out, size_t *olen); re-1.1.0/include/re_bfcp.h000066400000000000000000000154441373627245400153400ustar00rootroot00000000000000/** * @file re_bfcp.h Interface to Binary Floor Control Protocol (BFCP) * * Copyright (C) 2010 Creytiv.com */ /** BFCP Versions */ enum { BFCP_VER1 = 1, BFCP_VER2 = 2, }; /** BFCP Primitives */ enum bfcp_prim { BFCP_FLOOR_REQUEST = 1, BFCP_FLOOR_RELEASE = 2, BFCP_FLOOR_REQUEST_QUERY = 3, BFCP_FLOOR_REQUEST_STATUS = 4, BFCP_USER_QUERY = 5, BFCP_USER_STATUS = 6, BFCP_FLOOR_QUERY = 7, BFCP_FLOOR_STATUS = 8, BFCP_CHAIR_ACTION = 9, BFCP_CHAIR_ACTION_ACK = 10, BFCP_HELLO = 11, BFCP_HELLO_ACK = 12, BFCP_ERROR = 13, BFCP_FLOOR_REQ_STATUS_ACK = 14, BFCP_FLOOR_STATUS_ACK = 15, BFCP_GOODBYE = 16, BFCP_GOODBYE_ACK = 17, }; /** BFCP Attributes */ enum bfcp_attrib { BFCP_BENEFICIARY_ID = 1, BFCP_FLOOR_ID = 2, BFCP_FLOOR_REQUEST_ID = 3, BFCP_PRIORITY = 4, BFCP_REQUEST_STATUS = 5, BFCP_ERROR_CODE = 6, BFCP_ERROR_INFO = 7, BFCP_PART_PROV_INFO = 8, BFCP_STATUS_INFO = 9, BFCP_SUPPORTED_ATTRS = 10, BFCP_SUPPORTED_PRIMS = 11, BFCP_USER_DISP_NAME = 12, BFCP_USER_URI = 13, /* grouped: */ BFCP_BENEFICIARY_INFO = 14, BFCP_FLOOR_REQ_INFO = 15, BFCP_REQUESTED_BY_INFO = 16, BFCP_FLOOR_REQ_STATUS = 17, BFCP_OVERALL_REQ_STATUS = 18, /** Mandatory Attribute */ BFCP_MANDATORY = 1<<7, /** Encode Handler */ BFCP_ENCODE_HANDLER = 1<<8, }; /** BFCP Request Status */ enum bfcp_reqstat { BFCP_PENDING = 1, BFCP_ACCEPTED = 2, BFCP_GRANTED = 3, BFCP_DENIED = 4, BFCP_CANCELLED = 5, BFCP_RELEASED = 6, BFCP_REVOKED = 7 }; /** BFCP Error Codes */ enum bfcp_err { BFCP_CONF_NOT_EXIST = 1, BFCP_USER_NOT_EXIST = 2, BFCP_UNKNOWN_PRIM = 3, BFCP_UNKNOWN_MAND_ATTR = 4, BFCP_UNAUTH_OPERATION = 5, BFCP_INVALID_FLOOR_ID = 6, BFCP_FLOOR_REQ_ID_NOT_EXIST = 7, BFCP_MAX_FLOOR_REQ_REACHED = 8, BFCP_USE_TLS = 9, BFCP_PARSE_ERROR = 10, BFCP_USE_DTLS = 11, BFCP_UNSUPPORTED_VERSION = 12, BFCP_BAD_LENGTH = 13, BFCP_GENERIC_ERROR = 14, }; /** BFCP Priority */ enum bfcp_priority { BFCP_PRIO_LOWEST = 0, BFCP_PRIO_LOW = 1, BFCP_PRIO_NORMAL = 2, BFCP_PRIO_HIGH = 3, BFCP_PRIO_HIGHEST = 4 }; /** BFCP Transport */ enum bfcp_transp { BFCP_UDP, BFCP_DTLS, }; /** BFCP Request Status */ struct bfcp_reqstatus { enum bfcp_reqstat status; uint8_t qpos; }; /** BFCP Error Code */ struct bfcp_errcode { enum bfcp_err code; uint8_t *details; /* optional */ size_t len; }; /** BFCP Supported Attributes */ struct bfcp_supattr { enum bfcp_attrib *attrv; size_t attrc; }; /** BFCP Supported Primitives */ struct bfcp_supprim { enum bfcp_prim *primv; size_t primc; }; /** BFCP Attribute */ struct bfcp_attr { struct le le; struct list attrl; enum bfcp_attrib type; bool mand; union bfcp_union { /* generic types */ char *str; uint16_t u16; /* actual attributes */ uint16_t beneficiaryid; uint16_t floorid; uint16_t floorreqid; enum bfcp_priority priority; struct bfcp_reqstatus reqstatus; struct bfcp_errcode errcode; char *errinfo; char *partprovinfo; char *statusinfo; struct bfcp_supattr supattr; struct bfcp_supprim supprim; char *userdname; char *useruri; uint16_t reqbyid; } v; }; /** BFCP unknown attributes */ struct bfcp_unknown_attr { uint8_t typev[16]; size_t typec; }; /** BFCP Message */ struct bfcp_msg { struct bfcp_unknown_attr uma; struct sa src; uint8_t ver; unsigned r:1; unsigned f:1; enum bfcp_prim prim; uint16_t len; uint32_t confid; uint16_t tid; uint16_t userid; struct list attrl; }; struct tls; struct bfcp_conn; /** * Defines the BFCP encode handler * * @param mb Mbuf to encode into * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ typedef int (bfcp_encode_h)(struct mbuf *mb, void *arg); /** BFCP Encode */ struct bfcp_encode { bfcp_encode_h *ench; void *arg; }; /** * Defines the BFCP attribute handler * * @param attr BFCP attribute * @param arg Handler argument * * @return True to stop processing, false to continue */ typedef bool (bfcp_attr_h)(const struct bfcp_attr *attr, void *arg); /** * Defines the BFCP receive handler * * @param msg BFCP message * @param arg Handler argument */ typedef void (bfcp_recv_h)(const struct bfcp_msg *msg, void *arg); /** * Defines the BFCP response handler * * @param err Error code * @param msg BFCP message * @param arg Handler argument */ typedef void (bfcp_resp_h)(int err, const struct bfcp_msg *msg, void *arg); /* attr */ int bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap); int bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...); struct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr, enum bfcp_attrib type); struct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr, bfcp_attr_h *h, void *arg); int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr); const char *bfcp_attr_name(enum bfcp_attrib type); const char *bfcp_reqstatus_name(enum bfcp_reqstat status); const char *bfcp_errcode_name(enum bfcp_err code); /* msg */ int bfcp_msg_vencode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim, uint32_t confid, uint16_t tid, uint16_t userid, unsigned attrc, va_list *ap); int bfcp_msg_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim, uint32_t confid, uint16_t tid, uint16_t userid, unsigned attrc, ...); int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb); struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg, enum bfcp_attrib type); struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg, bfcp_attr_h *h, void *arg); int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg); const char *bfcp_prim_name(enum bfcp_prim prim); /* conn */ int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr, struct tls *tls, bfcp_recv_h *recvh, void *arg); void *bfcp_sock(const struct bfcp_conn *bc); /* request */ int bfcp_request(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver, enum bfcp_prim prim, uint32_t confid, uint16_t userid, bfcp_resp_h *resph, void *arg, unsigned attrc, ...); /* notify */ int bfcp_notify(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver, enum bfcp_prim prim, uint32_t confid, uint16_t userid, unsigned attrc, ...); /* reply */ int bfcp_reply(struct bfcp_conn *bc, const struct bfcp_msg *req, enum bfcp_prim prim, unsigned attrc, ...); int bfcp_edreply(struct bfcp_conn *bc, const struct bfcp_msg *req, enum bfcp_err code, const uint8_t *details, size_t len); int bfcp_ereply(struct bfcp_conn *bc, const struct bfcp_msg *req, enum bfcp_err code); re-1.1.0/include/re_bitv.h000066400000000000000000000024401373627245400153620ustar00rootroot00000000000000/** * @file re_bitv.h Interface to Bit Vector functions * * Copyright (C) 2010 Creytiv.com */ typedef unsigned long bitv_t; enum { BITS_SZ = (8*sizeof(bitv_t)), BITS_MASK = (BITS_SZ-1) }; #define BITV_NELEM(nbits) (((nbits) + (BITS_SZ) - 1) / (BITS_SZ)) #define BITV_DECL(name, nbits) bitv_t (name)[BITV_NELEM(nbits)] static inline uint32_t index2offset(uint32_t i) { return i/BITS_SZ; } static inline bitv_t index2bit(uint32_t i) { return (bitv_t)1<<(i & BITS_MASK); } /* * Public API */ static inline void bitv_init(bitv_t *bv, uint32_t nbits, bool val) { memset(bv, val?0xff:0x00, BITV_NELEM(nbits)*sizeof(bitv_t)); } static inline void bitv_set(bitv_t *bv, uint32_t i) { bv[index2offset(i)] |= index2bit(i); } static inline void bitv_clr(bitv_t *bv, uint32_t i) { bv[index2offset(i)] &= ~index2bit(i); } static inline void bitv_assign(bitv_t *bv, uint32_t i, bool val) { if (val) bitv_set(bv, i); else bitv_clr(bv, i); } static inline bool bitv_val(const bitv_t *bv, uint32_t i) { return 0 != (bv[index2offset(i)] & index2bit(i)); } static inline void bitv_toggle(bitv_t *bv, uint32_t i) { bv[index2offset(i)] ^= index2bit(i); } static inline void bitv_assign_range(bitv_t *bv, uint32_t i, uint32_t n, bool val) { while (n--) bitv_assign(bv, i+n, val); } re-1.1.0/include/re_conf.h000066400000000000000000000012661373627245400153500ustar00rootroot00000000000000/** * @file re_conf.h Interface to configuration * * Copyright (C) 2010 Creytiv.com */ struct conf; typedef int (conf_h)(const struct pl *val, void *arg); int conf_alloc(struct conf **confp, const char *filename); int conf_alloc_buf(struct conf **confp, const uint8_t *buf, size_t sz); int conf_get(const struct conf *conf, const char *name, struct pl *pl); int conf_get_str(const struct conf *conf, const char *name, char *str, size_t size); int conf_get_u32(const struct conf *conf, const char *name, uint32_t *num); int conf_get_bool(const struct conf *conf, const char *name, bool *val); int conf_apply(const struct conf *conf, const char *name, conf_h *ch, void *arg); re-1.1.0/include/re_crc32.h000066400000000000000000000003171373627245400153330ustar00rootroot00000000000000/** * @file re_crc32.h Interface to CRC-32 functions * * Copyright (C) 2010 Creytiv.com */ #ifdef USE_ZLIB #include #else uint32_t crc32(uint32_t crc, const void *buf, uint32_t size); #endif re-1.1.0/include/re_dbg.h000066400000000000000000000077141373627245400151630ustar00rootroot00000000000000/** * @file re_dbg.h Interface to debugging module * * Copyright (C) 2010 Creytiv.com */ #ifdef __cplusplus extern "C" { #endif /** Debug levels */ enum { DBG_EMERG = 0, /**< System is unusable */ DBG_ALERT = 1, /**< Action must be taken immediately */ DBG_CRIT = 2, /**< Critical conditions */ DBG_ERR = 3, /**< Error conditions */ DBG_WARNING = 4, /**< Warning conditions */ DBG_NOTICE = 5, /**< Normal but significant condition */ DBG_INFO = 6, /**< Informational */ DBG_DEBUG = 7 /**< Debug-level messages */ }; /** * @def DEBUG_MODULE * * Module name defined by application */ /** * @def DEBUG_LEVEL * * Debug level defined by application */ #ifndef DEBUG_MODULE # warning "DEBUG_MODULE is not defined" #define DEBUG_MODULE "?" #endif #ifndef DEBUG_LEVEL # warning "DEBUG_LEVEL is not defined" #define DEBUG_LEVEL 7 #endif /** * @def DEBUG_WARNING(...) * * Print warning message */ /** * @def DEBUG_NOTICE(...) * * Print notice message */ /** * @def DEBUG_INFO(...) * * Print info message */ /** * @def DEBUG_PRINTF(...) * * Print debug message */ /* Check for ISO C99 variable argument macros */ #if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) \ || (__GNUC__ >= 3) #if (DEBUG_LEVEL >= 4) #define DEBUG_WARNING(...) \ dbg_printf(DBG_WARNING, DEBUG_MODULE ": " __VA_ARGS__) #else #define DEBUG_WARNING(...) #endif #if (DEBUG_LEVEL >= 5) #define DEBUG_NOTICE(...) \ dbg_printf(DBG_NOTICE, DEBUG_MODULE ": " __VA_ARGS__) #else #define DEBUG_NOTICE(...) #endif #if (DEBUG_LEVEL >= 6) #define DEBUG_INFO(...) \ dbg_printf(DBG_INFO, DEBUG_MODULE ": " __VA_ARGS__) #else #define DEBUG_INFO(...) #endif #if (DEBUG_LEVEL >= 7) #define DEBUG_PRINTF(...) \ dbg_printf(DBG_DEBUG, DEBUG_MODULE ": " __VA_ARGS__) #else #define DEBUG_PRINTF(...) #endif /* GNU extensions for variable argument macros */ #elif defined(__GNUC__) #if (DEBUG_LEVEL >= 4) #define DEBUG_WARNING(a...) dbg_printf(DBG_WARNING, DEBUG_MODULE ": " a) #else #define DEBUG_WARNING(a...) #endif #if (DEBUG_LEVEL >= 5) #define DEBUG_NOTICE(a...) dbg_printf(DBG_NOTICE, DEBUG_MODULE ": " a) #else #define DEBUG_NOTICE(a...) #endif #if (DEBUG_LEVEL >= 6) #define DEBUG_INFO(a...) dbg_printf(DBG_INFO, DEBUG_MODULE ": " a) #else #define DEBUG_INFO(a...) #endif #if (DEBUG_LEVEL >= 7) #define DEBUG_PRINTF(a...) dbg_printf(DBG_DEBUG, DEBUG_MODULE ": " a) #else #define DEBUG_PRINTF(a...) #endif /* No variable argument macros */ #else #if (DEBUG_LEVEL >= 4) #define DEBUG_WARNING dbg_warning #else #define DEBUG_WARNING dbg_noprintf #endif #if (DEBUG_LEVEL >= 5) #define DEBUG_NOTICE dbg_notice #else #define DEBUG_NOTICE dbg_noprintf #endif #if (DEBUG_LEVEL >= 6) #define DEBUG_INFO dbg_info #else #define DEBUG_INFO dbg_noprintf #endif #if (DEBUG_LEVEL >= 7) #define DEBUG_PRINTF dbg_noprintf #else #define DEBUG_PRINTF dbg_noprintf #endif #endif /** Debug flags */ enum dbg_flags { DBG_NONE = 0, /**< No debug flags */ DBG_TIME = 1<<0, /**< Print timestamp flag */ DBG_ANSI = 1<<1, /**< Print ANSI color codes */ DBG_ALL = DBG_TIME|DBG_ANSI /**< All flags enabled */ }; /** * Defines the debug print handler * * @param level Debug level * @param p Debug string * @param len String length * @param arg Handler argument */ typedef void (dbg_print_h)(int level, const char *p, size_t len, void *arg); void dbg_init(int level, enum dbg_flags flags); void dbg_close(void); int dbg_logfile_set(const char *name); void dbg_handler_set(dbg_print_h *ph, void *arg); void dbg_printf(int level, const char *fmt, ...); void dbg_noprintf(const char *fmt, ...); void dbg_warning(const char *fmt, ...); void dbg_notice(const char *fmt, ...); void dbg_info(const char *fmt, ...); const char *dbg_level_str(int level); #ifdef __cplusplus } #endif re-1.1.0/include/re_dns.h000066400000000000000000000124201373627245400152010ustar00rootroot00000000000000/** * @file re_dns.h Interface to DNS module * * Copyright (C) 2010 Creytiv.com */ enum { DNS_PORT = 53, DNS_HEADER_SIZE = 12 }; /** DNS Opcodes */ enum { DNS_OPCODE_QUERY = 0, DNS_OPCODE_IQUERY = 1, DNS_OPCODE_STATUS = 2, DNS_OPCODE_NOTIFY = 4 }; /** DNS Response codes */ enum { DNS_RCODE_OK = 0, DNS_RCODE_FMT_ERR = 1, DNS_RCODE_SRV_FAIL = 2, DNS_RCODE_NAME_ERR = 3, DNS_RCODE_NOT_IMPL = 4, DNS_RCODE_REFUSED = 5, DNS_RCODE_NOT_AUTH = 9 }; /** DNS Resource Record types */ enum { DNS_TYPE_A = 0x0001, DNS_TYPE_NS = 0x0002, DNS_TYPE_CNAME = 0x0005, DNS_TYPE_SOA = 0x0006, DNS_TYPE_PTR = 0x000c, DNS_TYPE_MX = 0x000f, DNS_TYPE_TXT = 0x0010, DNS_TYPE_AAAA = 0x001c, DNS_TYPE_SRV = 0x0021, DNS_TYPE_NAPTR = 0x0023, DNS_QTYPE_IXFR = 0x00fb, DNS_QTYPE_AXFR = 0x00fc, DNS_QTYPE_ANY = 0x00ff }; /** DNS Classes */ enum { DNS_CLASS_IN = 0x0001, DNS_QCLASS_ANY = 0x00ff }; /** Defines a DNS Header */ struct dnshdr { uint16_t id; bool qr; uint8_t opcode; bool aa; bool tc; bool rd; bool ra; uint8_t z; uint8_t rcode; uint16_t nq; uint16_t nans; uint16_t nauth; uint16_t nadd; }; /** Defines a DNS Resource Record (RR) */ struct dnsrr { struct le le; struct le le_priv; char *name; uint16_t type; uint16_t dnsclass; int64_t ttl; uint16_t rdlen; union { struct { uint32_t addr; } a; struct { char *nsdname; } ns; struct { char *cname; } cname; struct { char *mname; char *rname; uint32_t serial; uint32_t refresh; uint32_t retry; uint32_t expire; uint32_t ttlmin; } soa; struct { char *ptrdname; } ptr; struct { uint16_t pref; char *exchange; } mx; struct { char *data; } txt; struct { uint8_t addr[16]; } aaaa; struct { uint16_t pri; uint16_t weight; uint16_t port; char *target; } srv; struct { uint16_t order; uint16_t pref; char *flags; char *services; char *regexp; char *replace; } naptr; } rdata; }; struct hash; /** * Defines the DNS Query handler * * @param err 0 if success, otherwise errorcode * @param hdr DNS Header * @param ansl List of Answer records * @param authl List of Authoritive records * @param addl List of Additional records * @param arg Handler argument */ typedef void(dns_query_h)(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg); /** * Defines the DNS Resource Record list handler * * @param rr DNS Resource Record * @param arg Handler argument * * @return True to stop traversing, False to continue */ typedef bool(dns_rrlist_h)(struct dnsrr *rr, void *arg); int dns_hdr_encode(struct mbuf *mb, const struct dnshdr *hdr); int dns_hdr_decode(struct mbuf *mb, struct dnshdr *hdr); const char *dns_hdr_opcodename(uint8_t opcode); const char *dns_hdr_rcodename(uint8_t rcode); struct dnsrr *dns_rr_alloc(void); int dns_rr_encode(struct mbuf *mb, const struct dnsrr *rr, int64_t ttl_offs, struct hash *ht_dname, size_t start); int dns_rr_decode(struct mbuf *mb, struct dnsrr **rr, size_t start); bool dns_rr_cmp(const struct dnsrr *rr1, const struct dnsrr *rr2, bool rdata); const char *dns_rr_typename(uint16_t type); const char *dns_rr_classname(uint16_t dnsclass); int dns_rr_print(struct re_printf *pf, const struct dnsrr *rr); int dns_dname_encode(struct mbuf *mb, const char *name, struct hash *ht_dname, size_t start, bool comp); int dns_dname_decode(struct mbuf *mb, char **name, size_t start); int dns_cstr_encode(struct mbuf *mb, const char *str); int dns_cstr_decode(struct mbuf *mb, char **str); void dns_rrlist_sort(struct list *rrl, uint16_t type, size_t key); void dns_rrlist_sort_addr(struct list *rrl, size_t key); struct dnsrr *dns_rrlist_apply(struct list *rrl, const char *name, uint16_t type, uint16_t dnsclass, bool recurse, dns_rrlist_h *rrlh, void *arg); struct dnsrr *dns_rrlist_apply2(struct list *rrl, const char *name, uint16_t type1, uint16_t type2, uint16_t dnsclass, bool recurse, dns_rrlist_h *rrlh, void *arg); struct dnsrr *dns_rrlist_find(struct list *rrl, const char *name, uint16_t type, uint16_t dnsclass, bool recurse); /* DNS Client */ struct sa; struct dnsc; struct dns_query; /** DNS Client configuration */ struct dnsc_conf { uint32_t query_hash_size; uint32_t tcp_hash_size; uint32_t conn_timeout; /* in [ms] */ uint32_t idle_timeout; /* in [ms] */ }; int dnsc_alloc(struct dnsc **dcpp, const struct dnsc_conf *conf, const struct sa *srvv, uint32_t srvc); int dnsc_srv_set(struct dnsc *dnsc, const struct sa *srvv, uint32_t srvc); int dnsc_query(struct dns_query **qp, struct dnsc *dnsc, const char *name, uint16_t type, uint16_t dnsclass, bool rd, dns_query_h *qh, void *arg); int dnsc_query_srv(struct dns_query **qp, struct dnsc *dnsc, const char *name, uint16_t type, uint16_t dnsclass, int proto, const struct sa *srvv, const uint32_t *srvc, bool rd, dns_query_h *qh, void *arg); int dnsc_notify(struct dns_query **qp, struct dnsc *dnsc, const char *name, uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr, int proto, const struct sa *srvv, const uint32_t *srvc, dns_query_h *qh, void *arg); /* DNS System functions */ int dns_srv_get(char *domain, size_t dsize, struct sa *srvv, uint32_t *n); re-1.1.0/include/re_fmt.h000066400000000000000000000100211373627245400151760ustar00rootroot00000000000000/** * @file re_fmt.h Interface to formatted text functions * * Copyright (C) 2010 Creytiv.com */ #include #include struct mbuf; /** Defines a pointer-length string type */ struct pl { const char *p; /**< Pointer to string */ size_t l; /**< Length of string */ }; /** Initialise a pointer-length object from a constant string */ #define PL(s) {(s), sizeof((s))-1} /** Pointer-length Initializer */ #define PL_INIT {NULL, 0} extern const struct pl pl_null; void pl_set_str(struct pl *pl, const char *str); void pl_set_mbuf(struct pl *pl, const struct mbuf *mb); uint32_t pl_u32(const struct pl *pl); uint32_t pl_x32(const struct pl *pl); uint64_t pl_u64(const struct pl *pl); uint64_t pl_x64(const struct pl *pl); double pl_float(const struct pl *pl); bool pl_isset(const struct pl *pl); int pl_strcpy(const struct pl *pl, char *str, size_t size); int pl_strdup(char **dst, const struct pl *src); int pl_dup(struct pl *dst, const struct pl *src); int pl_strcmp(const struct pl *pl, const char *str); int pl_strcasecmp(const struct pl *pl, const char *str); int pl_cmp(const struct pl *pl1, const struct pl *pl2); int pl_casecmp(const struct pl *pl1, const struct pl *pl2); const char *pl_strchr(const struct pl *pl, char c); const char *pl_strrchr(const struct pl *pl, char c); /** Advance pl position/length by +/- N bytes */ static inline void pl_advance(struct pl *pl, ssize_t n) { pl->p += n; pl->l -= n; } /* Formatted printing */ /** * Defines the re_vhprintf() print handler * * @param p String to print * @param size Size of string to print * @param arg Handler argument * * @return 0 for success, otherwise errorcode */ typedef int(re_vprintf_h)(const char *p, size_t size, void *arg); /** Defines a print backend */ struct re_printf { re_vprintf_h *vph; /**< Print handler */ void *arg; /**< Handler agument */ }; /** * Defines the %H print handler * * @param pf Print backend * @param arg Handler argument * * @return 0 for success, otherwise errorcode */ typedef int(re_printf_h)(struct re_printf *pf, void *arg); int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg); int re_vfprintf(FILE *stream, const char *fmt, va_list ap); int re_vprintf(const char *fmt, va_list ap); int re_vsnprintf(char *str, size_t size, const char *fmt, va_list ap); int re_vsdprintf(char **strp, const char *fmt, va_list ap); int re_hprintf(struct re_printf *pf, const char *fmt, ...); int re_fprintf(FILE *stream, const char *fmt, ...); int re_printf(const char *fmt, ...); int re_snprintf(char *str, size_t size, const char *fmt, ...); int re_sdprintf(char **strp, const char *fmt, ...); /* Regular expressions */ int re_regex(const char *ptr, size_t len, const char *expr, ...); /* Character functions */ uint8_t ch_hex(char ch); /* String functions */ int str_hex(uint8_t *hex, size_t len, const char *str); void str_ncpy(char *dst, const char *src, size_t n); int str_dup(char **dst, const char *src); int str_cmp(const char *s1, const char *s2); int str_casecmp(const char *s1, const char *s2); size_t str_len(const char *s); const char *str_error(int errnum, char *buf, size_t sz); /** * Check if string is set * * @param s Zero-terminated string * * @return true if set, false if not set */ static inline bool str_isset(const char *s) { return s && s[0] != '\0'; } /* time */ int fmt_gmtime(struct re_printf *pf, void *ts); int fmt_human_time(struct re_printf *pf, const uint32_t *seconds); void hexdump(FILE *f, const void *p, size_t len); /* param */ typedef void (fmt_param_h)(const struct pl *name, const struct pl *val, void *arg); bool fmt_param_exists(const struct pl *pl, const char *pname); bool fmt_param_get(const struct pl *pl, const char *pname, struct pl *val); void fmt_param_apply(const struct pl *pl, fmt_param_h *ph, void *arg); /* unicode */ int utf8_encode(struct re_printf *pf, const char *str); int utf8_decode(struct re_printf *pf, const struct pl *pl); size_t utf8_byteseq(char u[4], unsigned cp); re-1.1.0/include/re_hash.h000066400000000000000000000020421373627245400153370ustar00rootroot00000000000000/** * @file re_hash.h Interface to hashmap table * * Copyright (C) 2010 Creytiv.com */ struct hash; struct pl; int hash_alloc(struct hash **hp, uint32_t bsize); void hash_append(struct hash *h, uint32_t key, struct le *le, void *data); void hash_unlink(struct le *le); struct le *hash_lookup(const struct hash *h, uint32_t key, list_apply_h *ah, void *arg); struct le *hash_apply(const struct hash *h, list_apply_h *ah, void *arg); struct list *hash_list(const struct hash *h, uint32_t key); uint32_t hash_bsize(const struct hash *h); void hash_flush(struct hash *h); void hash_clear(struct hash *h); uint32_t hash_valid_size(uint32_t size); /* Hash functions */ uint32_t hash_joaat(const uint8_t *key, size_t len); uint32_t hash_joaat_ci(const char *str, size_t len); uint32_t hash_joaat_str(const char *str); uint32_t hash_joaat_str_ci(const char *str); uint32_t hash_joaat_pl(const struct pl *pl); uint32_t hash_joaat_pl_ci(const struct pl *pl); uint32_t hash_fast(const char *k, size_t len); uint32_t hash_fast_str(const char *str); re-1.1.0/include/re_hmac.h000066400000000000000000000012451373627245400153300ustar00rootroot00000000000000/** * @file re_hmac.h Interface to HMAC functions * * Copyright (C) 2010 Creytiv.com */ void hmac_sha1(const uint8_t *k, /* secret key */ size_t lk, /* length of the key in bytes */ const uint8_t *d, /* data */ size_t ld, /* length of data in bytes */ uint8_t* out, /* output buffer, at least "t" bytes */ size_t t); enum hmac_hash { HMAC_HASH_SHA1, HMAC_HASH_SHA256 }; struct hmac; int hmac_create(struct hmac **hmacp, enum hmac_hash hash, const uint8_t *key, size_t key_len); int hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len, const uint8_t *data, size_t data_len); re-1.1.0/include/re_http.h000066400000000000000000000155021373627245400154000ustar00rootroot00000000000000/** * @file re_http.h Hypertext Transfer Protocol * * Copyright (C) 2010 Creytiv.com */ /** HTTP Header ID (perfect hash value) */ enum http_hdrid { HTTP_HDR_ACCEPT = 3186, HTTP_HDR_ACCEPT_CHARSET = 24, HTTP_HDR_ACCEPT_ENCODING = 708, HTTP_HDR_ACCEPT_LANGUAGE = 2867, HTTP_HDR_ACCEPT_RANGES = 3027, HTTP_HDR_AGE = 742, HTTP_HDR_ALLOW = 2429, HTTP_HDR_AUTHORIZATION = 2503, HTTP_HDR_CACHE_CONTROL = 2530, HTTP_HDR_CONNECTION = 865, HTTP_HDR_CONTENT_ENCODING = 580, HTTP_HDR_CONTENT_LANGUAGE = 3371, HTTP_HDR_CONTENT_LENGTH = 3861, HTTP_HDR_CONTENT_LOCATION = 3927, HTTP_HDR_CONTENT_MD5 = 406, HTTP_HDR_CONTENT_RANGE = 2846, HTTP_HDR_CONTENT_TYPE = 809, HTTP_HDR_DATE = 1027, HTTP_HDR_ETAG = 2392, HTTP_HDR_EXPECT = 1550, HTTP_HDR_EXPIRES = 1983, HTTP_HDR_FROM = 1963, HTTP_HDR_HOST = 3191, HTTP_HDR_IF_MATCH = 2684, HTTP_HDR_IF_MODIFIED_SINCE = 2187, HTTP_HDR_IF_NONE_MATCH = 4030, HTTP_HDR_IF_RANGE = 2220, HTTP_HDR_IF_UNMODIFIED_SINCE = 962, HTTP_HDR_LAST_MODIFIED = 2946, HTTP_HDR_LOCATION = 2514, HTTP_HDR_MAX_FORWARDS = 3549, HTTP_HDR_PRAGMA = 1673, HTTP_HDR_PROXY_AUTHENTICATE = 116, HTTP_HDR_PROXY_AUTHORIZATION = 2363, HTTP_HDR_RANGE = 4004, HTTP_HDR_REFERER = 2991, HTTP_HDR_RETRY_AFTER = 409, HTTP_HDR_SEC_WEBSOCKET_ACCEPT = 2959, HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS = 2937, HTTP_HDR_SEC_WEBSOCKET_KEY = 746, HTTP_HDR_SEC_WEBSOCKET_PROTOCOL = 2076, HTTP_HDR_SEC_WEBSOCKET_VERSION = 3158, HTTP_HDR_SERVER = 973, HTTP_HDR_TE = 2035, HTTP_HDR_TRAILER = 2577, HTTP_HDR_TRANSFER_ENCODING = 2115, HTTP_HDR_UPGRADE = 717, HTTP_HDR_USER_AGENT = 4064, HTTP_HDR_VARY = 3076, HTTP_HDR_VIA = 3961, HTTP_HDR_WARNING = 2108, HTTP_HDR_WWW_AUTHENTICATE = 2763, HTTP_HDR_NONE = -1 }; /** HTTP Header */ struct http_hdr { struct le le; /**< Linked-list element */ struct pl name; /**< HTTP Header name */ struct pl val; /**< HTTP Header value */ enum http_hdrid id; /**< HTTP Header id (unique) */ }; /** HTTP Message */ struct http_msg { struct pl ver; /**< HTTP Version number */ struct pl met; /**< Request Method */ struct pl path; /**< Request path/resource */ struct pl prm; /**< Request parameters */ uint16_t scode; /**< Response Status code */ struct pl reason; /**< Response Reason phrase */ struct list hdrl; /**< List of HTTP headers (struct http_hdr) */ struct msg_ctype ctyp; /**< Content-type */ struct mbuf *_mb; /**< Buffer containing the HTTP message */ struct mbuf *mb; /**< Buffer containing the HTTP body */ uint32_t clen; /**< Content length */ }; typedef bool(http_hdr_h)(const struct http_hdr *hdr, void *arg); int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req); const struct http_hdr *http_msg_hdr(const struct http_msg *msg, enum http_hdrid id); const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg, bool fwd, enum http_hdrid id, http_hdr_h *h, void *arg); const struct http_hdr *http_msg_xhdr(const struct http_msg *msg, const char *name); const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg, bool fwd, const char *name, http_hdr_h *h, void *arg); uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id); uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name); bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id, const char *value); bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name, const char *value); int http_msg_print(struct re_printf *pf, const struct http_msg *msg); /* Client */ struct http_cli; struct http_req; struct dnsc; struct tcp_conn; struct tls_conn; typedef void (http_resp_h)(int err, const struct http_msg *msg, void *arg); typedef int (http_data_h)(const uint8_t *buf, size_t size, const struct http_msg *msg, void *arg); typedef void (http_conn_h)(struct tcp_conn *tc, struct tls_conn *sc, void *arg); int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc); int http_client_add_ca(struct http_cli *cli, const char *tls_ca); int http_client_set_tls_hostname(struct http_cli *cli, const struct pl *hostname); int http_request(struct http_req **reqp, struct http_cli *cli, const char *met, const char *uri, http_resp_h *resph, http_data_h *datah, void *arg, const char *fmt, ...); void http_req_set_conn_handler(struct http_req *req, http_conn_h *connh); void http_client_set_laddr(struct http_cli *cli, struct sa *addr); void http_client_set_laddr6(struct http_cli *cli, struct sa *addr); /* Server */ struct http_sock; struct http_conn; typedef void (http_req_h)(struct http_conn *conn, const struct http_msg *msg, void *arg); int http_listen(struct http_sock **sockp, const struct sa *laddr, http_req_h *reqh, void *arg); int https_listen(struct http_sock **sockp, const struct sa *laddr, const char *cert, http_req_h *reqh, void *arg); struct tcp_sock *http_sock_tcp(struct http_sock *sock); const struct sa *http_conn_peer(const struct http_conn *conn); struct tcp_conn *http_conn_tcp(struct http_conn *conn); struct tls_conn *http_conn_tls(struct http_conn *conn); void http_conn_close(struct http_conn *conn); int http_reply(struct http_conn *conn, uint16_t scode, const char *reason, const char *fmt, ...); int http_creply(struct http_conn *conn, uint16_t scode, const char *reason, const char *ctype, const char *fmt, ...); int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason); /* Authentication */ struct http_auth { const char *realm; bool stale; }; typedef int (http_auth_h)(const struct pl *username, uint8_t *ha1, void *arg); int http_auth_print_challenge(struct re_printf *pf, const struct http_auth *auth); bool http_auth_check(const struct pl *hval, const struct pl *method, struct http_auth *auth, http_auth_h *authh, void *arg); bool http_auth_check_request(const struct http_msg *msg, struct http_auth *auth, http_auth_h *authh, void *arg); re-1.1.0/include/re_httpauth.h000066400000000000000000000015311373627245400162570ustar00rootroot00000000000000/** * @file re_httpauth.h Interface to HTTP Authentication * * Copyright (C) 2010 Creytiv.com */ /** HTTP Digest Challenge */ struct httpauth_digest_chall { struct pl realm; struct pl nonce; /* optional */ struct pl opaque; struct pl stale; struct pl algorithm; struct pl qop; }; /** HTTP Digest response */ struct httpauth_digest_resp { struct pl realm; struct pl nonce; struct pl response; struct pl username; struct pl uri; /* optional */ struct pl nc; struct pl cnonce; struct pl qop; }; int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, const struct pl *hval); int httpauth_digest_response_decode(struct httpauth_digest_resp *resp, const struct pl *hval); int httpauth_digest_response_auth(const struct httpauth_digest_resp *resp, const struct pl *method, const uint8_t *ha1); re-1.1.0/include/re_ice.h000066400000000000000000000131451373627245400151620ustar00rootroot00000000000000/** * @file re_ice.h Interface to Interactive Connectivity Establishment (ICE) * * Copyright (C) 2010 Creytiv.com */ /** ICE mode */ enum ice_mode { ICE_MODE_FULL, ICE_MODE_LITE }; /** ICE Role */ enum ice_role { ICE_ROLE_UNKNOWN = 0, ICE_ROLE_CONTROLLING, ICE_ROLE_CONTROLLED }; /** ICE Component ID */ enum ice_compid { ICE_COMPID_RTP = 1, ICE_COMPID_RTCP = 2 }; /** ICE Candidate type */ enum ice_cand_type { ICE_CAND_TYPE_HOST, /**< Host candidate */ ICE_CAND_TYPE_SRFLX, /**< Server Reflexive candidate */ ICE_CAND_TYPE_PRFLX, /**< Peer Reflexive candidate */ ICE_CAND_TYPE_RELAY /**< Relayed candidate */ }; /** ICE TCP protocol type */ enum ice_tcptype { ICE_TCP_ACTIVE, /**< Active TCP client */ ICE_TCP_PASSIVE, /**< Passive TCP server */ ICE_TCP_SO /**< Simultaneous-open TCP client/server */ }; /** Candidate pair states */ enum ice_candpair_state { ICE_CANDPAIR_FROZEN = 0, /**< Frozen state (default) */ ICE_CANDPAIR_WAITING, /**< Waiting to become highest on list */ ICE_CANDPAIR_INPROGRESS, /**< In-Progress state;transac. in progress */ ICE_CANDPAIR_SUCCEEDED, /**< Succeeded state; successful result */ ICE_CANDPAIR_FAILED /**< Failed state; check failed */ }; struct ice; struct ice_cand; struct icem; struct turnc; /** ICE Configuration */ struct ice_conf { uint32_t rto; /**< STUN Retransmission TimeOut */ uint32_t rc; /**< STUN Retransmission Count */ bool debug; /**< Enable ICE debugging */ }; typedef void (ice_connchk_h)(int err, bool update, void *arg); /* ICE Media */ int icem_alloc(struct icem **icemp, enum ice_mode mode, enum ice_role role, int proto, int layer, uint64_t tiebrk, const char *lufrag, const char *lpwd, ice_connchk_h *chkh, void *arg); struct ice_conf *icem_conf(struct icem *icem); enum ice_role icem_local_role(const struct icem *icem); void icem_set_conf(struct icem *icem, const struct ice_conf *conf); void icem_set_role(struct icem *icem, enum ice_role role); void icem_set_name(struct icem *icem, const char *name); int icem_comp_add(struct icem *icem, unsigned compid, void *sock); int icem_cand_add(struct icem *icem, unsigned compid, uint16_t lprio, const char *ifname, const struct sa *addr); bool icem_verify_support(struct icem *icem, unsigned compid, const struct sa *raddr); int icem_conncheck_start(struct icem *icem); void icem_conncheck_stop(struct icem *icem, int err); int icem_add_chan(struct icem *icem, unsigned compid, const struct sa *raddr); bool icem_mismatch(const struct icem *icem); void icem_update(struct icem *icem); int ice_sdp_decode(struct icem *ice, const char *name, const char *value); int icem_sdp_decode(struct icem *icem, const char *name, const char *value); int icem_debug(struct re_printf *pf, const struct icem *icem); struct list *icem_lcandl(const struct icem *icem); struct list *icem_rcandl(const struct icem *icem); struct list *icem_checkl(const struct icem *icem); struct list *icem_validl(const struct icem *icem); const struct sa *icem_cand_default(struct icem *icem, unsigned compid); const struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid); const struct ice_cand *icem_selected_lcand(const struct icem *icem, unsigned compid); const struct ice_cand *icem_selected_rcand(const struct icem *icem, unsigned compid); void ice_candpair_set_states(struct icem *icem); void icem_cand_redund_elim(struct icem *icem); int icem_comps_set_default_cand(struct icem *icem); struct stun *icem_stun(struct icem *icem); int icem_set_turn_client(struct icem *icem, unsigned compid, struct turnc *turnc); bool ice_remotecands_avail(const struct icem *icem); int ice_cand_encode(struct re_printf *pf, const struct ice_cand *cand); int ice_remotecands_encode(struct re_printf *pf, const struct icem *icem); struct ice_cand *icem_cand_find(const struct list *lst, unsigned compid, const struct sa *addr); int icem_lcand_add(struct icem *icem, struct ice_cand *base, enum ice_cand_type type, const struct sa *addr); struct ice_cand *icem_lcand_base(struct ice_cand *lcand); const struct sa *icem_lcand_addr(const struct ice_cand *cand); enum ice_cand_type icem_cand_type(const struct ice_cand *cand); extern const char ice_attr_cand[]; extern const char ice_attr_lite[]; extern const char ice_attr_mismatch[]; extern const char ice_attr_pwd[]; extern const char ice_attr_remote_cand[]; extern const char ice_attr_ufrag[]; const char *ice_cand_type2name(enum ice_cand_type type); enum ice_cand_type ice_cand_name2type(const char *name); const char *ice_role2name(enum ice_role role); const char *ice_candpair_state2name(enum ice_candpair_state st); uint32_t ice_cand_calc_prio(enum ice_cand_type type, uint16_t local, unsigned compid); /** Defines an SDP candidate attribute */ struct ice_cand_attr { char foundation[32]; /**< Foundation string */ unsigned compid; /**< Component ID (1-256) */ int proto; /**< Transport protocol */ uint32_t prio; /**< Priority of this candidate */ struct sa addr; /**< Transport address */ enum ice_cand_type type; /**< Candidate type */ struct sa rel_addr; /**< Related transport address (optional) */ enum ice_tcptype tcptype; /**< TCP candidate type (TCP-only) */ }; int ice_cand_attr_encode(struct re_printf *pf, const struct ice_cand_attr *cand); int ice_cand_attr_decode(struct ice_cand_attr *cand, const char *val); re-1.1.0/include/re_jbuf.h000066400000000000000000000022531373627245400153460ustar00rootroot00000000000000/** * @file re_jbuf.h Interface to Jitter Buffer * * Copyright (C) 2010 Creytiv.com */ struct jbuf; struct rtp_header; /** Jitter buffer statistics */ struct jbuf_stat { uint32_t n_put; /**< Number of frames put into jitter buffer */ uint32_t n_get; /**< Number of frames got from jitter buffer */ uint32_t n_oos; /**< Number of out-of-sequence frames */ uint32_t n_dups; /**< Number of duplicate frames detected */ uint32_t n_late; /**< Number of frames arriving too late */ uint32_t n_lost; /**< Number of lost frames */ uint32_t n_overflow; /**< Number of overflows */ uint32_t n_underflow; /**< Number of underflows */ uint32_t n_flush; /**< Number of times jitter buffer flushed */ }; int jbuf_alloc(struct jbuf **jbp, uint32_t min, uint32_t max); int jbuf_put(struct jbuf *jb, const struct rtp_header *hdr, void *mem); int jbuf_get(struct jbuf *jb, struct rtp_header *hdr, void **mem); void jbuf_flush(struct jbuf *jb); int jbuf_stats(const struct jbuf *jb, struct jbuf_stat *jstat); int jbuf_debug(struct re_printf *pf, const struct jbuf *jb); re-1.1.0/include/re_json.h000066400000000000000000000023151373627245400153700ustar00rootroot00000000000000/** * @file re_json.h Interface to JavaScript Object Notation (JSON) -- RFC 7159 * * Copyright (C) 2010 - 2015 Creytiv.com */ enum json_typ { JSON_STRING, JSON_INT, JSON_DOUBLE, JSON_BOOL, JSON_NULL, }; struct json_value { union { char *str; int64_t integer; double dbl; bool boolean; } v; enum json_typ type; }; struct json_handlers; typedef int (json_object_entry_h)(const char *name, const struct json_value *value, void *arg); typedef int (json_array_entry_h)(unsigned idx, const struct json_value *value, void *arg); typedef int (json_object_h)(const char *name, unsigned idx, struct json_handlers *h); typedef int (json_array_h)(const char *name, unsigned idx, struct json_handlers *h); struct json_handlers { json_object_h *oh; json_array_h *ah; json_object_entry_h *oeh; json_array_entry_h *aeh; void *arg; }; int json_decode(const char *str, size_t len, unsigned maxdepth, json_object_h *oh, json_array_h *ah, json_object_entry_h *oeh, json_array_entry_h *aeh, void *arg); int json_decode_odict(struct odict **op, uint32_t hash_size, const char *str, size_t len, unsigned maxdepth); int json_encode_odict(struct re_printf *pf, const struct odict *o); re-1.1.0/include/re_list.h000066400000000000000000000046001373627245400153710ustar00rootroot00000000000000/** * @file re_list.h Interface to Linked List * * Copyright (C) 2010 Creytiv.com */ /** Linked-list element */ struct le { struct le *prev; /**< Previous element */ struct le *next; /**< Next element */ struct list *list; /**< Parent list (NULL if not linked-in) */ void *data; /**< User-data */ }; /** List Element Initializer */ #define LE_INIT {NULL, NULL, NULL, NULL} /** Defines a linked list */ struct list { struct le *head; /**< First list element */ struct le *tail; /**< Last list element */ }; /** Linked list Initializer */ #define LIST_INIT {NULL, NULL} /** * Defines the list apply handler * * @param le List element * @param arg Handler argument * * @return true to stop traversing, false to continue */ typedef bool (list_apply_h)(struct le *le, void *arg); /** * Defines the list sort handler * * @param le1 Current list element * @param le2 Next list element * @param arg Handler argument * * @return true if sorted, otherwise false */ typedef bool (list_sort_h)(struct le *le1, struct le *le2, void *arg); void list_init(struct list *list); void list_flush(struct list *list); void list_clear(struct list *list); void list_append(struct list *list, struct le *le, void *data); void list_prepend(struct list *list, struct le *le, void *data); void list_insert_before(struct list *list, struct le *le, struct le *ile, void *data); void list_insert_after(struct list *list, struct le *le, struct le *ile, void *data); void list_unlink(struct le *le); void list_sort(struct list *list, list_sort_h *sh, void *arg); struct le *list_apply(const struct list *list, bool fwd, list_apply_h *ah, void *arg); struct le *list_head(const struct list *list); struct le *list_tail(const struct list *list); uint32_t list_count(const struct list *list); /** * Get the user-data from a list element * * @param le List element * * @return Pointer to user-data */ static inline void *list_ledata(const struct le *le) { return le ? le->data : NULL; } static inline bool list_contains(const struct list *list, const struct le *le) { return le ? le->list == list : false; } static inline bool list_isempty(const struct list *list) { return list ? list->head == NULL : true; } #define LIST_FOREACH(list, le) \ for ((le) = list_head((list)); (le); (le) = (le)->next) re-1.1.0/include/re_lock.h000066400000000000000000000005071373627245400153500ustar00rootroot00000000000000/** * @file re_lock.h Interface to locking functions * * Copyright (C) 2010 Creytiv.com */ struct lock; int lock_alloc(struct lock **lp); void lock_read_get(struct lock *l); void lock_write_get(struct lock *l); int lock_read_try(struct lock *l); int lock_write_try(struct lock *l); void lock_rel(struct lock *l); re-1.1.0/include/re_main.h000066400000000000000000000024221373627245400153420ustar00rootroot00000000000000/** * @file re_main.h Interface to main polling routine * * Copyright (C) 2010 Creytiv.com */ enum { #ifndef FD_READ FD_READ = 1<<0, #endif #ifndef FD_WRITE FD_WRITE = 1<<1, #endif FD_EXCEPT = 1<<2 }; /** * File descriptor event handler * * @param flags Event flags * @param arg Handler argument */ typedef void (fd_h)(int flags, void *arg); /** * Thread-safe signal handler * * @param sig Signal number */ typedef void (re_signal_h)(int sig); int fd_listen(int fd, int flags, fd_h *fh, void *arg); void fd_close(int fd); int fd_setsize(int maxfds); void fd_debug(void); int libre_init(void); void libre_close(void); int re_main(re_signal_h *signalh); void re_cancel(void); int re_debug(struct re_printf *pf, void *unused); int re_thread_init(void); void re_thread_close(void); void re_thread_enter(void); void re_thread_leave(void); void re_set_mutex(void *mutexp); /** Polling methods */ enum poll_method { METHOD_NULL = 0, METHOD_POLL, METHOD_SELECT, METHOD_EPOLL, METHOD_KQUEUE, /* sep */ METHOD_MAX }; int poll_method_set(enum poll_method method); enum poll_method poll_method_best(void); const char *poll_method_name(enum poll_method method); int poll_method_type(enum poll_method *method, const struct pl *name); re-1.1.0/include/re_mbuf.h000066400000000000000000000075541373627245400153620ustar00rootroot00000000000000/** * @file re_mbuf.h Interface to memory buffers * * Copyright (C) 2010 Creytiv.com */ #include #ifndef RELEASE #define MBUF_DEBUG 1 /**< Mbuf debugging (0 or 1) */ #endif #if MBUF_DEBUG /** Check that mbuf position does not exceed end */ #define MBUF_CHECK_POS(mb) \ if ((mb) && (mb)->pos > (mb)->end) { \ BREAKPOINT; \ } /** Check that mbuf end does not exceed size */ #define MBUF_CHECK_END(mb) \ if ((mb) && (mb)->end > (mb)->size) { \ BREAKPOINT; \ } #else #define MBUF_CHECK_POS(mb) #define MBUF_CHECK_END(mb) #endif /** Defines a memory buffer */ struct mbuf { uint8_t *buf; /**< Buffer memory */ size_t size; /**< Size of buffer */ size_t pos; /**< Position in buffer */ size_t end; /**< End of buffer */ }; struct pl; struct re_printf; struct mbuf *mbuf_alloc(size_t size); struct mbuf *mbuf_alloc_ref(struct mbuf *mbr); void mbuf_init(struct mbuf *mb); void mbuf_reset(struct mbuf *mb); int mbuf_resize(struct mbuf *mb, size_t size); void mbuf_trim(struct mbuf *mb); int mbuf_shift(struct mbuf *mb, ssize_t shift); int mbuf_write_mem(struct mbuf *mb, const uint8_t *buf, size_t size); int mbuf_write_u8(struct mbuf *mb, uint8_t v); int mbuf_write_u16(struct mbuf *mb, uint16_t v); int mbuf_write_u32(struct mbuf *mb, uint32_t v); int mbuf_write_u64(struct mbuf *mb, uint64_t v); int mbuf_write_str(struct mbuf *mb, const char *str); int mbuf_write_pl(struct mbuf *mb, const struct pl *pl); int mbuf_read_mem(struct mbuf *mb, uint8_t *buf, size_t size); uint8_t mbuf_read_u8(struct mbuf *mb); uint16_t mbuf_read_u16(struct mbuf *mb); uint32_t mbuf_read_u32(struct mbuf *mb); uint64_t mbuf_read_u64(struct mbuf *mb); int mbuf_read_str(struct mbuf *mb, char *str, size_t size); int mbuf_strdup(struct mbuf *mb, char **strp, size_t len); int mbuf_vprintf(struct mbuf *mb, const char *fmt, va_list ap); int mbuf_printf(struct mbuf *mb, const char *fmt, ...); int mbuf_write_pl_skip(struct mbuf *mb, const struct pl *pl, const struct pl *skip); int mbuf_fill(struct mbuf *mb, uint8_t c, size_t n); int mbuf_debug(struct re_printf *pf, const struct mbuf *mb); /** * Get the buffer from the current position * * @param mb Memory buffer * * @return Current buffer */ static inline uint8_t *mbuf_buf(const struct mbuf *mb) { return mb ? mb->buf + mb->pos : (uint8_t *)NULL; } /** * Get number of bytes left in a memory buffer, from current position to end * * @param mb Memory buffer * * @return Number of bytes left */ static inline size_t mbuf_get_left(const struct mbuf *mb) { return (mb && (mb->end > mb->pos)) ? (mb->end - mb->pos) : 0; } /** * Get available space in buffer (size - pos) * * @param mb Memory buffer * * @return Number of bytes available in buffer */ static inline size_t mbuf_get_space(const struct mbuf *mb) { return (mb && (mb->size > mb->pos)) ? (mb->size - mb->pos) : 0; } /** * Set absolute position * * @param mb Memory buffer * @param pos Position */ static inline void mbuf_set_pos(struct mbuf *mb, size_t pos) { mb->pos = pos; MBUF_CHECK_POS(mb); } /** * Set absolute end * * @param mb Memory buffer * @param end End position */ static inline void mbuf_set_end(struct mbuf *mb, size_t end) { mb->end = end; MBUF_CHECK_END(mb); } /** * Advance position +/- N bytes * * @param mb Memory buffer * @param n Number of bytes to advance */ static inline void mbuf_advance(struct mbuf *mb, ssize_t n) { mb->pos += n; MBUF_CHECK_POS(mb); } /** * Rewind position and end to the beginning of buffer * * @param mb Memory buffer */ static inline void mbuf_rewind(struct mbuf *mb) { mb->pos = mb->end = 0; } /** * Set position to the end of the buffer * * @param mb Memory buffer */ static inline void mbuf_skip_to_end(struct mbuf *mb) { mb->pos = mb->end; } re-1.1.0/include/re_md5.h000066400000000000000000000005551373627245400151100ustar00rootroot00000000000000/** * @file re_md5.h Interface to MD5 functions * * Copyright (C) 2010 Creytiv.com */ /** MD5 values */ enum { MD5_SIZE = 16, /**< Number of bytes in MD5 hash */ MD5_STR_SIZE = 2*MD5_SIZE + 1 /**< Number of bytes in MD5 string */ }; void md5(const uint8_t *d, size_t n, uint8_t *md); int md5_printf(uint8_t *md, const char *fmt, ...); re-1.1.0/include/re_mem.h000066400000000000000000000025711373627245400152010ustar00rootroot00000000000000/** * @file re_mem.h Interface to Memory management with reference counting * * Copyright (C) 2010 Creytiv.com */ /** * Defines the memory destructor handler, which is called when the reference * of a memory object goes down to zero * * @param data Pointer to memory object */ typedef void (mem_destroy_h)(void *data); /** Memory Statistics */ struct memstat { size_t bytes_cur; /**< Current bytes allocated */ size_t bytes_peak; /**< Peak bytes allocated */ size_t blocks_cur; /**< Current blocks allocated */ size_t blocks_peak; /**< Peak blocks allocated */ size_t size_min; /**< Lowest block size allocated */ size_t size_max; /**< Largest block size allocated */ }; void *mem_alloc(size_t size, mem_destroy_h *dh); void *mem_zalloc(size_t size, mem_destroy_h *dh); void *mem_realloc(void *data, size_t size); void *mem_reallocarray(void *ptr, size_t nmemb, size_t membsize, mem_destroy_h *dh); void *mem_ref(void *data); void *mem_deref(void *data); uint32_t mem_nrefs(const void *data); void mem_debug(void); void mem_threshold_set(ssize_t n); struct re_printf; int mem_status(struct re_printf *pf, void *unused); int mem_get_stat(struct memstat *mstat); /* Secure memory functions */ int mem_seccmp(const volatile uint8_t *volatile s1, const volatile uint8_t *volatile s2, size_t n); re-1.1.0/include/re_mod.h000066400000000000000000000026541373627245400152040ustar00rootroot00000000000000/** * @file re_mod.h Interface to loadable modules * * Copyright (C) 2010 Creytiv.com */ /** * @def MOD_PRE * * Module Prefix * * @def MOD_EXT * * Module Extension */ #if defined (WIN32) #define MOD_PRE "" #define MOD_EXT ".dll" #else #define MOD_PRE "" #define MOD_EXT ".so" #endif /** Symbol to enable exporting of functions from a module */ #ifdef WIN32 #define EXPORT_SYM __declspec(dllexport) #else #define EXPORT_SYM #endif /* ----- Module API ----- */ /** * Defines the module initialisation handler * * @return 0 for success, otherwise errorcode */ typedef int (mod_init_h)(void); /** * Defines the module close handler * * @return 0 for success, otherwise errorcode */ typedef int (mod_close_h)(void); struct mod; struct re_printf; /** Defines the module export */ struct mod_export { const char *name; /**< Module name */ const char *type; /**< Module type */ mod_init_h *init; /**< Module init handler */ mod_close_h *close; /**< Module close handler */ }; /* ----- Application API ----- */ void mod_init(void); void mod_close(void); int mod_load(struct mod **mp, const char *name); int mod_add(struct mod **mp, const struct mod_export *me); struct mod *mod_find(const char *name); const struct mod_export *mod_export(const struct mod *m); struct list *mod_list(void); int mod_debug(struct re_printf *pf, void *unused); re-1.1.0/include/re_mqueue.h000066400000000000000000000004351373627245400157210ustar00rootroot00000000000000/** * @file re_mqueue.h Thread Safe Message Queue * * Copyright (C) 2010 Creytiv.com */ struct mqueue; typedef void (mqueue_h)(int id, void *data, void *arg); int mqueue_alloc(struct mqueue **mqp, mqueue_h *h, void *arg); int mqueue_push(struct mqueue *mq, int id, void *data); re-1.1.0/include/re_msg.h000066400000000000000000000010151373627245400152010ustar00rootroot00000000000000/** * @file re_msg.h Interface to generic message components * * Copyright (C) 2010 Creytiv.com */ /** Content-Type */ struct msg_ctype { struct pl type; struct pl subtype; struct pl params; }; int msg_ctype_decode(struct msg_ctype *ctype, const struct pl *pl); bool msg_ctype_cmp(const struct msg_ctype *ctype, const char *type, const char *subtype); int msg_param_decode(const struct pl *pl, const char *name, struct pl *val); int msg_param_exists(const struct pl *pl, const char *name, struct pl *end); re-1.1.0/include/re_natbd.h000066400000000000000000000064031373627245400155110ustar00rootroot00000000000000/** * @file re_natbd.h NAT Behavior Discovery Using STUN (RFC 5780) * * Copyright (C) 2010 Creytiv.com */ /** NAT Mapping/Filtering types - See RFC 4787 for definitions */ enum nat_type { NAT_TYPE_UNKNOWN = 0, /**< Unknown type */ NAT_TYPE_ENDP_INDEP = 1, /**< Endpoint-Independent */ NAT_TYPE_ADDR_DEP = 2, /**< Address-Dependent */ NAT_TYPE_ADDR_PORT_DEP = 3 /**< Address and Port-Dependent */ }; /* Strings */ const char *nat_type_str(enum nat_type type); /* * Diagnosing NAT Hairpinning */ struct nat_hairpinning; /** * Defines the NAT Hairpinning handler */ typedef void (nat_hairpinning_h)(int err, bool supported, void *arg); int nat_hairpinning_alloc(struct nat_hairpinning **nhp, const struct sa *srv, int proto, const struct stun_conf *conf, nat_hairpinning_h *hph, void *arg); int nat_hairpinning_start(struct nat_hairpinning *nh); /* * Determining NAT Mapping Behavior */ struct nat_mapping; /** * Defines the NAT Mapping handler * * @param err Errorcode * @param type NAT Mapping type * @param arg Handler argument */ typedef void (nat_mapping_h)(int err, enum nat_type map, void *arg); int nat_mapping_alloc(struct nat_mapping **nmp, const struct sa *laddr, const struct sa *srv, int proto, const struct stun_conf *conf, nat_mapping_h *mh, void *arg); int nat_mapping_start(struct nat_mapping *nm); /* * Determining NAT Filtering Behavior */ struct nat_filtering; /** * Defines the NAT Filtering handler * * @param err Errorcode * @param type NAT Filtering type * @param arg Handler argument */ typedef void (nat_filtering_h)(int err, enum nat_type filt, void *arg); int nat_filtering_alloc(struct nat_filtering **nfp, const struct sa *srv, const struct stun_conf *conf, nat_filtering_h *fh, void *arg); int nat_filtering_start(struct nat_filtering *nf); /* * Binding Lifetime Discovery */ struct nat_lifetime; /** Defines the NAT lifetime interval */ struct nat_lifetime_interval { uint32_t min; /**< Minimum lifetime interval in [seconds] */ uint32_t cur; /**< Current lifetime interval in [seconds] */ uint32_t max; /**< Maximum lifetime interval in [seconds] */ }; /** * Defines the NAT Lifetime handler * * @param err Errorcode * @param i NAT Lifetime intervals * @param arg Handler argument */ typedef void (nat_lifetime_h)(int err, const struct nat_lifetime_interval *i, void *arg); int nat_lifetime_alloc(struct nat_lifetime **nlp, const struct sa *srv, uint32_t interval, const struct stun_conf *conf, nat_lifetime_h *lh, void *arg); int nat_lifetime_start(struct nat_lifetime *nl); /* * Detecting Generic ALGs */ struct nat_genalg; /** * Defines the NAT Generic ALG handler * * @param err Errorcode * @param errcode STUN Error code (if set) * @param status Generic ALG status (-1=not detected, 1=detected) * @param map Mapped network address * @param arg Handler argument */ typedef void (nat_genalg_h)(int err, uint16_t scode, const char *reason, int status, const struct sa *map, void *arg); int nat_genalg_alloc(struct nat_genalg **ngp, const struct sa *srv, int proto, const struct stun_conf *conf, nat_genalg_h *gh, void *arg); int nat_genalg_start(struct nat_genalg *ng); re-1.1.0/include/re_net.h000066400000000000000000000052331373627245400152070ustar00rootroot00000000000000/** * @file re_net.h Interface to Networking module. * * Copyright (C) 2010 Creytiv.com */ #if defined(WIN32) #include #include #else #include #ifndef _BSD_SOCKLEN_T_ #define _BSD_SOCKLEN_T_ int /**< Defines the BSD socket length type */ #endif #include #include #include #endif /** Length of IPv4 address string */ #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif /** Length of IPv6 address string */ #ifndef INET6_ADDRSTRLEN #define INET6_ADDRSTRLEN 46 #endif /** Length of IPv4/v6 address string */ #ifdef HAVE_INET6 #define NET_ADDRSTRLEN INET6_ADDRSTRLEN #else #define NET_ADDRSTRLEN INET_ADDRSTRLEN #endif /* forward declarations */ struct sa; /* Net generic */ int net_hostaddr(int af, struct sa *ip); int net_default_source_addr_get(int af, struct sa *ip); int net_default_gateway_get(int af, struct sa *gw); /* Net sockets */ int net_sock_init(void); void net_sock_close(void); /* Net socket options */ int net_sockopt_blocking_set(int fd, bool blocking); int net_sockopt_reuse_set(int fd, bool reuse); /* Net interface (if.c) */ /** * Defines the interface address handler - called once per interface * * @param ifname Name of the interface * @param sa IP address of the interface * @param arg Handler argument * * @return true to stop traversing, false to continue */ typedef bool (net_ifaddr_h)(const char *ifname, const struct sa *sa, void *arg); int net_if_getname(char *ifname, size_t sz, int af, const struct sa *ip); int net_if_getaddr(const char *ifname, int af, struct sa *ip); int net_if_getaddr4(const char *ifname, int af, struct sa *ip); int net_if_list(net_ifaddr_h *ifh, void *arg); int net_if_apply(net_ifaddr_h *ifh, void *arg); int net_if_debug(struct re_printf *pf, void *unused); int net_if_getlinklocal(const char *ifname, int af, struct sa *ip); /* Net interface (ifaddrs.c) */ int net_getifaddrs(net_ifaddr_h *ifh, void *arg); /* Net route */ /** * Defines the routing table handler - called once per route entry * * @param ifname Interface name * @param dst Destination IP address/network * @param dstlen Prefix length of destination * @param gw Gateway IP address * @param arg Handler argument * * @return true to stop traversing, false to continue */ typedef bool (net_rt_h)(const char *ifname, const struct sa *dst, int dstlen, const struct sa *gw, void *arg); int net_rt_list(net_rt_h *rth, void *arg); int net_rt_default_get(int af, char *ifname, size_t size); int net_rt_debug(struct re_printf *pf, void *unused); /* Net strings */ const char *net_proto2name(int proto); const char *net_af2name(int af); re-1.1.0/include/re_odict.h000066400000000000000000000030351373627245400155210ustar00rootroot00000000000000/** * @file re_odict.h Interface to Ordered Dictionary * * Copyright (C) 2010 - 2015 Creytiv.com */ enum odict_type { ODICT_OBJECT, ODICT_ARRAY, ODICT_STRING, ODICT_INT, ODICT_DOUBLE, ODICT_BOOL, ODICT_NULL, }; struct odict { struct list lst; struct hash *ht; }; struct odict_entry { struct le le, he; char *key; union { struct odict *odict; /* ODICT_OBJECT / ODICT_ARRAY */ char *str; /* ODICT_STRING */ int64_t integer; /* ODICT_INT */ double dbl; /* ODICT_DOUBLE */ bool boolean; /* ODICT_BOOL */ } u; enum odict_type type; }; int odict_alloc(struct odict **op, uint32_t hash_size); const struct odict_entry *odict_lookup(const struct odict *o, const char *key); size_t odict_count(const struct odict *o, bool nested); int odict_debug(struct re_printf *pf, const struct odict *o); int odict_entry_add(struct odict *o, const char *key, int type, ...); void odict_entry_del(struct odict *o, const char *key); int odict_entry_debug(struct re_printf *pf, const struct odict_entry *e); bool odict_type_iscontainer(enum odict_type type); bool odict_type_isreal(enum odict_type type); const char *odict_type_name(enum odict_type type); /* Helpers */ const struct odict_entry *odict_get_type(const struct odict *o, enum odict_type type, const char *key); const char *odict_string(const struct odict *o, const char *key); bool odict_get_number(const struct odict *o, uint64_t *num, const char *key); bool odict_get_boolean(const struct odict *o, bool *value, const char *key); re-1.1.0/include/re_rtmp.h000066400000000000000000000123311373627245400154000ustar00rootroot00000000000000/** * @file re_rtmp.h Interface to Real Time Messaging Protocol (RTMP) * * Copyright (C) 2010 Creytiv.com */ /** RTMP Protocol values */ enum { RTMP_PORT = 1935, }; /** RTMP Stream IDs */ enum { /* User Control messages SHOULD use message stream ID 0 (known as the control stream) */ RTMP_CONTROL_STREAM_ID = 0 }; /** RTMP Packet types */ enum rtmp_packet_type { RTMP_TYPE_SET_CHUNK_SIZE = 1, /**< Set Chunk Size */ RTMP_TYPE_ACKNOWLEDGEMENT = 3, /**< Acknowledgement */ RTMP_TYPE_USER_CONTROL_MSG = 4, /**< User Control Messages */ RTMP_TYPE_WINDOW_ACK_SIZE = 5, /**< Window Acknowledgement Size */ RTMP_TYPE_SET_PEER_BANDWIDTH = 6, /**< Set Peer Bandwidth */ RTMP_TYPE_AUDIO = 8, /**< Audio Message */ RTMP_TYPE_VIDEO = 9, /**< Video Message */ RTMP_TYPE_DATA = 18, /**< Data Message */ RTMP_TYPE_AMF0 = 20, /**< Action Message Format (AMF) */ }; /** RTMP AMF types */ enum rtmp_amf_type { RTMP_AMF_TYPE_ROOT = -1, /**< Special internal type */ RTMP_AMF_TYPE_NUMBER = 0x00, /**< Number Type */ RTMP_AMF_TYPE_BOOLEAN = 0x01, /**< Boolean Type */ RTMP_AMF_TYPE_STRING = 0x02, /**< String Type */ RTMP_AMF_TYPE_OBJECT = 0x03, /**< Object Type */ RTMP_AMF_TYPE_NULL = 0x05, /**< Null type */ RTMP_AMF_TYPE_ECMA_ARRAY = 0x08, /**< ECMA 'associative' Array */ RTMP_AMF_TYPE_OBJECT_END = 0x09, /**< Object End Type */ RTMP_AMF_TYPE_STRICT_ARRAY = 0x0a, /**< Array with ordinal indices */ }; /** RTMP Event types */ enum rtmp_event_type { RTMP_EVENT_STREAM_BEGIN = 0, /**< Stream begin */ RTMP_EVENT_STREAM_EOF = 1, /**< Stream End-Of-File */ RTMP_EVENT_STREAM_DRY = 2, /**< No more data on the stream */ RTMP_EVENT_SET_BUFFER_LENGTH = 3, /**< Set buffer size in [ms] */ RTMP_EVENT_STREAM_IS_RECORDED = 4, /**< Stream is recorded */ RTMP_EVENT_PING_REQUEST = 6, /**< Ping Request from server */ RTMP_EVENT_PING_RESPONSE = 7, /**< Ping Response to server */ }; /* forward declarations */ struct tls; struct dnsc; struct odict; struct tcp_sock; /* * RTMP High-level API (connection, stream) */ /* conn */ struct rtmp_conn; typedef void (rtmp_estab_h)(void *arg); typedef void (rtmp_command_h)(const struct odict *msg, void *arg); typedef void (rtmp_close_h)(int err, void *arg); int rtmp_connect(struct rtmp_conn **connp, struct dnsc *dnsc, const char *uri, struct tls *tls, rtmp_estab_h *estabh, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg); int rtmp_accept(struct rtmp_conn **connp, struct tcp_sock *ts, struct tls *tls, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg); int rtmp_control(const struct rtmp_conn *conn, enum rtmp_packet_type type, ...); void rtmp_set_handlers(struct rtmp_conn *conn, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg); struct tcp_conn *rtmp_conn_tcpconn(const struct rtmp_conn *conn); const char *rtmp_conn_stream(const struct rtmp_conn *conn); int rtmp_conn_debug(struct re_printf *pf, const struct rtmp_conn *conn); typedef void (rtmp_resp_h)(bool success, const struct odict *msg, void *arg); /* amf */ int rtmp_amf_command(const struct rtmp_conn *conn, uint32_t stream_id, const char *command, unsigned body_propc, ...); int rtmp_amf_request(struct rtmp_conn *conn, uint32_t stream_id, const char *command, rtmp_resp_h *resph, void *arg, unsigned body_propc, ...); int rtmp_amf_reply(struct rtmp_conn *conn, uint32_t stream_id, bool success, const struct odict *req, unsigned body_propc, ...); int rtmp_amf_data(const struct rtmp_conn *conn, uint32_t stream_id, const char *command, unsigned body_propc, ...); /* stream */ struct rtmp_stream; typedef void (rtmp_control_h)(enum rtmp_event_type event, struct mbuf *mb, void *arg); typedef void (rtmp_audio_h)(uint32_t timestamp, const uint8_t *pld, size_t len, void *arg); typedef void (rtmp_video_h)(uint32_t timestamp, const uint8_t *pld, size_t len, void *arg); int rtmp_stream_alloc(struct rtmp_stream **strmp, struct rtmp_conn *conn, uint32_t stream_id, rtmp_command_h *cmdh, rtmp_control_h *ctrlh, rtmp_audio_h *auh, rtmp_video_h *vidh, rtmp_command_h *datah, void *arg); int rtmp_stream_create(struct rtmp_stream **strmp, struct rtmp_conn *conn, rtmp_resp_h *resph, rtmp_command_h *cmdh, rtmp_control_h *ctrlh, rtmp_audio_h *auh, rtmp_video_h *vidh, rtmp_command_h *datah, void *arg); int rtmp_play(struct rtmp_stream *strm, const char *name); int rtmp_publish(struct rtmp_stream *strm, const char *name); int rtmp_meta(struct rtmp_stream *strm); int rtmp_send_audio(struct rtmp_stream *strm, uint32_t timestamp, const uint8_t *pld, size_t len); int rtmp_send_video(struct rtmp_stream *strm, uint32_t timestamp, const uint8_t *pld, size_t len); struct rtmp_stream *rtmp_stream_find(const struct rtmp_conn *conn, uint32_t stream_id); const char *rtmp_event_name(enum rtmp_event_type event); re-1.1.0/include/re_rtp.h000066400000000000000000000213551373627245400152310ustar00rootroot00000000000000/** * @file re_rtp.h Interface to Real-time Transport Protocol and RTCP * * Copyright (C) 2010 Creytiv.com */ /** RTP protocol values */ enum { RTP_VERSION = 2, /**< Defines the RTP version we support */ RTCP_VERSION = 2, /**< Supported RTCP Version */ RTP_HEADER_SIZE = 12 /**< Number of bytes in RTP Header */ }; /** Defines the RTP header */ struct rtp_header { uint8_t ver; /**< RTP version number */ bool pad; /**< Padding bit */ bool ext; /**< Extension bit */ uint8_t cc; /**< CSRC count */ bool m; /**< Marker bit */ uint8_t pt; /**< Payload type */ uint16_t seq; /**< Sequence number */ uint32_t ts; /**< Timestamp */ uint32_t ssrc; /**< Synchronization source */ uint32_t csrc[16]; /**< Contributing sources */ struct { uint16_t type; /**< Defined by profile */ uint16_t len; /**< Number of 32-bit words */ } x; }; /** RTCP Packet Types */ enum rtcp_type { RTCP_FIR = 192, /**< Full INTRA-frame Request (RFC 2032) */ RTCP_NACK = 193, /**< Negative Acknowledgement (RFC 2032) */ RTCP_SR = 200, /**< Sender Report */ RTCP_RR = 201, /**< Receiver Report */ RTCP_SDES = 202, /**< Source Description */ RTCP_BYE = 203, /**< Goodbye */ RTCP_APP = 204, /**< Application-defined */ RTCP_RTPFB = 205, /**< Transport layer FB message (RFC 4585) */ RTCP_PSFB = 206, /**< Payload-specific FB message (RFC 4585) */ RTCP_XR = 207, /**< Extended Report (RFC 3611) */ RTCP_AVB = 208, /**< AVB RTCP Packet (IEEE1733) */ }; /** SDES Types */ enum rtcp_sdes_type { RTCP_SDES_END = 0, /**< End of SDES list */ RTCP_SDES_CNAME = 1, /**< Canonical name */ RTCP_SDES_NAME = 2, /**< User name */ RTCP_SDES_EMAIL = 3, /**< User's electronic mail address */ RTCP_SDES_PHONE = 4, /**< User's phone number */ RTCP_SDES_LOC = 5, /**< Geographic user location */ RTCP_SDES_TOOL = 6, /**< Name of application or tool */ RTCP_SDES_NOTE = 7, /**< Notice about the source */ RTCP_SDES_PRIV = 8 /**< Private extension */ }; /** Transport Layer Feedback Messages */ enum rtcp_rtpfb { RTCP_RTPFB_GNACK = 1 /**< Generic NACK */ }; /** Payload-Specific Feedback Messages */ enum rtcp_psfb { RTCP_PSFB_PLI = 1, /**< Picture Loss Indication (PLI) */ RTCP_PSFB_SLI = 2, /**< Slice Loss Indication (SLI) */ RTCP_PSFB_AFB = 15, /**< Application layer Feedback Messages */ }; /** Reception report block */ struct rtcp_rr { uint32_t ssrc; /**< Data source being reported */ unsigned int fraction:8; /**< Fraction lost since last SR/RR */ int lost:24; /**< Cumul. no. pkts lost (signed!) */ uint32_t last_seq; /**< Extended last seq. no. received */ uint32_t jitter; /**< Interarrival jitter */ uint32_t lsr; /**< Last SR packet from this source */ uint32_t dlsr; /**< Delay since last SR packet */ }; /** SDES item */ struct rtcp_sdes_item { enum rtcp_sdes_type type; /**< Type of item (enum rtcp_sdes_type) */ uint8_t length; /**< Length of item (in octets) */ char *data; /**< Text, not null-terminated */ }; /** One RTCP Message */ struct rtcp_msg { /** RTCP Header */ struct rtcp_hdr { unsigned int version:2; /**< Protocol version */ unsigned int p:1; /**< Padding flag */ unsigned int count:5; /**< Varies by packet type */ unsigned int pt:8; /**< RTCP packet type */ uint16_t length; /**< Packet length in words */ } hdr; union { /** Sender report (SR) */ struct { uint32_t ssrc; /**< Sender generating report */ uint32_t ntp_sec; /**< NTP timestamp - seconds */ uint32_t ntp_frac; /**< NTP timestamp - fractions */ uint32_t rtp_ts; /**< RTP timestamp */ uint32_t psent; /**< RTP packets sent */ uint32_t osent; /**< RTP octets sent */ struct rtcp_rr *rrv; /**< Reception report blocks */ } sr; /** Reception report (RR) */ struct { uint32_t ssrc; /**< Receiver generating report*/ struct rtcp_rr *rrv; /**< Reception report blocks */ } rr; /** Source Description (SDES) */ struct rtcp_sdes { uint32_t src; /**< First SSRC/CSRC */ struct rtcp_sdes_item *itemv; /**< SDES items */ uint32_t n; /**< Number of SDES items */ } *sdesv; /** BYE */ struct { uint32_t *srcv; /**< List of sources */ char *reason; /**< Reason for leaving (opt.) */ } bye; /** Application-defined (APP) */ struct { uint32_t src; /**< SSRC/CSRC */ char name[4]; /**< Name (ASCII) */ uint8_t *data; /**< Application data (32 bits) */ size_t data_len; /**< Number of data bytes */ } app; /** Full INTRA-frame Request (FIR) packet */ struct { uint32_t ssrc; /**< SSRC for sender of this packet */ } fir; /** Negative ACKnowledgements (NACK) packet */ struct { uint32_t ssrc; /**< SSRC for sender of this packet */ uint16_t fsn; /**< First Sequence Number lost */ uint16_t blp; /**< Bitmask of lost packets */ } nack; /** Feedback (RTPFB or PSFB) packet */ struct { uint32_t ssrc_packet; uint32_t ssrc_media; uint32_t n; /** Feedback Control Information (FCI) */ union { struct gnack { uint16_t pid; uint16_t blp; } *gnackv; struct sli { uint16_t first; uint16_t number; uint8_t picid; } *sliv; struct mbuf *afb; void *p; } fci; } fb; } r; }; /** RTCP Statistics */ struct rtcp_stats { struct { uint32_t sent; /**< Tx RTP Packets */ int lost; /**< Tx RTP Packets Lost */ uint32_t jit; /**< Tx Inter-arrival Jitter in [us] */ } tx; struct { uint32_t sent; /**< Rx RTP Packets */ int lost; /**< Rx RTP Packets Lost */ uint32_t jit; /**< Rx Inter-Arrival Jitter in [us] */ } rx; uint32_t rtt; /**< Current Round-Trip Time in [us] */ }; struct sa; struct re_printf; struct rtp_sock; typedef void (rtp_recv_h)(const struct sa *src, const struct rtp_header *hdr, struct mbuf *mb, void *arg); typedef void (rtcp_recv_h)(const struct sa *src, struct rtcp_msg *msg, void *arg); /* RTP api */ int rtp_alloc(struct rtp_sock **rsp); int rtp_listen(struct rtp_sock **rsp, int proto, const struct sa *ip, uint16_t min_port, uint16_t max_port, bool enable_rtcp, rtp_recv_h *recvh, rtcp_recv_h *rtcph, void *arg); int rtp_hdr_encode(struct mbuf *mb, const struct rtp_header *hdr); int rtp_hdr_decode(struct rtp_header *hdr, struct mbuf *mb); int rtp_encode(struct rtp_sock *rs, bool ext, bool marker, uint8_t pt, uint32_t ts, struct mbuf *mb); int rtp_decode(struct rtp_sock *rs, struct mbuf *mb, struct rtp_header *hdr); int rtp_send(struct rtp_sock *rs, const struct sa *dst, bool ext, bool marker, uint8_t pt, uint32_t ts, struct mbuf *mb); int rtp_debug(struct re_printf *pf, const struct rtp_sock *rs); void *rtp_sock(const struct rtp_sock *rs); uint32_t rtp_sess_ssrc(const struct rtp_sock *rs); const struct sa *rtp_local(const struct rtp_sock *rs); /* RTCP session api */ void rtcp_start(struct rtp_sock *rs, const char *cname, const struct sa *peer); void rtcp_enable_mux(struct rtp_sock *rs, bool enabled); void rtcp_set_srate(struct rtp_sock *rs, uint32_t sr_tx, uint32_t sr_rx); void rtcp_set_srate_tx(struct rtp_sock *rs, uint32_t srate_tx); void rtcp_set_srate_rx(struct rtp_sock *rs, uint32_t srate_rx); int rtcp_send_app(struct rtp_sock *rs, const char name[4], const uint8_t *data, size_t len); int rtcp_send_fir(struct rtp_sock *rs, uint32_t ssrc); int rtcp_send_nack(struct rtp_sock *rs, uint16_t fsn, uint16_t blp); int rtcp_send_pli(struct rtp_sock *rs, uint32_t fb_ssrc); int rtcp_debug(struct re_printf *pf, const struct rtp_sock *rs); void *rtcp_sock(const struct rtp_sock *rs); int rtcp_stats(struct rtp_sock *rs, uint32_t ssrc, struct rtcp_stats *stats); /* RTCP utils */ int rtcp_encode(struct mbuf *mb, enum rtcp_type type, uint32_t count, ...); int rtcp_decode(struct rtcp_msg **msgp, struct mbuf *mb); int rtcp_msg_print(struct re_printf *pf, const struct rtcp_msg *msg); int rtcp_sdes_encode(struct mbuf *mb, uint32_t src, uint32_t itemc, ...); const char *rtcp_type_name(enum rtcp_type type); const char *rtcp_sdes_name(enum rtcp_sdes_type sdes); re-1.1.0/include/re_sa.h000066400000000000000000000033161373627245400150240ustar00rootroot00000000000000/** * @file re_sa.h Interface to Socket Address * * Copyright (C) 2010 Creytiv.com */ #if defined(WIN32) #include #include #else #include #include #include #endif struct pl; /** Socket Address flags */ enum sa_flag { SA_ADDR = 1<<0, SA_PORT = 1<<1, SA_ALL = SA_ADDR | SA_PORT }; /** Defines a Socket Address */ struct sa { union { struct sockaddr sa; struct sockaddr_in in; #ifdef HAVE_INET6 struct sockaddr_in6 in6; #endif uint8_t padding[28]; } u; socklen_t len; }; void sa_init(struct sa *sa, int af); int sa_set(struct sa *sa, const struct pl *addr, uint16_t port); int sa_set_str(struct sa *sa, const char *addr, uint16_t port); void sa_set_in(struct sa *sa, uint32_t addr, uint16_t port); void sa_set_in6(struct sa *sa, const uint8_t *addr, uint16_t port); int sa_set_sa(struct sa *sa, const struct sockaddr *s); void sa_set_port(struct sa *sa, uint16_t port); int sa_decode(struct sa *sa, const char *str, size_t len); int sa_af(const struct sa *sa); uint32_t sa_in(const struct sa *sa); void sa_in6(const struct sa *sa, uint8_t *addr); int sa_ntop(const struct sa *sa, char *buf, int size); uint16_t sa_port(const struct sa *sa); bool sa_isset(const struct sa *sa, int flag); uint32_t sa_hash(const struct sa *sa, int flag); void sa_cpy(struct sa *dst, const struct sa *src); bool sa_cmp(const struct sa *l, const struct sa *r, int flag); bool sa_is_linklocal(const struct sa *sa); bool sa_is_loopback(const struct sa *sa); bool sa_is_any(const struct sa *sa); struct re_printf; int sa_print_addr(struct re_printf *pf, const struct sa *sa); re-1.1.0/include/re_sdp.h000066400000000000000000000147531373627245400152160ustar00rootroot00000000000000/** * @file re_sdp.h Interface to Session Description Protocol (SDP) * * Copyright (C) 2010 Creytiv.com */ enum { SDP_VERSION = 0 }; /** SDP Direction */ enum sdp_dir { SDP_INACTIVE = 0, SDP_RECVONLY = 1, SDP_SENDONLY = 2, SDP_SENDRECV = 3, }; /** SDP Bandwidth type */ enum sdp_bandwidth { SDP_BANDWIDTH_MIN = 0, SDP_BANDWIDTH_CT = 0, /**< [kbit/s] Conference Total */ SDP_BANDWIDTH_AS, /**< [kbit/s] Application Specific */ SDP_BANDWIDTH_RS, /**< [bit/s] RTCP Senders (RFC 3556) */ SDP_BANDWIDTH_RR, /**< [bit/s] RTCP Receivers (RFC 3556) */ SDP_BANDWIDTH_TIAS, /**< [bit/s] Transport Independent Application Specific Maximum (RFC 3890) */ SDP_BANDWIDTH_MAX, }; struct sdp_format; typedef int(sdp_media_enc_h)(struct mbuf *mb, bool offer, void *arg); typedef int(sdp_fmtp_enc_h)(struct mbuf *mb, const struct sdp_format *fmt, bool offer, void *data); typedef bool(sdp_fmtp_cmp_h)(const char *params1, const char *params2, void *data); typedef bool(sdp_format_h)(struct sdp_format *fmt, void *arg); typedef bool(sdp_attr_h)(const char *name, const char *value, void *arg); /** SDP Format */ struct sdp_format { struct le le; char *id; char *params; char *rparams; char *name; sdp_fmtp_enc_h *ench; sdp_fmtp_cmp_h *cmph; void *data; bool ref; bool sup; int pt; uint32_t srate; uint8_t ch; }; /* session */ struct sdp_session; int sdp_session_alloc(struct sdp_session **sessp, const struct sa *laddr); void sdp_session_set_laddr(struct sdp_session *sess, const struct sa *laddr); void sdp_session_set_lbandwidth(struct sdp_session *sess, enum sdp_bandwidth type, int32_t bw); int sdp_session_set_lattr(struct sdp_session *sess, bool replace, const char *name, const char *value, ...); void sdp_session_del_lattr(struct sdp_session *sess, const char *name); int32_t sdp_session_lbandwidth(const struct sdp_session *sess, enum sdp_bandwidth type); int32_t sdp_session_rbandwidth(const struct sdp_session *sess, enum sdp_bandwidth type); const char *sdp_session_rattr(const struct sdp_session *sess, const char *name); const char *sdp_session_rattr_apply(const struct sdp_session *sess, const char *name, sdp_attr_h *attrh, void *arg); const struct list *sdp_session_medial(const struct sdp_session *sess, bool local); int sdp_session_debug(struct re_printf *pf, const struct sdp_session *sess); /* media */ struct sdp_media; int sdp_media_add(struct sdp_media **mp, struct sdp_session *sess, const char *name, uint16_t port, const char *proto); int sdp_media_set_alt_protos(struct sdp_media *m, unsigned protoc, ...); void sdp_media_set_encode_handler(struct sdp_media *m, sdp_media_enc_h *ench, void *arg); void sdp_media_set_fmt_ignore(struct sdp_media *m, bool fmt_ignore); bool sdp_media_disabled(struct sdp_media *m); void sdp_media_set_disabled(struct sdp_media *m, bool disabled); void sdp_media_set_lport(struct sdp_media *m, uint16_t port); void sdp_media_set_laddr(struct sdp_media *m, const struct sa *laddr); void sdp_media_set_lbandwidth(struct sdp_media *m, enum sdp_bandwidth type, int32_t bw); void sdp_media_set_lport_rtcp(struct sdp_media *m, uint16_t port); void sdp_media_set_laddr_rtcp(struct sdp_media *m, const struct sa *laddr); void sdp_media_set_ldir(struct sdp_media *m, enum sdp_dir dir); int sdp_media_set_lattr(struct sdp_media *m, bool replace, const char *name, const char *value, ...); void sdp_media_del_lattr(struct sdp_media *m, const char *name); const char *sdp_media_proto(const struct sdp_media *m); uint16_t sdp_media_rport(const struct sdp_media *m); const struct sa *sdp_media_raddr(const struct sdp_media *m); const struct sa *sdp_media_laddr(const struct sdp_media *m); void sdp_media_raddr_rtcp(const struct sdp_media *m, struct sa *raddr); int32_t sdp_media_rbandwidth(const struct sdp_media *m, enum sdp_bandwidth type); enum sdp_dir sdp_media_ldir(const struct sdp_media *m); enum sdp_dir sdp_media_rdir(const struct sdp_media *m); enum sdp_dir sdp_media_dir(const struct sdp_media *m); const struct sdp_format *sdp_media_lformat(const struct sdp_media *m, int pt); const struct sdp_format *sdp_media_rformat(const struct sdp_media *m, const char *name); struct sdp_format *sdp_media_format(const struct sdp_media *m, bool local, const char *id, int pt, const char *name, int32_t srate, int8_t ch); struct sdp_format *sdp_media_format_apply(const struct sdp_media *m, bool local, const char *id, int pt, const char *name, int32_t srate, int8_t ch, sdp_format_h *fmth, void *arg); const struct list *sdp_media_format_lst(const struct sdp_media *m, bool local); const char *sdp_media_rattr(const struct sdp_media *m, const char *name); const char *sdp_media_session_rattr(const struct sdp_media *m, const struct sdp_session *sess, const char *name); const char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name, sdp_attr_h *attrh, void *arg); const char *sdp_media_name(const struct sdp_media *m); int sdp_media_debug(struct re_printf *pf, const struct sdp_media *m); /* format */ int sdp_format_add(struct sdp_format **fmtp, struct sdp_media *m, bool prepend, const char *id, const char *name, uint32_t srate, uint8_t ch, sdp_fmtp_enc_h *ench, sdp_fmtp_cmp_h *cmph, void *data, bool ref, const char *params, ...); int sdp_format_set_params(struct sdp_format *fmt, const char *params, ...); bool sdp_format_cmp(const struct sdp_format *fmt1, const struct sdp_format *fmt2); int sdp_format_debug(struct re_printf *pf, const struct sdp_format *fmt); /* encode/decode */ int sdp_encode(struct mbuf **mbp, struct sdp_session *sess, bool offer); int sdp_decode(struct sdp_session *sess, struct mbuf *mb, bool offer); /* strings */ const char *sdp_dir_name(enum sdp_dir dir); const char *sdp_bandwidth_name(enum sdp_bandwidth type); extern const char sdp_attr_fmtp[]; extern const char sdp_attr_maxptime[]; extern const char sdp_attr_ptime[]; extern const char sdp_attr_rtcp[]; extern const char sdp_attr_rtpmap[]; extern const char sdp_media_audio[]; extern const char sdp_media_video[]; extern const char sdp_media_text[]; extern const char sdp_proto_rtpavp[]; extern const char sdp_proto_rtpsavp[]; /* utility functions */ /** RTP Header Extensions, as defined in RFC 5285 */ struct sdp_extmap { struct pl name; struct pl attrs; enum sdp_dir dir; bool dir_set; uint32_t id; }; int sdp_extmap_decode(struct sdp_extmap *ext, const char *val); re-1.1.0/include/re_sha.h000066400000000000000000000015331373627245400151730ustar00rootroot00000000000000/** * @file re_sha.h Interface to SHA (Secure Hash Standard) functions * * Copyright (C) 2010 Creytiv.com */ #ifdef USE_OPENSSL #include #else /* public api for steve reid's public domain SHA-1 implementation */ /* this file is in the public domain */ /** SHA-1 Context */ typedef struct { uint32_t state[5]; /**< Context state */ uint32_t count[2]; /**< Counter */ uint8_t buffer[64]; /**< SHA-1 buffer */ } SHA1_CTX; /** SHA-1 Context (OpenSSL compat) */ typedef SHA1_CTX SHA_CTX; /** SHA-1 Digest size in bytes */ #define SHA1_DIGEST_SIZE 20 /** SHA-1 Digest size in bytes (OpenSSL compat) */ #define SHA_DIGEST_LENGTH SHA1_DIGEST_SIZE void SHA1_Init(SHA1_CTX* context); void SHA1_Update(SHA1_CTX* context, const void *p, size_t len); void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX* context); #endif re-1.1.0/include/re_sip.h000066400000000000000000000345341373627245400152220ustar00rootroot00000000000000/** * @file re_sip.h Session Initiation Protocol * * Copyright (C) 2010 Creytiv.com */ enum { SIP_PORT = 5060, SIP_PORT_TLS = 5061, }; /** SIP Transport */ enum sip_transp { SIP_TRANSP_NONE = -1, SIP_TRANSP_UDP = 0, SIP_TRANSP_TCP, SIP_TRANSP_TLS, SIP_TRANSP_WS, SIP_TRANSP_WSS, SIP_TRANSPC, }; /** SIP Header ID (perfect hash value) */ enum sip_hdrid { SIP_HDR_ACCEPT = 3186, SIP_HDR_ACCEPT_CONTACT = 232, SIP_HDR_ACCEPT_ENCODING = 708, SIP_HDR_ACCEPT_LANGUAGE = 2867, SIP_HDR_ACCEPT_RESOURCE_PRIORITY = 1848, SIP_HDR_ALERT_INFO = 274, SIP_HDR_ALLOW = 2429, SIP_HDR_ALLOW_EVENTS = 66, SIP_HDR_ANSWER_MODE = 2905, SIP_HDR_AUTHENTICATION_INFO = 3144, SIP_HDR_AUTHORIZATION = 2503, SIP_HDR_CALL_ID = 3095, SIP_HDR_CALL_INFO = 586, SIP_HDR_CONTACT = 229, SIP_HDR_CONTENT_DISPOSITION = 1425, SIP_HDR_CONTENT_ENCODING = 580, SIP_HDR_CONTENT_LANGUAGE = 3371, SIP_HDR_CONTENT_LENGTH = 3861, SIP_HDR_CONTENT_TYPE = 809, SIP_HDR_CSEQ = 746, SIP_HDR_DATE = 1027, SIP_HDR_ENCRYPTION = 3125, SIP_HDR_ERROR_INFO = 21, SIP_HDR_EVENT = 3286, SIP_HDR_EXPIRES = 1983, SIP_HDR_FLOW_TIMER = 584, SIP_HDR_FROM = 1963, SIP_HDR_HIDE = 283, SIP_HDR_HISTORY_INFO = 2582, SIP_HDR_IDENTITY = 2362, SIP_HDR_IDENTITY_INFO = 980, SIP_HDR_IN_REPLY_TO = 1577, SIP_HDR_JOIN = 3479, SIP_HDR_MAX_BREADTH = 3701, SIP_HDR_MAX_FORWARDS = 3549, SIP_HDR_MIME_VERSION = 3659, SIP_HDR_MIN_EXPIRES = 1121, SIP_HDR_MIN_SE = 2847, SIP_HDR_ORGANIZATION = 3247, SIP_HDR_P_ACCESS_NETWORK_INFO = 1662, SIP_HDR_P_ANSWER_STATE = 42, SIP_HDR_P_ASSERTED_IDENTITY = 1233, SIP_HDR_P_ASSOCIATED_URI = 900, SIP_HDR_P_CALLED_PARTY_ID = 3347, SIP_HDR_P_CHARGING_FUNCTION_ADDRESSES = 2171, SIP_HDR_P_CHARGING_VECTOR = 25, SIP_HDR_P_DCS_TRACE_PARTY_ID = 3027, SIP_HDR_P_DCS_OSPS = 1788, SIP_HDR_P_DCS_BILLING_INFO = 2017, SIP_HDR_P_DCS_LAES = 693, SIP_HDR_P_DCS_REDIRECT = 1872, SIP_HDR_P_EARLY_MEDIA = 2622, SIP_HDR_P_MEDIA_AUTHORIZATION = 1035, SIP_HDR_P_PREFERRED_IDENTITY = 1263, SIP_HDR_P_PROFILE_KEY = 1904, SIP_HDR_P_REFUSED_URI_LIST = 1047, SIP_HDR_P_SERVED_USER = 1588, SIP_HDR_P_USER_DATABASE = 2827, SIP_HDR_P_VISITED_NETWORK_ID = 3867, SIP_HDR_PATH = 2741, SIP_HDR_PERMISSION_MISSING = 1409, SIP_HDR_PRIORITY = 3520, SIP_HDR_PRIV_ANSWER_MODE = 2476, SIP_HDR_PRIVACY = 3150, SIP_HDR_PROXY_AUTHENTICATE = 116, SIP_HDR_PROXY_AUTHORIZATION = 2363, SIP_HDR_PROXY_REQUIRE = 3562, SIP_HDR_RACK = 2523, SIP_HDR_REASON = 3732, SIP_HDR_RECORD_ROUTE = 278, SIP_HDR_REFER_SUB = 2458, SIP_HDR_REFER_TO = 1521, SIP_HDR_REFERRED_BY = 3456, SIP_HDR_REJECT_CONTACT = 285, SIP_HDR_REPLACES = 2534, SIP_HDR_REPLY_TO = 2404, SIP_HDR_REQUEST_DISPOSITION = 3715, SIP_HDR_REQUIRE = 3905, SIP_HDR_RESOURCE_PRIORITY = 1643, SIP_HDR_RESPONSE_KEY = 1548, SIP_HDR_RETRY_AFTER = 409, SIP_HDR_ROUTE = 661, SIP_HDR_RSEQ = 445, SIP_HDR_SECURITY_CLIENT = 1358, SIP_HDR_SECURITY_SERVER = 811, SIP_HDR_SECURITY_VERIFY = 519, SIP_HDR_SERVER = 973, SIP_HDR_SERVICE_ROUTE = 1655, SIP_HDR_SESSION_EXPIRES = 1979, SIP_HDR_SIP_ETAG = 1997, SIP_HDR_SIP_IF_MATCH = 3056, SIP_HDR_SUBJECT = 1043, SIP_HDR_SUBSCRIPTION_STATE = 2884, SIP_HDR_SUPPORTED = 119, SIP_HDR_TARGET_DIALOG = 3450, SIP_HDR_TIMESTAMP = 938, SIP_HDR_TO = 1449, SIP_HDR_TRIGGER_CONSENT = 3180, SIP_HDR_UNSUPPORTED = 982, SIP_HDR_USER_AGENT = 4064, SIP_HDR_VIA = 3961, SIP_HDR_WARNING = 2108, SIP_HDR_WWW_AUTHENTICATE = 2763, SIP_HDR_NONE = -1 }; enum { SIP_T1 = 500, SIP_T2 = 4000, SIP_T4 = 5000, }; /** SIP Via header */ struct sip_via { struct pl sentby; struct sa addr; struct pl params; struct pl branch; struct pl val; enum sip_transp tp; }; /** SIP Address */ struct sip_addr { struct pl dname; struct pl auri; struct uri uri; struct pl params; }; /** SIP Tag address */ struct sip_taddr { struct pl dname; struct pl auri; struct uri uri; struct pl params; struct pl tag; struct pl val; }; /** SIP CSeq header */ struct sip_cseq { struct pl met; uint32_t num; }; /** SIP Header */ struct sip_hdr { struct le le; /**< Linked-list element */ struct le he; /**< Hash-table element */ struct pl name; /**< SIP Header name */ struct pl val; /**< SIP Header value */ enum sip_hdrid id; /**< SIP Header id (unique) */ }; /** SIP Message */ struct sip_msg { struct sa src; /**< Source network address */ struct sa dst; /**< Destination network address */ struct pl ver; /**< SIP Version number */ struct pl met; /**< Request method */ struct pl ruri; /**< Raw request URI */ struct uri uri; /**< Parsed request URI */ uint16_t scode; /**< Response status code */ struct pl reason; /**< Response reason phrase */ struct list hdrl; /**< List of SIP Headers (struct sip_hdr) */ struct sip_via via; /**< Parsed first Via header */ struct sip_taddr to; /**< Parsed To header */ struct sip_taddr from; /**< Parsed From header */ struct sip_cseq cseq; /**< Parsed CSeq header */ struct msg_ctype ctyp; /**< Content Type */ struct pl callid; /**< Cached Call-ID header */ struct pl maxfwd; /**< Cached Max-Forwards header */ struct pl expires; /**< Cached Expires header */ struct pl clen; /**< Cached Content-Length header */ struct hash *hdrht; /**< Hash-table with all SIP headers */ struct mbuf *mb; /**< Buffer containing the SIP message */ void *sock; /**< Transport socket */ uint64_t tag; /**< Opaque tag */ enum sip_transp tp; /**< SIP Transport */ bool req; /**< True if Request, False if Response */ }; /** SIP Loop-state */ struct sip_loopstate { uint32_t failc; uint16_t last_scode; }; /** SIP Contact */ struct sip_contact { const char *uri; const struct sa *addr; enum sip_transp tp; }; struct sip; struct sip_lsnr; struct sip_request; struct sip_strans; struct sip_auth; struct sip_dialog; struct sip_keepalive; struct dnsc; typedef bool(sip_msg_h)(const struct sip_msg *msg, void *arg); typedef int(sip_send_h)(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg); typedef void(sip_resp_h)(int err, const struct sip_msg *msg, void *arg); typedef void(sip_cancel_h)(void *arg); typedef void(sip_exit_h)(void *arg); typedef int(sip_auth_h)(char **username, char **password, const char *realm, void *arg); typedef bool(sip_hdr_h)(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg); typedef void(sip_keepalive_h)(int err, void *arg); #define LIBRE_HAVE_SIPTRACE 1 typedef void(sip_trace_h)(bool tx, enum sip_transp tp, const struct sa *src, const struct sa *dst, const uint8_t *pkt, size_t len, void *arg); /* sip */ int sip_alloc(struct sip **sipp, struct dnsc *dnsc, uint32_t ctsz, uint32_t stsz, uint32_t tcsz, const char *software, sip_exit_h *exith, void *arg); void sip_close(struct sip *sip, bool force); int sip_listen(struct sip_lsnr **lsnrp, struct sip *sip, bool req, sip_msg_h *msgh, void *arg); int sip_debug(struct re_printf *pf, const struct sip *sip); int sip_send(struct sip *sip, void *sock, enum sip_transp tp, const struct sa *dst, struct mbuf *mb); void sip_set_trace_handler(struct sip *sip, sip_trace_h *traceh); /* transport */ int sip_transp_add(struct sip *sip, enum sip_transp tp, const struct sa *laddr, ...); int sip_transp_add_websock(struct sip *sip, enum sip_transp tp, const struct sa *laddr, bool server, const char *cert); void sip_transp_flush(struct sip *sip); bool sip_transp_isladdr(const struct sip *sip, enum sip_transp tp, const struct sa *laddr); const char *sip_transp_name(enum sip_transp tp); const char *sip_transp_param(enum sip_transp tp); uint16_t sip_transp_port(enum sip_transp tp, uint16_t port); int sip_transp_laddr(struct sip *sip, struct sa *laddr, enum sip_transp tp, const struct sa *dst); /* request */ int sip_request(struct sip_request **reqp, struct sip *sip, bool stateful, const char *met, int metl, const char *uri, int uril, const struct uri *route, struct mbuf *mb, size_t sortkey, sip_send_h *sendh, sip_resp_h *resph, void *arg); int sip_requestf(struct sip_request **reqp, struct sip *sip, bool stateful, const char *met, const char *uri, const struct uri *route, struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph, void *arg, const char *fmt, ...); int sip_drequestf(struct sip_request **reqp, struct sip *sip, bool stateful, const char *met, struct sip_dialog *dlg, uint32_t cseq, struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph, void *arg, const char *fmt, ...); void sip_request_cancel(struct sip_request *req); bool sip_request_loops(struct sip_loopstate *ls, uint16_t scode); void sip_loopstate_reset(struct sip_loopstate *ls); /* reply */ int sip_strans_alloc(struct sip_strans **stp, struct sip *sip, const struct sip_msg *msg, sip_cancel_h *cancelh, void *arg); int sip_strans_reply(struct sip_strans **stp, struct sip *sip, const struct sip_msg *msg, const struct sa *dst, uint16_t scode, struct mbuf *mb); int sip_treplyf(struct sip_strans **stp, struct mbuf **mbp, struct sip *sip, const struct sip_msg *msg, bool rec_route, uint16_t scode, const char *reason, const char *fmt, ...); int sip_treply(struct sip_strans **stp, struct sip *sip, const struct sip_msg *msg, uint16_t scode, const char *reason); int sip_replyf(struct sip *sip, const struct sip_msg *msg, uint16_t scode, const char *reason, const char *fmt, ...); int sip_reply(struct sip *sip, const struct sip_msg *msg, uint16_t scode, const char *reason); void sip_reply_addr(struct sa *addr, const struct sip_msg *msg, bool rport); /* auth */ int sip_auth_authenticate(struct sip_auth *auth, const struct sip_msg *msg); int sip_auth_alloc(struct sip_auth **authp, sip_auth_h *authh, void *arg, bool ref); void sip_auth_reset(struct sip_auth *auth); /* contact */ void sip_contact_set(struct sip_contact *contact, const char *uri, const struct sa *addr, enum sip_transp tp); int sip_contact_print(struct re_printf *pf, const struct sip_contact *contact); /* dialog */ int sip_dialog_alloc(struct sip_dialog **dlgp, const char *uri, const char *to_uri, const char *from_name, const char *from_uri, const char *routev[], uint32_t routec); int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg); int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg); int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg, const struct sip_msg *msg); int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg); bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg); const char *sip_dialog_callid(const struct sip_dialog *dlg); uint32_t sip_dialog_lseq(const struct sip_dialog *dlg); bool sip_dialog_established(const struct sip_dialog *dlg); bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg); bool sip_dialog_cmp_half(const struct sip_dialog *dlg, const struct sip_msg *msg); /* msg */ int sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb); const struct sip_hdr *sip_msg_hdr(const struct sip_msg *msg, enum sip_hdrid id); const struct sip_hdr *sip_msg_hdr_apply(const struct sip_msg *msg, bool fwd, enum sip_hdrid id, sip_hdr_h *h, void *arg); const struct sip_hdr *sip_msg_xhdr(const struct sip_msg *msg, const char *name); const struct sip_hdr *sip_msg_xhdr_apply(const struct sip_msg *msg, bool fwd, const char *name, sip_hdr_h *h, void *arg); uint32_t sip_msg_hdr_count(const struct sip_msg *msg, enum sip_hdrid id); uint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name); bool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id, const char *value); bool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name, const char *value); struct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg); void sip_msg_dump(const struct sip_msg *msg); int sip_addr_decode(struct sip_addr *addr, const struct pl *pl); int sip_via_decode(struct sip_via *via, const struct pl *pl); int sip_cseq_decode(struct sip_cseq *cseq, const struct pl *pl); /* keepalive */ int sip_keepalive_start(struct sip_keepalive **kap, struct sip *sip, const struct sip_msg *msg, uint32_t interval, sip_keepalive_h *kah, void *arg); re-1.1.0/include/re_sipevent.h000066400000000000000000000076061373627245400162640ustar00rootroot00000000000000/** * @file re_sipevent.h SIP Event Framework * * Copyright (C) 2010 Creytiv.com */ /* Message Components */ struct sipevent_event { struct pl event; struct pl params; struct pl id; }; enum sipevent_subst { SIPEVENT_ACTIVE = 0, SIPEVENT_PENDING, SIPEVENT_TERMINATED, }; enum sipevent_reason { SIPEVENT_DEACTIVATED = 0, SIPEVENT_PROBATION, SIPEVENT_REJECTED, SIPEVENT_TIMEOUT, SIPEVENT_GIVEUP, SIPEVENT_NORESOURCE, }; struct sipevent_substate { enum sipevent_subst state; enum sipevent_reason reason; struct pl expires; struct pl retry_after; struct pl params; }; int sipevent_event_decode(struct sipevent_event *se, const struct pl *pl); int sipevent_substate_decode(struct sipevent_substate *ss, const struct pl *pl); const char *sipevent_substate_name(enum sipevent_subst state); const char *sipevent_reason_name(enum sipevent_reason reason); /* Listener Socket */ struct sipevent_sock; int sipevent_listen(struct sipevent_sock **sockp, struct sip *sip, uint32_t htsize_not, uint32_t htsize_sub, sip_msg_h *subh, void *arg); /* Notifier */ struct sipnot; typedef void (sipnot_close_h)(int err, const struct sip_msg *msg, void *arg); int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock, const struct sip_msg *msg, struct sip_dialog *dlg, const struct sipevent_event *event, uint16_t scode, const char *reason, uint32_t expires_min, uint32_t expires_dfl, uint32_t expires_max, const char *cuser, const char *ctype, sip_auth_h *authh, void *aarg, bool aref, sipnot_close_h *closeh, void *arg, const char *fmt, ...); int sipevent_notify(struct sipnot *sipnot, struct mbuf *mb, enum sipevent_subst state, enum sipevent_reason reason, uint32_t retry_after); int sipevent_notifyf(struct sipnot *sipnot, struct mbuf **mbp, enum sipevent_subst state, enum sipevent_reason reason, uint32_t retry_after, const char *fmt, ...); /* Subscriber */ struct sipsub; typedef int (sipsub_fork_h)(struct sipsub **subp, struct sipsub *osub, const struct sip_msg *msg, void *arg); typedef void (sipsub_notify_h)(struct sip *sip, const struct sip_msg *msg, void *arg); typedef void (sipsub_close_h)(int err, const struct sip_msg *msg, const struct sipevent_substate *substate, void *arg); int sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock, const char *uri, const char *from_name, const char *from_uri, const char *event, const char *id, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, sip_auth_h *authh, void *aarg, bool aref, sipsub_fork_h *forkh, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...); int sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock, struct sip_dialog *dlg, const char *event, const char *id, uint32_t expires, const char *cuser, sip_auth_h *authh, void *aarg, bool aref, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...); int sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock, const char *uri, const char *from_name, const char *from_uri, const char *cuser, const char *routev[], uint32_t routec, sip_auth_h *authh, void *aarg, bool aref, sipsub_fork_h *forkh, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...); int sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock, struct sip_dialog *dlg, const char *cuser, sip_auth_h *authh, void *aarg, bool aref, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...); int sipevent_fork(struct sipsub **subp, struct sipsub *osub, const struct sip_msg *msg, sip_auth_h *authh, void *aarg, bool aref, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg); re-1.1.0/include/re_sipreg.h000066400000000000000000000015371373627245400157150ustar00rootroot00000000000000/** * @file re_sipreg.h SIP Registration * * Copyright (C) 2010 Creytiv.com */ struct sipreg; int sipreg_register(struct sipreg **regp, struct sip *sip, const char *reg_uri, const char *to_uri, const char *from_name, const char *from_uri, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, int regid, sip_auth_h *authh, void *aarg, bool aref, sip_resp_h *resph, void *arg, const char *params, const char *fmt, ...); int sipreg_set_rwait(struct sipreg *reg, uint32_t rwait); const struct sa *sipreg_laddr(const struct sipreg *reg); uint32_t sipreg_proxy_expires(const struct sipreg *reg); bool sipreg_registered(const struct sipreg *reg); bool sipreg_failed(const struct sipreg *reg); void sipreg_incfailc(struct sipreg *reg); int sipreg_set_fbregint(struct sipreg *reg, uint32_t fbregint); re-1.1.0/include/re_sipsess.h000066400000000000000000000051251373627245400161120ustar00rootroot00000000000000/** * @file re_sipsess.h SIP Session * * Copyright (C) 2010 Creytiv.com */ struct sipsess_sock; struct sipsess; typedef void (sipsess_conn_h)(const struct sip_msg *msg, void *arg); typedef int (sipsess_offer_h)(struct mbuf **descp, const struct sip_msg *msg, void *arg); typedef int (sipsess_answer_h)(const struct sip_msg *msg, void *arg); typedef void (sipsess_progr_h)(const struct sip_msg *msg, void *arg); typedef void (sipsess_estab_h)(const struct sip_msg *msg, void *arg); typedef void (sipsess_info_h)(struct sip *sip, const struct sip_msg *msg, void *arg); typedef void (sipsess_refer_h)(struct sip *sip, const struct sip_msg *msg, void *arg); typedef void (sipsess_close_h)(int err, const struct sip_msg *msg, void *arg); int sipsess_listen(struct sipsess_sock **sockp, struct sip *sip, int htsize, sipsess_conn_h *connh, void *arg); int sipsess_connect(struct sipsess **sessp, struct sipsess_sock *sock, const char *to_uri, const char *from_name, const char *from_uri, const char *cuser, const char *routev[], uint32_t routec, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_progr_h *progrh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg, const char *fmt, ...); int sipsess_accept(struct sipsess **sessp, struct sipsess_sock *sock, const struct sip_msg *msg, uint16_t scode, const char *reason, const char *cuser, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg, const char *fmt, ...); int sipsess_progress(struct sipsess *sess, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, ...); int sipsess_answer(struct sipsess *sess, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, ...); int sipsess_reject(struct sipsess *sess, uint16_t scode, const char *reason, const char *fmt, ...); int sipsess_modify(struct sipsess *sess, struct mbuf *desc); int sipsess_info(struct sipsess *sess, const char *ctype, struct mbuf *body, sip_resp_h *resph, void *arg); int sipsess_set_close_headers(struct sipsess *sess, const char *hdrs, ...); void sipsess_close_all(struct sipsess_sock *sock); struct sip_dialog *sipsess_dialog(const struct sipsess *sess); re-1.1.0/include/re_srtp.h000066400000000000000000000013551373627245400154120ustar00rootroot00000000000000/** * @file re_srtp.h Secure Real-time Transport Protocol (SRTP) * * Copyright (C) 2010 Creytiv.com */ enum srtp_suite { SRTP_AES_CM_128_HMAC_SHA1_32, SRTP_AES_CM_128_HMAC_SHA1_80, SRTP_AES_256_CM_HMAC_SHA1_32, SRTP_AES_256_CM_HMAC_SHA1_80, SRTP_AES_128_GCM, SRTP_AES_256_GCM, }; enum srtp_flags { SRTP_UNENCRYPTED_SRTCP = 1<<1, }; struct srtp; int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite, const uint8_t *key, size_t key_bytes, int flags); int srtp_encrypt(struct srtp *srtp, struct mbuf *mb); int srtp_decrypt(struct srtp *srtp, struct mbuf *mb); int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb); int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb); const char *srtp_suite_name(enum srtp_suite suite); re-1.1.0/include/re_stun.h000066400000000000000000000225111373627245400154100ustar00rootroot00000000000000/** * @file re_stun.h Session Traversal Utilities for (NAT) (STUN) * * Copyright (C) 2010 Creytiv.com */ /** STUN Protocol values */ enum { STUN_PORT = 3478, /**< STUN Port number */ STUNS_PORT = 5349, /**< STUNS Port number */ STUN_HEADER_SIZE = 20, /**< Number of bytes in header */ STUN_ATTR_HEADER_SIZE = 4, /**< Size of attribute header */ STUN_TID_SIZE = 12, /**< Number of bytes in transaction ID */ STUN_DEFAULT_RTO = 500, /**< Default Retrans Timeout in [ms] */ STUN_DEFAULT_RC = 7, /**< Default number of retransmits */ STUN_DEFAULT_RM = 16, /**< Wait time after last request is sent */ STUN_DEFAULT_TI = 39500 /**< Reliable timeout */ }; /** STUN Address Family */ enum stun_af { STUN_AF_IPv4 = 0x01, /**< IPv4 Address Family */ STUN_AF_IPv6 = 0x02 /**< IPv6 Address Family */ }; /** STUN Transport */ enum stun_transp { STUN_TRANSP_UDP = IPPROTO_UDP, /**< UDP-transport (struct udp_sock) */ STUN_TRANSP_TCP = IPPROTO_TCP, /**< TCP-transport (struct tcp_conn) */ STUN_TRANSP_DTLS, /**< DTLS-transport (struct tls_conn) */ }; /** STUN Methods */ enum stun_method { STUN_METHOD_BINDING = 0x001, STUN_METHOD_ALLOCATE = 0x003, STUN_METHOD_REFRESH = 0x004, STUN_METHOD_SEND = 0x006, STUN_METHOD_DATA = 0x007, STUN_METHOD_CREATEPERM = 0x008, STUN_METHOD_CHANBIND = 0x009, }; /** STUN Message class */ enum stun_msg_class { STUN_CLASS_REQUEST = 0x0, /**< STUN Request */ STUN_CLASS_INDICATION = 0x1, /**< STUN Indication */ STUN_CLASS_SUCCESS_RESP = 0x2, /**< STUN Success Response */ STUN_CLASS_ERROR_RESP = 0x3 /**< STUN Error Response */ }; /** STUN Attributes */ enum stun_attrib { /* Comprehension-required range (0x0000-0x7FFF) */ STUN_ATTR_MAPPED_ADDR = 0x0001, STUN_ATTR_CHANGE_REQ = 0x0003, STUN_ATTR_USERNAME = 0x0006, STUN_ATTR_MSG_INTEGRITY = 0x0008, STUN_ATTR_ERR_CODE = 0x0009, STUN_ATTR_UNKNOWN_ATTR = 0x000a, STUN_ATTR_CHANNEL_NUMBER = 0x000c, STUN_ATTR_LIFETIME = 0x000d, STUN_ATTR_XOR_PEER_ADDR = 0x0012, STUN_ATTR_DATA = 0x0013, STUN_ATTR_REALM = 0x0014, STUN_ATTR_NONCE = 0x0015, STUN_ATTR_XOR_RELAY_ADDR = 0x0016, STUN_ATTR_REQ_ADDR_FAMILY = 0x0017, STUN_ATTR_EVEN_PORT = 0x0018, STUN_ATTR_REQ_TRANSPORT = 0x0019, STUN_ATTR_DONT_FRAGMENT = 0x001a, STUN_ATTR_XOR_MAPPED_ADDR = 0x0020, STUN_ATTR_RSV_TOKEN = 0x0022, STUN_ATTR_PRIORITY = 0x0024, STUN_ATTR_USE_CAND = 0x0025, STUN_ATTR_PADDING = 0x0026, STUN_ATTR_RESP_PORT = 0x0027, /* Comprehension-optional range (0x8000-0xFFFF) */ STUN_ATTR_SOFTWARE = 0x8022, STUN_ATTR_ALT_SERVER = 0x8023, STUN_ATTR_FINGERPRINT = 0x8028, STUN_ATTR_CONTROLLED = 0x8029, STUN_ATTR_CONTROLLING = 0x802a, STUN_ATTR_RESP_ORIGIN = 0x802b, STUN_ATTR_OTHER_ADDR = 0x802c, }; struct stun_change_req { bool ip; bool port; }; struct stun_errcode { uint16_t code; char *reason; }; struct stun_unknown_attr { uint16_t typev[8]; uint32_t typec; }; struct stun_even_port { bool r; }; /** Defines a STUN attribute */ struct stun_attr { struct le le; uint16_t type; union { /* generic types */ struct sa sa; char *str; uint64_t uint64; uint32_t uint32; uint16_t uint16; uint8_t uint8; struct mbuf mb; /* actual attributes */ struct sa mapped_addr; struct stun_change_req change_req; char *username; uint8_t msg_integrity[20]; struct stun_errcode err_code; struct stun_unknown_attr unknown_attr; uint16_t channel_number; uint32_t lifetime; struct sa xor_peer_addr; struct mbuf data; char *realm; char *nonce; struct sa xor_relay_addr; uint8_t req_addr_family; struct stun_even_port even_port; uint8_t req_transport; struct sa xor_mapped_addr; uint64_t rsv_token; uint32_t priority; struct mbuf padding; uint16_t resp_port; char *software; struct sa alt_server; uint32_t fingerprint; uint64_t controlled; uint64_t controlling; struct sa resp_origin; struct sa other_addr; } v; }; /** STUN Configuration */ struct stun_conf { uint32_t rto; /**< RTO Retransmission TimeOut [ms] */ uint32_t rc; /**< Rc Retransmission count (default 7) */ uint32_t rm; /**< Rm Max retransmissions (default 16) */ uint32_t ti; /**< Ti Timeout for reliable transport [ms] */ uint8_t tos; /**< Type-of-service field */ }; extern const char *stun_software; struct stun; struct stun_msg; struct stun_ctrans; typedef void(stun_resp_h)(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg); typedef void(stun_ind_h)(struct stun_msg *msg, void *arg); typedef bool(stun_attr_h)(const struct stun_attr *attr, void *arg); int stun_alloc(struct stun **stunp, const struct stun_conf *conf, stun_ind_h *indh, void *arg); struct stun_conf *stun_conf(struct stun *stun); int stun_send(int proto, void *sock, const struct sa *dst, struct mbuf *mb); int stun_recv(struct stun *stun, struct mbuf *mb); int stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg, const struct stun_unknown_attr *ua); struct re_printf; int stun_debug(struct re_printf *pf, const struct stun *stun); int stun_request(struct stun_ctrans **ctp, struct stun *stun, int proto, void *sock, const struct sa *dst, size_t presz, uint16_t method, const uint8_t *key, size_t keylen, bool fp, stun_resp_h *resph, void *arg, uint32_t attrc, ...); int stun_reply(int proto, void *sock, const struct sa *dst, size_t presz, const struct stun_msg *req, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...); int stun_ereply(int proto, void *sock, const struct sa *dst, size_t presz, const struct stun_msg *req, uint16_t scode, const char *reason, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...); int stun_indication(int proto, void *sock, const struct sa *dst, size_t presz, uint16_t method, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...); int stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t cls, const uint8_t *tid, const struct stun_errcode *ec, const uint8_t *key, size_t keylen, bool fp, uint8_t padding, uint32_t attrc, va_list ap); int stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t cls, const uint8_t *tid, const struct stun_errcode *ec, const uint8_t *key, size_t keylen, bool fp, uint8_t padding, uint32_t attrc, ...); int stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb, struct stun_unknown_attr *ua); uint16_t stun_msg_type(const struct stun_msg *msg); uint16_t stun_msg_class(const struct stun_msg *msg); uint16_t stun_msg_method(const struct stun_msg *msg); bool stun_msg_mcookie(const struct stun_msg *msg); const uint8_t *stun_msg_tid(const struct stun_msg *msg); struct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type); struct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg, stun_attr_h *h, void *arg); int stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key, size_t keylen); int stun_msg_chk_fingerprint(const struct stun_msg *msg); void stun_msg_dump(const struct stun_msg *msg); const char *stun_class_name(uint16_t cls); const char *stun_method_name(uint16_t method); const char *stun_attr_name(uint16_t type); const char *stun_transp_name(enum stun_transp tp); /* DNS Discovery of a STUN Server */ extern const char *stun_proto_udp; extern const char *stun_proto_tcp; extern const char *stun_usage_binding; extern const char *stuns_usage_binding; extern const char *stun_usage_relay; extern const char *stuns_usage_relay; extern const char *stun_usage_behavior; extern const char *stuns_usage_behavior; /** * Defines the STUN Server Discovery handler * * @param err Errorcode * @param srv IP Address and port of STUN Server * @param arg Handler argument */ typedef void (stun_dns_h)(int err, const struct sa *srv, void *arg); struct stun_dns; struct dnsc; int stun_server_discover(struct stun_dns **dnsp, struct dnsc *dnsc, const char *service, const char *proto, int af, const char *domain, uint16_t port, stun_dns_h *dnsh, void *arg); /* NAT Keepalives */ struct stun_keepalive; /** * Defines the STUN Keepalive Mapped-Address handler * * @param err Errorcode * @param map Mapped Address * @param arg Handler argument */ typedef void (stun_mapped_addr_h)(int err, const struct sa *map, void *arg); int stun_keepalive_alloc(struct stun_keepalive **skap, int proto, void *sock, int layer, const struct sa *dst, const struct stun_conf *conf, stun_mapped_addr_h *mah, void *arg); void stun_keepalive_enable(struct stun_keepalive *ska, uint32_t interval); /* STUN Reason Phrase */ extern const char *stun_reason_300; extern const char *stun_reason_400; extern const char *stun_reason_401; extern const char *stun_reason_403; extern const char *stun_reason_420; extern const char *stun_reason_437; extern const char *stun_reason_438; extern const char *stun_reason_440; extern const char *stun_reason_441; extern const char *stun_reason_442; extern const char *stun_reason_443; extern const char *stun_reason_486; extern const char *stun_reason_500; extern const char *stun_reason_508; re-1.1.0/include/re_sys.h000066400000000000000000000025531373627245400152410ustar00rootroot00000000000000/** * @file re_sys.h Interface to system module * * Copyright (C) 2010 Creytiv.com */ #ifndef VERSION #define VERSION "?" #endif /** * @def ARCH * * Architecture */ #ifndef ARCH #define ARCH "?" #endif /** * @def OS * * Operating System */ #ifndef OS #ifdef WIN32 #define OS "win32" #else #define OS "?" #endif #endif struct re_printf; int sys_rel_get(uint32_t *rel, uint32_t *maj, uint32_t *min, uint32_t *patch); int sys_kernel_get(struct re_printf *pf, void *unused); int sys_build_get(struct re_printf *pf, void *unused); const char *sys_arch_get(void); const char *sys_os_get(void); const char *sys_libre_version_get(void); const char *sys_username(void); int sys_coredump_set(bool enable); int sys_daemon(void); void sys_usleep(unsigned int us); static inline void sys_msleep(unsigned int ms) { sys_usleep(ms * 1000); } uint16_t sys_htols(uint16_t v); uint32_t sys_htoll(uint32_t v); uint16_t sys_ltohs(uint16_t v); uint32_t sys_ltohl(uint32_t v); uint64_t sys_htonll(uint64_t v); uint64_t sys_ntohll(uint64_t v); /* Random */ void rand_init(void); uint16_t rand_u16(void); uint32_t rand_u32(void); uint64_t rand_u64(void); char rand_char(void); void rand_str(char *str, size_t size); void rand_bytes(uint8_t *p, size_t size); /* File-System */ int fs_mkdir(const char *path, uint16_t mode); int fs_gethome(char *path, size_t sz); re-1.1.0/include/re_tcp.h000066400000000000000000000064121373627245400152070ustar00rootroot00000000000000/** * @file re_tcp.h Interface to Transport Control Protocol * * Copyright (C) 2010 Creytiv.com */ struct sa; struct tcp_sock; struct tcp_conn; /** * Defines the incoming TCP connection handler * * @param peer Network address of peer * @param arg Handler argument */ typedef void (tcp_conn_h)(const struct sa *peer, void *arg); /** * Defines the TCP connection established handler * * @param arg Handler argument */ typedef void (tcp_estab_h)(void *arg); /** * Defines the TCP connection data send handler * * @param arg Handler argument */ typedef void (tcp_send_h)(void *arg); /** * Defines the TCP connection data receive handler * * @param mb Buffer with data * @param arg Handler argument */ typedef void (tcp_recv_h)(struct mbuf *mb, void *arg); /** * Defines the TCP connection close handler * * @param err Error code * @param arg Handler argument */ typedef void (tcp_close_h)(int err, void *arg); /* TCP Socket */ int tcp_sock_alloc(struct tcp_sock **tsp, const struct sa *local, tcp_conn_h *ch, void *arg); struct tcp_sock *tcp_sock_dup(struct tcp_sock *tso); int tcp_sock_bind(struct tcp_sock *ts, const struct sa *local); int tcp_sock_listen(struct tcp_sock *ts, int backlog); int tcp_accept(struct tcp_conn **tcp, struct tcp_sock *ts, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg); void tcp_reject(struct tcp_sock *ts); int tcp_sock_local_get(const struct tcp_sock *ts, struct sa *local); /* TCP Connection */ int tcp_conn_alloc(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg); int tcp_conn_bind(struct tcp_conn *tc, const struct sa *local); int tcp_conn_connect(struct tcp_conn *tc, const struct sa *peer); int tcp_send(struct tcp_conn *tc, struct mbuf *mb); int tcp_set_send(struct tcp_conn *tc, tcp_send_h *sendh); void tcp_set_handlers(struct tcp_conn *tc, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg); void tcp_conn_rxsz_set(struct tcp_conn *tc, size_t rxsz); void tcp_conn_txqsz_set(struct tcp_conn *tc, size_t txqsz); int tcp_conn_local_get(const struct tcp_conn *tc, struct sa *local); int tcp_conn_peer_get(const struct tcp_conn *tc, struct sa *peer); int tcp_conn_fd(const struct tcp_conn *tc); size_t tcp_conn_txqsz(const struct tcp_conn *tc); /* High-level API */ int tcp_listen(struct tcp_sock **tsp, const struct sa *local, tcp_conn_h *ch, void *arg); int tcp_connect(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg); int tcp_connect_bind(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, const struct sa *local, void *arg); int tcp_local_get(const struct tcp_sock *ts, struct sa *local); /* Helper API */ typedef bool (tcp_helper_estab_h)(int *err, bool active, void *arg); typedef bool (tcp_helper_send_h)(int *err, struct mbuf *mb, void *arg); typedef bool (tcp_helper_recv_h)(int *err, struct mbuf *mb, bool *estab, void *arg); struct tcp_helper; int tcp_register_helper(struct tcp_helper **thp, struct tcp_conn *tc, int layer, tcp_helper_estab_h *eh, tcp_helper_send_h *sh, tcp_helper_recv_h *rh, void *arg); int tcp_send_helper(struct tcp_conn *tc, struct mbuf *mb, struct tcp_helper *th); re-1.1.0/include/re_telev.h000066400000000000000000000011031373627245400155300ustar00rootroot00000000000000/** * @file re_telev.h Interface to Telephony Events (RFC 4733) * * Copyright (C) 2010 Creytiv.com */ enum { TELEV_PTIME = 50, TELEV_SRATE = 8000 }; struct telev; extern const char telev_rtpfmt[]; int telev_alloc(struct telev **tp, uint32_t ptime); int telev_set_srate(struct telev *tel, uint32_t srate); int telev_send(struct telev *tel, int event, bool end); int telev_recv(struct telev *tel, struct mbuf *mb, int *event, bool *end); int telev_poll(struct telev *tel, bool *marker, struct mbuf *mb); int telev_digit2code(int digit); int telev_code2digit(int code); re-1.1.0/include/re_tls.h000066400000000000000000000073041373627245400152240ustar00rootroot00000000000000/** * @file re_tls.h Interface to Transport Layer Security * * Copyright (C) 2010 Creytiv.com */ struct tls; struct tls_conn; struct tcp_conn; struct udp_sock; /** Defines the TLS method */ enum tls_method { TLS_METHOD_SSLV23, TLS_METHOD_DTLSV1, TLS_METHOD_DTLS, /* DTLS 1.0 and 1.2 */ TLS_METHOD_DTLSV1_2, /* DTLS 1.2 */ }; enum tls_fingerprint { TLS_FINGERPRINT_SHA1, TLS_FINGERPRINT_SHA256, }; enum tls_keytype { TLS_KEYTYPE_RSA, TLS_KEYTYPE_EC, }; int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, const char *pwd); int tls_add_ca(struct tls *tls, const char *cafile); int tls_set_selfsigned(struct tls *tls, const char *cn); int tls_set_selfsigned_rsa(struct tls *tls, const char *cn, size_t bits); int tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert, const char *key, size_t len_key); int tls_set_certificate_der(struct tls *tls, enum tls_keytype keytype, const uint8_t *cert, size_t len_cert, const uint8_t *key, size_t len_key); int tls_set_certificate(struct tls *tls, const char *cert, size_t len); void tls_set_verify_client(struct tls *tls); int tls_set_srtp(struct tls *tls, const char *suites); int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type, uint8_t *md, size_t size); int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type, uint8_t *md, size_t size); int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size); int tls_peer_set_verify_host(struct tls_conn *tc, const char *hostname); int tls_set_verify_purpose(struct tls *tls, const char *purpose); int tls_set_hostname(char *tls_hostname, const struct pl *hostname); int tls_peer_verify(const struct tls_conn *tc); int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite, uint8_t *cli_key, size_t cli_key_size, uint8_t *srv_key, size_t srv_key_size); const char *tls_cipher_name(const struct tls_conn *tc); int tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count); int tls_set_servername(struct tls_conn *tc, const char *servername); int tls_set_verify_server(struct tls_conn *tc, const char *host); int tls_get_issuer(struct tls *tls, struct mbuf *mb); int tls_get_subject(struct tls *tls, struct mbuf *mb); /* TCP */ int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp, int layer); /* UDP (DTLS) */ typedef void (dtls_conn_h)(const struct sa *peer, void *arg); typedef void (dtls_estab_h)(void *arg); typedef void (dtls_recv_h)(struct mbuf *mb, void *arg); typedef void (dtls_close_h)(int err, void *arg); struct dtls_sock; int dtls_listen(struct dtls_sock **sockp, const struct sa *laddr, struct udp_sock *us, uint32_t htsize, int layer, dtls_conn_h *connh, void *arg); struct udp_sock *dtls_udp_sock(struct dtls_sock *sock); void dtls_set_mtu(struct dtls_sock *sock, size_t mtu); int dtls_connect(struct tls_conn **ptc, struct tls *tls, struct dtls_sock *sock, const struct sa *peer, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg); int dtls_accept(struct tls_conn **ptc, struct tls *tls, struct dtls_sock *sock, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg); int dtls_send(struct tls_conn *tc, struct mbuf *mb); void dtls_set_handlers(struct tls_conn *tc, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg); const struct sa *dtls_peer(const struct tls_conn *tc); void dtls_set_peer(struct tls_conn *tc, const struct sa *peer); void dtls_recv_packet(struct dtls_sock *sock, const struct sa *src, struct mbuf *mb); #ifdef USE_OPENSSL struct ssl_ctx_st; struct ssl_ctx_st *tls_openssl_context(const struct tls *tls); #endif re-1.1.0/include/re_tmr.h000066400000000000000000000020471373627245400152230ustar00rootroot00000000000000/** * @file re_tmr.h Interface to timer implementation * * Copyright (C) 2010 Creytiv.com */ /** * Defines the timeout handler * * @param arg Handler argument */ typedef void (tmr_h)(void *arg); /** Defines a timer */ struct tmr { struct le le; /**< Linked list element */ tmr_h *th; /**< Timeout handler */ void *arg; /**< Handler argument */ uint64_t jfs; /**< Jiffies for timeout */ }; void tmr_poll(struct list *tmrl); uint64_t tmr_jiffies(void); uint64_t tmr_next_timeout(struct list *tmrl); void tmr_debug(void); int tmr_status(struct re_printf *pf, void *unused); void tmr_init(struct tmr *tmr); void tmr_start(struct tmr *tmr, uint64_t delay, tmr_h *th, void *arg); void tmr_cancel(struct tmr *tmr); uint64_t tmr_get_expire(const struct tmr *tmr); /** * Check if the timer is running * * @param tmr Timer to check * * @return true if running, false if not running */ static inline bool tmr_isrunning(const struct tmr *tmr) { return tmr ? NULL != tmr->th : false; } re-1.1.0/include/re_turn.h000066400000000000000000000021541373627245400154100ustar00rootroot00000000000000/** * @file re_turn.h Interface to TURN implementation * * Copyright (C) 2010 Creytiv.com */ /** TURN Protocol values */ enum { TURN_DEFAULT_LIFETIME = 600, /**< Default lifetime is 10 minutes */ TURN_MAX_LIFETIME = 3600 /**< Maximum lifetime is 1 hour */ }; typedef void(turnc_h)(int err, uint16_t scode, const char *reason, const struct sa *relay_addr, const struct sa *mapped_addr, const struct stun_msg *msg, void *arg); typedef void(turnc_perm_h)(void *arg); typedef void(turnc_chan_h)(void *arg); struct turnc; int turnc_alloc(struct turnc **turncp, const struct stun_conf *conf, int proto, void *sock, int layer, const struct sa *srv, const char *username, const char *password, uint32_t lifetime, turnc_h *th, void *arg); int turnc_send(struct turnc *turnc, const struct sa *dst, struct mbuf *mb); int turnc_recv(struct turnc *turnc, struct sa *src, struct mbuf *mb); int turnc_add_perm(struct turnc *turnc, const struct sa *peer, turnc_perm_h *ph, void *arg); int turnc_add_chan(struct turnc *turnc, const struct sa *peer, turnc_chan_h *ch, void *arg); re-1.1.0/include/re_types.h000066400000000000000000000107541373627245400155710ustar00rootroot00000000000000/** * @file re_types.h Defines basic types * * Copyright (C) 2010 Creytiv.com */ #include #ifdef _MSC_VER #include #endif /* * Basic integral types from C99 */ #ifdef HAVE_INTTYPES_H #include #else #ifndef __int8_t_defined #define __int8_t_defined /* Hack for OpenBSD */ #ifndef __BIT_TYPES_DEFINED__ #if defined(_CHAR_IS_SIGNED) typedef char int8_t; #elif defined(__STDC__) typedef signed char int8_t; #else typedef char int8_t; #endif typedef signed short int int16_t; typedef signed int int32_t; typedef signed long long int int64_t; #ifndef __uint32_t_defined #define __uint32_t_defined typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef unsigned long long int uint64_t; #endif #endif /* __BIT_TYPES_DEFINED__ */ #endif /* __int8_t_defined */ #ifndef __ssize_t_defined typedef long ssize_t; #define __ssize_t_defined #endif #ifndef WIN32 typedef uint32_t socklen_t; #endif #endif /* * Hack for Solaris which does not define int64_t/uint64_t for strict ANSI C */ #ifdef SOLARIS #if !(__STDC__ - 0 == 0 && !defined(_NO_LONGLONG)) typedef signed long long int int64_t; typedef unsigned long long int uint64_t; #endif #endif /* * Boolean type * see http://www.opengroup.org/onlinepubs/000095399/basedefs/stdbool.h.html * www.gnu.org/software/autoconf/manual/html_node/Particular-Headers.html */ #ifdef HAVE_STDBOOL_H # include #else # ifndef HAVE__BOOL # ifdef __cplusplus typedef bool _Bool; # else # define _Bool signed char # endif # endif # define bool _Bool # define false 0 # define true 1 # define __bool_true_false_are_defined 1 #endif /* Needed for MS compiler */ #ifdef _MSC_VER #ifndef __cplusplus #define inline _inline #endif #endif /* * Misc macros */ /** Defines the NULL pointer */ #ifndef NULL #define NULL ((void *)0) #endif /** Get number of elements in an array */ #undef ARRAY_SIZE #define ARRAY_SIZE(a) ((sizeof(a))/(sizeof((a)[0]))) /** Align a value to the boundary of mask */ #define ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) /** Get the minimal value */ #undef MIN #define MIN(a,b) (((a)<(b)) ? (a) : (b)) /** Get the maximal value */ #undef MAX #define MAX(a,b) (((a)>(b)) ? (a) : (b)) #ifndef __cplusplus /** Get the minimal value */ #undef min #define min(x,y) MIN(x, y) /** Get the maximal value */ #undef max #define max(x,y) MAX(x, y) #endif /** Defines a soft breakpoint */ #if (defined(__i386__) || defined(__x86_64__)) #define BREAKPOINT __asm__("int $0x03") #else #define BREAKPOINT #endif /* Error codes */ #include /* Duplication of error codes. Values are from linux asm-generic/errno.h */ /** No data available */ #ifndef ENODATA #define ENODATA 200 #endif /** Protocol error */ #ifndef EPROTO #define EPROTO 201 #endif /** Not a data message */ #ifndef EBADMSG #define EBADMSG 202 #endif /** Value too large for defined data type */ #ifndef EOVERFLOW #define EOVERFLOW 203 #endif /** Accessing a corrupted shared library */ #ifndef ELIBBAD #define ELIBBAD 204 #endif /** Destination address required */ #ifndef EDESTADDRREQ #define EDESTADDRREQ 205 #endif /** Protocol not supported */ #ifndef EPROTONOSUPPORT #define EPROTONOSUPPORT 206 #endif /** Operation not supported */ #ifndef ENOTSUP #define ENOTSUP 207 #endif /** Address family not supported by protocol */ #ifndef EAFNOSUPPORT #define EAFNOSUPPORT 208 #endif /** Cannot assign requested address */ #ifndef EADDRNOTAVAIL #define EADDRNOTAVAIL 209 #endif /** Software caused connection abort */ #ifndef ECONNABORTED #define ECONNABORTED 210 #endif /** Connection reset by peer */ #ifndef ECONNRESET #define ECONNRESET 211 #endif /** Transport endpoint is not connected */ #ifndef ENOTCONN #define ENOTCONN 212 #endif /** Connection timed out */ #ifndef ETIMEDOUT #define ETIMEDOUT 213 #endif /** Connection refused */ #ifndef ECONNREFUSED #define ECONNREFUSED 214 #endif /** Operation already in progress */ #ifndef EALREADY #define EALREADY 215 #endif /** Operation now in progress */ #ifndef EINPROGRESS #define EINPROGRESS 216 #endif /** Authentication error */ #ifndef EAUTH #define EAUTH 217 #endif /** No STREAM resources */ #ifndef ENOSR #define ENOSR 218 #endif /* * Any C compiler conforming to C99 or later MUST support __func__ */ #if __STDC_VERSION__ >= 199901L #define __REFUNC__ (const char *)__func__ #else #define __REFUNC__ __FUNCTION__ #endif re-1.1.0/include/re_udp.h000066400000000000000000000037161373627245400152150ustar00rootroot00000000000000/** * @file re_udp.h Interface to User Datagram Protocol * * Copyright (C) 2010 Creytiv.com */ struct sa; struct udp_sock; /** * Defines the UDP Receive handler * * @param src Source address * @param mb Datagram buffer * @param arg Handler argument */ typedef void (udp_recv_h)(const struct sa *src, struct mbuf *mb, void *arg); typedef void (udp_error_h)(int err, void *arg); int udp_listen(struct udp_sock **usp, const struct sa *local, udp_recv_h *rh, void *arg); int udp_connect(struct udp_sock *us, const struct sa *peer); int udp_send(struct udp_sock *us, const struct sa *dst, struct mbuf *mb); int udp_send_anon(const struct sa *dst, struct mbuf *mb); int udp_local_get(const struct udp_sock *us, struct sa *local); int udp_setsockopt(struct udp_sock *us, int level, int optname, const void *optval, uint32_t optlen); int udp_sockbuf_set(struct udp_sock *us, int size); void udp_rxsz_set(struct udp_sock *us, size_t rxsz); void udp_rxbuf_presz_set(struct udp_sock *us, size_t rx_presz); void udp_handler_set(struct udp_sock *us, udp_recv_h *rh, void *arg); void udp_error_handler_set(struct udp_sock *us, udp_error_h *eh); int udp_thread_attach(struct udp_sock *us); void udp_thread_detach(struct udp_sock *us); int udp_sock_fd(const struct udp_sock *us, int af); int udp_multicast_join(struct udp_sock *us, const struct sa *group); int udp_multicast_leave(struct udp_sock *us, const struct sa *group); /* Helper API */ typedef bool (udp_helper_send_h)(int *err, struct sa *dst, struct mbuf *mb, void *arg); typedef bool (udp_helper_recv_h)(struct sa *src, struct mbuf *mb, void *arg); struct udp_helper; int udp_register_helper(struct udp_helper **uhp, struct udp_sock *us, int layer, udp_helper_send_h *sh, udp_helper_recv_h *rh, void *arg); int udp_send_helper(struct udp_sock *us, const struct sa *dst, struct mbuf *mb, struct udp_helper *uh); struct udp_helper *udp_helper_find(const struct udp_sock *us, int layer); re-1.1.0/include/re_uri.h000066400000000000000000000036341373627245400152230ustar00rootroot00000000000000/** * @file re_uri.h Interface to URI module * * Copyright (C) 2010 Creytiv.com */ /** Defines a URI - Uniform Resource Identifier */ struct uri { struct pl scheme; /**< URI scheme e.g. "sip:" "sips:" */ struct pl user; /**< Username */ struct pl password; /**< Optional password */ struct pl host; /**< Hostname or IP-address */ int af; /**< Address family of host IP-address */ uint16_t port; /**< Port number */ struct pl path; /**< Optional URI-path */ struct pl params; /**< Optional URI-parameters */ struct pl headers; /**< Optional URI-headers */ }; typedef int (uri_apply_h)(const struct pl *name, const struct pl *val, void *arg); struct re_printf; int uri_encode(struct re_printf *pf, const struct uri *uri); int uri_decode(struct uri *uri, const struct pl *pl); int uri_decode_hostport(const struct pl *hostport, struct pl *host, struct pl *port); int uri_param_get(const struct pl *pl, const struct pl *pname, struct pl *pvalue); int uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg); int uri_header_get(const struct pl *pl, const struct pl *hname, struct pl *hvalue); int uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg); /* Special URI escaping/unescaping */ int uri_user_escape(struct re_printf *pf, const struct pl *pl); int uri_user_unescape(struct re_printf *pf, const struct pl *pl); int uri_password_escape(struct re_printf *pf, const struct pl *pl); int uri_password_unescape(struct re_printf *pf, const struct pl *pl); int uri_param_escape(struct re_printf *pf, const struct pl *pl); int uri_param_unescape(struct re_printf *pf, const struct pl *pl); int uri_header_escape(struct re_printf *pf, const struct pl *pl); int uri_header_unescape(struct re_printf *pf, const struct pl *pl); re-1.1.0/include/re_websock.h000066400000000000000000000037611373627245400160620ustar00rootroot00000000000000/** * @file re_websock.h The WebSocket Protocol * * Copyright (C) 2010 Creytiv.com */ enum { WEBSOCK_VERSION = 13, }; enum websock_opcode { /* Data frames */ WEBSOCK_CONT = 0x0, WEBSOCK_TEXT = 0x1, WEBSOCK_BIN = 0x2, /* Control frames */ WEBSOCK_CLOSE = 0x8, WEBSOCK_PING = 0x9, WEBSOCK_PONG = 0xa, }; enum websock_scode { WEBSOCK_NORMAL_CLOSURE = 1000, WEBSOCK_GOING_AWAY = 1001, WEBSOCK_PROTOCOL_ERROR = 1002, WEBSOCK_UNSUPPORTED_DATA = 1003, WEBSOCK_INVALID_PAYLOAD = 1007, WEBSOCK_POLICY_VIOLATION = 1008, WEBSOCK_MESSAGE_TOO_BIG = 1009, WEBSOCK_EXTENSION_ERROR = 1010, WEBSOCK_INTERNAL_ERROR = 1011, }; struct websock_hdr { unsigned fin:1; unsigned rsv1:1; unsigned rsv2:1; unsigned rsv3:1; unsigned opcode:4; unsigned mask:1; uint64_t len; uint8_t mkey[4]; }; struct websock; struct websock_conn; typedef void (websock_estab_h)(void *arg); typedef void (websock_recv_h)(const struct websock_hdr *hdr, struct mbuf *mb, void *arg); typedef void (websock_close_h)(int err, void *arg); int websock_connect(struct websock_conn **connp, struct websock *sock, struct http_cli *cli, const char *uri, unsigned kaint, websock_estab_h *estabh, websock_recv_h *recvh, websock_close_h *closeh, void *arg, const char *fmt, ...); int websock_accept(struct websock_conn **connp, struct websock *sock, struct http_conn *htconn, const struct http_msg *msg, unsigned kaint, websock_recv_h *recvh, websock_close_h *closeh, void *arg); int websock_send(struct websock_conn *conn, enum websock_opcode opcode, const char *fmt, ...); int websock_close(struct websock_conn *conn, enum websock_scode scode, const char *fmt, ...); const struct sa *websock_peer(const struct websock_conn *conn); struct tcp_conn *websock_tcp(const struct websock_conn *conn); typedef void (websock_shutdown_h)(void *arg); int websock_alloc(struct websock **sockp, websock_shutdown_h *shuth, void *arg); void websock_shutdown(struct websock *sock); re-1.1.0/mk/000077500000000000000000000000001373627245400125435ustar00rootroot00000000000000re-1.1.0/mk/CMakeLists.txt000066400000000000000000000125631373627245400153120ustar00rootroot00000000000000cmake_minimum_required(VERSION 2.8.2) project(re) set(CMAKE_VERBOSE_MAKEFILE ON) SET(RE_ROOT ${PROJECT_SOURCE_DIR}/..) SET(RE_SRC_PREFIX ../src) # include header files for the IDEs file(GLOB_RECURSE HEADER_FILES src/*.h include/*.h) if(UNIX) # get make db and extract information via regex's execute_process(COMMAND make --no-print-directory --just-print --print-data-base info OUTPUT_VARIABLE RE_MAKEDB OUTPUT_STRIP_TRAILING_WHITESPACE WORKING_DIRECTORY ${RE_ROOT}) # get list of source files in SRCS = ... STRING(REGEX MATCH "[\n\r]SRCS = ([^\n\r]+)" RE_SRCS "${RE_MAKEDB}") STRING(REGEX REPLACE "[\n\r]SRCS = " "" RE_SRCS "${RE_SRCS}") # convert into list and prefix every element with 'src/' SET(RE_SRCS "${RE_SRC_PREFIX}/${RE_SRCS}") STRING(REPLACE " " ";${RE_SRC_PREFIX}/" RE_SRCS ${RE_SRCS}) # get CFLAGS STRING(REGEX MATCH "[\n\r]CFLAGS = ([^\n\r]+)" RE_CFLAGS "${RE_MAKEDB}") STRING(REGEX REPLACE "[\n\r]CFLAGS = " "" RE_CFLAGS "${RE_CFLAGS}") # remove anything that is not a macro define STRING(REGEX REPLACE "-[^D][^ ]*" "" RE_CFLAGS "${RE_CFLAGS}" ) # get EXTRA_CFLAGS STRING(REGEX MATCH "[\n\r]EXTRA_CFLAGS := ([^\n\r]+)" RE_EXTRA_CFLAGS "${RE_MAKEDB}") STRING(REGEX REPLACE "[\n\r]EXTRA_CFLAGS := " "" RE_EXTRA_CFLAGS "${RE_EXTRA_CFLAGS}") if (RE_EXTRA_CFLAGS) # not tested STRING(REGEX REPLACE "\\$\\(EXTRA_CFLAGS\\)" ${RE_EXTRA_CFLAGS} RE_CFLAGS "${RE_CFLAGS}") else() STRING(REGEX REPLACE "\\$\\(EXTRA_CFLAGS\\)" "" RE_CFLAGS "${RE_CFLAGS}") endif() # get OS STRING(REGEX MATCH "[\n\r]OS := ([^\n\r]+)" RE_OS "${RE_MAKEDB}") STRING(REGEX REPLACE "[\n\r]OS := " "" RE_OS "${RE_OS}") STRING(REGEX REPLACE "\\$\\(OS\\)" ${RE_OS} RE_CFLAGS "${RE_CFLAGS}") # get VERSION STRING(REGEX MATCH "[\n\r]VERSION := ([^\n\r]+)" RE_VERSION "${RE_MAKEDB}") STRING(REGEX REPLACE "[\n\r]VERSION := " "" RE_VERSION "${RE_VERSION}") STRING(REGEX REPLACE "\\$\\(VERSION\\)" ${RE_VERSION} RE_CFLAGS "${RE_CFLAGS}") # get ARCH STRING(REGEX MATCH "[\n\r]ARCH := ([^\n\r]+)" RE_ARCH "${RE_MAKEDB}") STRING(REGEX REPLACE "[\n\r]ARCH := " "" RE_ARCH "${RE_ARCH}") STRING(REGEX REPLACE "\\$\\(ARCH\\)" ${RE_ARCH} RE_CFLAGS "${RE_CFLAGS}") # escaping '\': makefiles do need it, but it breaks xcode - not sure who's "right" if (CMAKE_GENERATOR MATCHES Xcode) STRING(REGEX REPLACE "\\\\" "" RE_CFLAGS "${RE_CFLAGS}" ) endif() if (APPLE) LIST(APPEND RE_SRCS "${RE_SRC_PREFIX}/lock/rwlock.c") # get MacOSX version execute_process(COMMAND /usr/bin/sw_vers -productVersion OUTPUT_VARIABLE MACOSX_VERSION ERROR_VARIABLE MACOSX_VERSION_errors RESULT_VARIABLE MACOSX_VERSION_result OUTPUT_STRIP_TRAILING_WHITESPACE) # build universal binaries set(CMAKE_OSX_ARCHITECTURES "x86_64;i386") if (${MACOSX_VERSION} VERSION_LESS 10.9) foreach(FLAGS CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) set(${FLAGS} "${${FLAGS}} -mmacosx-version-min=10.6 -stdlib=libstdc++") endforeach() else() foreach(FLAGS CMAKE_C_FLAGS CMAKE_CXX_FLAGS CMAKE_EXE_LINKER_FLAGS CMAKE_SHARED_LINKER_FLAGS CMAKE_MODULE_LINKER_FLAGS) set(${FLAGS} "${${FLAGS}} -mmacosx-version-min=10.7 -stdlib=libc++") endforeach() endif() endif() message("libre sources: ${RE_SRCS}") message("libre cflags: ${RE_CFLAGS}") message("libre extra_cflags: ${RE_EXTRA_CFLAGS}") message("libre OS: ${RE_OS}") message("libre arch: ${RE_ARCH}") message("libre version: ${RE_VERSION}") elseif(WIN32) # hard-coded on Win32 if(MSVC) SET(RE_CFLAGS "-DWIN32 -D_CONSOLE -D_CRT_SECURE_NO_DEPRECATE -DHAVE_SELECT -DHAVE_IO_H" ) elseif(MINGW) SET(RE_CFLAGS "-DHAVE_STDBOOL_H -DHAVE_INET6 -DHAVE_SELECT -DHAVE_IO_H" ) add_definitions(-Wall -D_WIN32_WINNT=0x0501) endif() # quotes get eaten in generator add_definitions(-DOS=\"win32\" -DWIN32 -DARCH=\"i386\" -DVERSION=\"0.3.0\") # on windows we cannot rely on make and have to do this by hand file(GLOB_RECURSE RE_SRCS RELATIVE ${PROJECT_SOURCE_DIR} ${RE_ROOT}/src/*.c) # remove files to compile depending on the compiler flags if (RE_CFLAGS MATCHES USE_ZLIB) LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/crc32/crc32.c") endif() if (NOT RE_CFLAGS MATCHES HAVE_PTHREAD_RWLOCK) LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/lock/rwlock.c") endif() if (NOT RE_CFLAGS MATCHES HAVE_GETIFADDRS) LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/net/ifaddrs.c") endif() if (NOT RE_CFLAGS MATCHES HAVE_EPOLL) LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/main/epoll.c") endif() if (NOT RE_CFLAGS MATCHES USE_OPENSSL) LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/tls/openssl/tls.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/tls/openssl/tls_tcp.c") endif() if (NOT RE_CFLAGS MATCHES HAVE_PTHREAD) LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/lock/lock.c") endif() # remove files not to be comiled for win32 in any case LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/httpauth/basic.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/mod/dl.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/net/posix/pif.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/net/linux/rt.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/dns/res.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/dns/darwin/srv.c") LIST(REMOVE_ITEM RE_SRCS "${RE_SRC_PREFIX}/net/bsd/brt.c") endif() include_directories(${RE_ROOT}/include) add_library(re ${RE_SRCS} ${HEADER_FILES}) SET_TARGET_PROPERTIES(re PROPERTIES COMPILE_FLAGS ${RE_CFLAGS} ) re-1.1.0/mk/Doxyfile000066400000000000000000000214721373627245400142570ustar00rootroot00000000000000# Doxyfile 1.4.7 #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- PROJECT_NAME = libre PROJECT_NUMBER = 0.1.0 OUTPUT_DIRECTORY = ../re-dox CREATE_SUBDIRS = NO OUTPUT_LANGUAGE = English #USE_WINDOWS_ENCODING = NO BRIEF_MEMBER_DESC = YES REPEAT_BRIEF = YES ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the ALWAYS_DETAILED_SEC = NO INLINE_INHERITED_MEMB = NO FULL_PATH_NAMES = NO STRIP_FROM_PATH = STRIP_FROM_INC_PATH = SHORT_NAMES = NO JAVADOC_AUTOBRIEF = YES MULTILINE_CPP_IS_BRIEF = NO #DETAILS_AT_TOP = NO INHERIT_DOCS = YES SEPARATE_MEMBER_PAGES = NO TAB_SIZE = 8 ALIASES = OPTIMIZE_OUTPUT_FOR_C = YES OPTIMIZE_OUTPUT_JAVA = NO #BUILTIN_STL_SUPPORT = NO DISTRIBUTE_GROUP_DOC = NO SUBGROUPING = YES #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- EXTRACT_ALL = NO EXTRACT_PRIVATE = NO EXTRACT_STATIC = NO EXTRACT_LOCAL_CLASSES = YES EXTRACT_LOCAL_METHODS = NO HIDE_UNDOC_MEMBERS = YES HIDE_UNDOC_CLASSES = YES HIDE_FRIEND_COMPOUNDS = NO HIDE_IN_BODY_DOCS = NO INTERNAL_DOCS = NO CASE_SENSE_NAMES = YES HIDE_SCOPE_NAMES = NO SHOW_INCLUDE_FILES = YES INLINE_INFO = YES SORT_MEMBER_DOCS = YES SORT_BRIEF_DOCS = NO SORT_BY_SCOPE_NAME = NO GENERATE_TODOLIST = YES GENERATE_TESTLIST = YES GENERATE_BUGLIST = YES GENERATE_DEPRECATEDLIST= YES ENABLED_SECTIONS = MAX_INITIALIZER_LINES = 30 SHOW_USED_FILES = YES #SHOW_DIRECTORIES = NO FILE_VERSION_FILTER = #--------------------------------------------------------------------------- # configuration options related to warning and progress messages #--------------------------------------------------------------------------- QUIET = YES WARNINGS = YES WARN_IF_UNDOCUMENTED = YES WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = YES WARN_FORMAT = "$file:$line: $text" WARN_LOGFILE = #--------------------------------------------------------------------------- # configuration options related to the input files #--------------------------------------------------------------------------- INPUT = include src docs FILE_PATTERNS = *.c \ *.h \ *.dox RECURSIVE = YES EXCLUDE = test.c \ include/re_bitv.h \ src/md5/md5.h src/md5/md5.c EXCLUDE_SYMLINKS = NO EXCLUDE_PATTERNS = */.svn/* EXAMPLE_PATH = . EXAMPLE_PATTERNS = * EXAMPLE_RECURSIVE = NO IMAGE_PATH = INPUT_FILTER = FILTER_PATTERNS = FILTER_SOURCE_FILES = NO #--------------------------------------------------------------------------- # configuration options related to source browsing #--------------------------------------------------------------------------- SOURCE_BROWSER = YES INLINE_SOURCES = NO STRIP_CODE_COMMENTS = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES #REFERENCES_LINK_SOURCE = YES #USE_HTAGS = NO VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # configuration options related to the alphabetical class index #--------------------------------------------------------------------------- ALPHABETICAL_INDEX = YES COLS_IN_ALPHA_INDEX = 5 IGNORE_PREFIX = #--------------------------------------------------------------------------- # configuration options related to the HTML output #--------------------------------------------------------------------------- GENERATE_HTML = YES HTML_OUTPUT = html HTML_FILE_EXTENSION = .html HTML_HEADER = HTML_FOOTER = HTML_STYLESHEET = #HTML_ALIGN_MEMBERS = YES GENERATE_HTMLHELP = NO CHM_FILE = HHC_LOCATION = GENERATE_CHI = NO BINARY_TOC = NO TOC_EXPAND = NO DISABLE_INDEX = NO ENUM_VALUES_PER_LINE = 4 GENERATE_TREEVIEW = NO TREEVIEW_WIDTH = 250 #--------------------------------------------------------------------------- # configuration options related to the LaTeX output #--------------------------------------------------------------------------- GENERATE_LATEX = NO LATEX_OUTPUT = latex LATEX_CMD_NAME = latex MAKEINDEX_CMD_NAME = makeindex COMPACT_LATEX = NO PAPER_TYPE = a4wide EXTRA_PACKAGES = LATEX_HEADER = PDF_HYPERLINKS = NO USE_PDFLATEX = NO LATEX_BATCHMODE = NO LATEX_HIDE_INDICES = NO #--------------------------------------------------------------------------- # configuration options related to the RTF output #--------------------------------------------------------------------------- GENERATE_RTF = NO RTF_OUTPUT = rtf COMPACT_RTF = NO RTF_HYPERLINKS = NO RTF_STYLESHEET_FILE = RTF_EXTENSIONS_FILE = #--------------------------------------------------------------------------- # configuration options related to the man page output #--------------------------------------------------------------------------- GENERATE_MAN = NO MAN_OUTPUT = man MAN_EXTENSION = .3 MAN_LINKS = NO #--------------------------------------------------------------------------- # configuration options related to the XML output #--------------------------------------------------------------------------- GENERATE_XML = NO XML_OUTPUT = xml XML_PROGRAMLISTING = YES #--------------------------------------------------------------------------- # configuration options for the AutoGen Definitions output #--------------------------------------------------------------------------- GENERATE_AUTOGEN_DEF = NO #--------------------------------------------------------------------------- # configuration options related to the Perl module output #--------------------------------------------------------------------------- GENERATE_PERLMOD = NO PERLMOD_LATEX = NO PERLMOD_PRETTY = YES PERLMOD_MAKEVAR_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the preprocessor #--------------------------------------------------------------------------- ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES SEARCH_INCLUDES = YES INCLUDE_PATH = include INCLUDE_FILE_PATTERNS = PREDEFINED = HAVE_INTTYPES_H HAVE_INET6 HAVE_STDBOOL_H EXPAND_AS_DEFINED = SKIP_FUNCTION_MACROS = YES #--------------------------------------------------------------------------- # Configuration::additions related to external references #--------------------------------------------------------------------------- TAGFILES = GENERATE_TAGFILE = ALLEXTERNALS = NO EXTERNAL_GROUPS = YES PERL_PATH = /usr/bin/perl #--------------------------------------------------------------------------- # Configuration options related to the dot tool #--------------------------------------------------------------------------- CLASS_DIAGRAMS = YES HIDE_UNDOC_RELATIONS = YES HAVE_DOT = YES CLASS_GRAPH = YES COLLABORATION_GRAPH = YES GROUP_GRAPHS = YES UML_LOOK = NO TEMPLATE_RELATIONS = NO INCLUDE_GRAPH = YES INCLUDED_BY_GRAPH = YES #CALL_GRAPH = YES todo: disabled to run faster #CALLER_GRAPH = YES GRAPHICAL_HIERARCHY = YES DIRECTORY_GRAPH = YES DOT_IMAGE_FORMAT = png DOT_PATH = DOTFILE_DIRS = DOT_GRAPH_MAX_NODES = 256 #MAX_DOT_GRAPH_WIDTH = 1024 #MAX_DOT_GRAPH_HEIGHT = 1024 #MAX_DOT_GRAPH_DEPTH = 1000 DOT_TRANSPARENT = NO DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES DOT_CLEANUP = YES #--------------------------------------------------------------------------- # Configuration::additions related to the search engine #--------------------------------------------------------------------------- SEARCHENGINE = NO re-1.1.0/mk/exclude000066400000000000000000000000051373627245400141120ustar00rootroot00000000000000rpm/ re-1.1.0/mk/re.mk000066400000000000000000000454741373627245400135200ustar00rootroot00000000000000# # re.mk - common make rules # # Copyright (C) 2010 Creytiv.com # # Imported variables: # # ARCH Target architecture # CC Compiler # CROSS_COMPILE Cross-compiler prefix (optional) # EXTRA_CFLAGS Extra compiler flags appended to CFLAGS # EXTRA_LFLAGS Extra linker flags appended to LFLAGS # GCOV If non-empty, enable GNU Coverage testing # GPROF If non-empty, enable GNU Profiling # OPT_SIZE If non-empty, optimize for size # OPT_SPEED If non-empty, optimize for speed # PROJECT Project name # RELEASE Release build # SYSROOT System root of library and include files # SYSROOT_ALT Alternative system root of library and include files # USE_OPENSSL If non-empty, link to libssl library # USE_ZLIB If non-empty, link to libz library # VERSION Version number # # Exported variables: # # APP_LFLAGS Linker flags for applications using modules # BIN_SUFFIX Suffix for binary executables # CC Compiler # CCACHE Compiler ccache tool # CFLAGS Compiler flags # DFLAGS Dependency generator flags # LFLAGS Common linker flags # LIBS Libraries to link against # LIB_SUFFIX Suffix for shared libraries # MOD_LFLAGS Linker flags for dynamic modules # MOD_SUFFIX Suffix for dynamic modules # SH_LFLAGS Linker flags for shared libraries # USE_TLS Defined if TLS is available # USE_DTLS Defined if DTLS is available # ifneq ($(RELEASE),) CFLAGS += -DRELEASE OPT_SPEED=1 endif # Default system root ifeq ($(SYSROOT),) SYSROOT := /usr endif # Alternative Systemroot ifeq ($(SYSROOT_ALT),) SYSROOT_ALT := $(shell [ -d /sw/include ] && echo "/sw") endif ifeq ($(SYSROOT_ALT),) SYSROOT_ALT := $(shell [ -d /opt/local/include ] && echo "/opt/local") endif ifneq ($(SYSROOT_ALT),) CFLAGS += -I$(SYSROOT_ALT)/include LFLAGS += -L$(SYSROOT_ALT)/lib endif ############################################################################## # # Compiler section # # find compiler name & version ifeq ($(CC),) CC := gcc endif ifeq ($(CC),cc) CC := gcc endif LD := $(CC) CC_LONGVER := $(shell if $(CC) -v 2>/dev/null; then \ $(CC) -v 2>&1 ;\ else \ $(CC) -V 2>&1 ; \ fi ) # find-out the compiler's name ifneq (,$(findstring gcc, $(CC_LONGVER))) CC_NAME := gcc CC_VER := $(word 1,$(CC)) $(shell $(CC) - --version|head -n 1|\ cut -d" " -f 3|\ sed -e 's/^.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/'\ -e 's/^[^0-9].*\([0-9][0-9]*\.[0-9][0-9]*\).*/\1/') # sun sed is a little brain damaged => this complicated expression MKDEP := $(CC) -MM #transform gcc version into 2.9x, 3.x or 4.x CC_SHORTVER := $(shell echo "$(CC_VER)" | cut -d" " -f 2| \ sed -e 's/[^0-9]*-\(.*\)/\1/'| \ sed -e 's/2\.9.*/2.9x/' -e 's/3\.[0-3]\..*/3.0/' -e \ 's/3\.[0-3]/3.0/' -e 's/3\.[4-9]\..*/3.4/' -e\ 's/3\.[4-9]/3.4/' -e 's/4\.[0-9]\..*/4.x/' -e\ 's/4\.[0-9]/4.x/' ) endif ifeq ($(CC_NAME),) ifneq (,$(findstring clang, $(CC_LONGVER))) CC_NAME := clang CC_SHORTVER := $(shell echo "$(CC_LONGVER)"|head -n 1| \ sed -e 's/.*version \([0-9]\.[0-9]\).*/\1/g' ) CC_VER := $(CC) $(CC_SHORTVER) MKDEP := $(CC) -MM endif endif ifeq ($(CC_NAME),) ifneq (, $(findstring Sun, $(CC_LONGVER))) CC_NAME := suncc CC_SHORTVER := $(shell echo "$(CC_LONGVER)"|head -n 1| \ sed -e 's/.*\([0-9]\.[0-9]\).*/\1/g' ) CC_VER := $(CC) $(CC_SHORTVER) MKDEP := $(CC) -xM1 endif endif ifeq ($(CC_NAME),) ifneq (, $(findstring Intel(R) C++ Compiler, $(CC_LONGVER))) # very nice: gcc compatible CC_NAME := icc CC_FULLVER := $(shell echo "$(CC_LONGVER)"|head -n 1| \ sed -e 's/.*Version \([0-9]\.[0-9]\.[0-9]*\).*/\1/g') CC_SHORTVER := $(shell echo "$(CC_FULLVER)" | cut -d. -f1,2 ) CC_VER := $(CC) $(CC_FULLVER) MKDEP := $(CC) -MM endif endif ifeq (,$(CC_NAME)) #not found CC_NAME := $(CC) CC_SHORTVER := unknown CC_VER := unknown MKDEP := gcc -MM $(warning Unknown compiler $(CC)\; supported compilers: \ gcc, clang, sun cc, intel icc ) endif # Compiler warning flags CFLAGS += -Wall CFLAGS += -Wmissing-declarations CFLAGS += -Wmissing-prototypes CFLAGS += -Wstrict-prototypes CFLAGS += -Wbad-function-cast CFLAGS += -Wsign-compare CFLAGS += -Wnested-externs CFLAGS += -Wshadow CFLAGS += -Waggregate-return CFLAGS += -Wcast-align ifeq ($(CC_SHORTVER),4.x) CFLAGS += -Wextra CFLAGS += -Wold-style-definition CFLAGS += -Wdeclaration-after-statement endif CFLAGS += -g ifneq ($(OPT_SPEED),) CFLAGS += -O3 # Optimize for speed - takes longer to compile! OPTIMIZE := 1 endif ifneq ($(OPT_SIZE),) CFLAGS += -Os # Optimize for size - takes longer to compile! OPTIMIZE := 1 endif ifneq ($(OPTIMIZE),) CFLAGS += -Wuninitialized ifneq ($(CC_SHORTVER), 2.9x) CFLAGS += -Wno-strict-aliasing endif endif # Compiler dependency flags ifeq ($(CC_SHORTVER), 2.9x) DFLAGS = -MD else DFLAGS = -MD -MF $(@:.o=.d) -MT $@ endif ############################################################################## # # OS section # MACHINE := $(shell $(CC) -dumpmachine) ifeq ($(CROSS_COMPILE),) OS := $(shell uname -s | sed -e s/SunOS/solaris/ | tr "[A-Z]" "[a-z]") endif ifneq ($(strip $(filter i386-mingw32 i486-mingw32 i586-mingw32msvc \ i686-w64-mingw32 x86_64-w64-mingw32 mingw32, \ $(MACHINE))),) OS := win32 ifeq ($(MACHINE), mingw32) CROSS_COMPILE := endif endif # default LIB_SUFFIX := .so MOD_SUFFIX := .so BIN_SUFFIX := ifeq ($(OS),solaris) CFLAGS += -fPIC -DSOLARIS LIBS += -ldl -lresolv -lsocket -lnsl LFLAGS += -fPIC SH_LFLAGS += -G MOD_LFLAGS += APP_LFLAGS += AR := ar AFLAGS := cru endif ifeq ($(OS),linux) CFLAGS += -fPIC -DLINUX LIBS += -ldl LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := crD endif ifeq ($(OS),gnu) CFLAGS += -fPIC -DGNU LIBS += -ldl LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := cru endif ifeq ($(OS),darwin) CFLAGS += -fPIC -dynamic -DDARWIN ifneq (,$(findstring Apple, $(CC_LONGVER))) CFLAGS += -Wshorten-64-to-32 endif DFLAGS := -MD LIBS += -lresolv LFLAGS += -fPIC SH_LFLAGS += -dynamiclib ifeq ($(CC_NAME),gcc) SH_LFLAGS += -dylib endif ifneq ($(VERSION),) SH_LFLAGS += -current_version $(VERSION) SH_LFLAGS += -compatibility_version $(VERSION) endif MOD_LFLAGS += -undefined dynamic_lookup APP_LFLAGS += AR := ar AFLAGS := cru LIB_SUFFIX := .dylib HAVE_KQUEUE := 1 SYSROOT := $(shell xcrun --show-sdk-path)/usr endif ifeq ($(OS),netbsd) CFLAGS += -fPIC -DNETBSD LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := cru HAVE_KQUEUE := 1 endif ifeq ($(OS),freebsd) CFLAGS += -fPIC -DFREEBSD LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := cru HAVE_KQUEUE := 1 endif ifeq ($(OS),gnu/kfreebsd) CFLAGS += -fPIC -DKFREEBSD -D_GNU_SOURCE LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := cru HAVE_KQUEUE := 1 endif ifeq ($(OS),dragonfly) CFLAGS += -fPIC -DDRAGONFLY LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := cru HAVE_KQUEUE := 1 endif ifeq ($(OS),openbsd) CFLAGS += -fPIC -DOPENBSD LFLAGS += -fPIC SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -rdynamic AR := ar AFLAGS := cru HAVE_KQUEUE := 1 HAVE_ARC4RANDOM := 1 endif ifeq ($(OS),win32) CFLAGS += -DWIN32 -D_WIN32_WINNT=0x0501 -D__ssize_t_defined LIBS += -lwsock32 -lws2_32 -liphlpapi LFLAGS += SH_LFLAGS += -shared MOD_LFLAGS += APP_LFLAGS += -Wl,--export-all-symbols AR := ar AFLAGS := cru CROSS_COMPILE ?= $(MACHINE)- RANLIB := $(CROSS_COMPILE)ranlib LIB_SUFFIX := .dll MOD_SUFFIX := .dll BIN_SUFFIX := .exe SYSROOT := /usr/$(MACHINE)/ endif CFLAGS += -DOS=\"$(OS)\" ifeq ($(CC_SHORTVER),2.9x) CFLAGS += -Wno-long-long else CFLAGS += -std=c99 PEDANTIC := 1 endif # CC_SHORTVER ifneq ($(PEDANTIC),) CFLAGS += -pedantic endif ifeq ($(OS),) $(warning Could not detect OS) endif ############################################################################## # # Architecture section # ifeq ($(ARCH),) ifeq ($(CC_NAME),$(filter $(CC_NAME),gcc clang)) PREDEF := $(shell $(CC) -dM -E -x c $(EXTRA_CFLAGS) $(CFLAGS) /dev/null) ifneq ($(strip $(filter i386 __i386__ __i386 _M_IX86 __X86__ _X86_, \ $(PREDEF))),) ARCH := i386 endif ifneq ($(strip $(filter __i486__,$(PREDEF))),) ARCH := i486 endif ifneq ($(strip $(filter __i586__,$(PREDEF))),) ARCH := i586 endif ifneq ($(strip $(filter __i686__ ,$(PREDEF))),) ARCH := i686 endif ifneq ($(strip $(filter __amd64__ __amd64 __x86_64__ __x86_64, \ $(PREDEF))),) ARCH := x86_64 endif ifneq ($(strip $(filter __arm__ __thumb__,$(PREDEF))),) ifneq ($(strip $(filter __ARM_ARCH_6__,$(PREDEF))),) ARCH := arm6 else ARCH := arm endif endif ifneq ($(strip $(filter __arm64__ ,$(PREDEF))),) ARCH := arm64 endif ifneq ($(strip $(filter __mips__ __mips, $(PREDEF))),) ARCH := mips endif ifneq ($(strip $(filter __powerpc __powerpc__ __POWERPC__ __ppc__ \ _ARCH_PPC, $(PREDEF))),) ARCH := ppc endif ifneq ($(strip $(filter __ppc64__ _ARCH_PPC64 , $(PREDEF))),) ARCH := ppc64 endif ifneq ($(strip $(filter __sparc__ __sparc __sparcv8 , $(PREDEF))),) ifneq ($(strip $(filter __sparcv9 __sparc_v9__ , $(PREDEF))),) ARCH := sparc64 else ARCH := sparc endif endif endif endif ifeq ($(ARCH),) $(warning Could not detect ARCH) endif CFLAGS += -DARCH=\"$(ARCH)\" ifeq ($(ARCH),mipsel) CFLAGS += -march=mips32 endif ############################################################################## # # External libraries section # USE_OPENSSL := $(shell [ -f $(SYSROOT)/include/openssl/ssl.h ] || \ [ -f $(SYSROOT)/local/include/openssl/ssl.h ] || \ [ -f $(SYSROOT_ALT)/include/openssl/ssl.h ] && echo "yes") ifneq ($(USE_OPENSSL),) CFLAGS += -DUSE_OPENSSL -DUSE_TLS LIBS += -lssl -lcrypto USE_TLS := yes USE_OPENSSL_DTLS := $(shell [ -f $(SYSROOT)/include/openssl/dtls1.h ] || \ [ -f $(SYSROOT)/local/include/openssl/dtls1.h ] || \ [ -f $(SYSROOT_ALT)/include/openssl/dtls1.h ] && echo "yes") USE_OPENSSL_SRTP := $(shell [ -f $(SYSROOT)/include/openssl/srtp.h ] || \ [ -f $(SYSROOT)/local/include/openssl/srtp.h ] || \ [ -f $(SYSROOT_ALT)/include/openssl/srtp.h ] && echo "yes") ifneq ($(USE_OPENSSL_DTLS),) CFLAGS += -DUSE_OPENSSL_DTLS -DUSE_DTLS USE_DTLS := yes endif ifneq ($(USE_OPENSSL_SRTP),) CFLAGS += -DUSE_OPENSSL_SRTP -DUSE_DTLS_SRTP USE_DTLS_SRTP := yes endif USE_OPENSSL_AES := yes USE_OPENSSL_HMAC := yes endif USE_ZLIB := $(shell [ -f $(SYSROOT)/include/zlib.h ] || \ [ -f $(SYSROOT)/local/include/zlib.h ] || \ [ -f $(SYSROOT_ALT)/include/zlib.h ] && echo "yes") ifneq ($(USE_ZLIB),) CFLAGS += -DUSE_ZLIB LIBS += -lz endif ifneq ($(OS),win32) HAVE_PTHREAD := $(shell [ -f $(SYSROOT)/include/pthread.h ] && echo "1") ifneq ($(HAVE_PTHREAD),) HAVE_PTHREAD_RWLOCK := 1 CFLAGS += -DHAVE_PTHREAD HAVE_LIBPTHREAD := 1 ifneq ($(HAVE_LIBPTHREAD),) LIBS += -lpthread endif endif ifneq ($(ARCH),mipsel) HAVE_GETIFADDRS := $(shell [ -f $(SYSROOT)/include/ifaddrs.h ] && echo "1") ifneq ($(HAVE_GETIFADDRS),) CFLAGS += -DHAVE_GETIFADDRS endif endif HAVE_STRERROR_R := 1 ifneq ($(HAVE_STRERROR_R),) CFLAGS += -DHAVE_STRERROR_R endif endif HAVE_GETOPT := $(shell [ -f $(SYSROOT)/include/getopt.h ] && echo "1") ifneq ($(HAVE_GETOPT),) CFLAGS += -DHAVE_GETOPT endif HAVE_INTTYPES_H := $(shell [ -f $(SYSROOT)/include/inttypes.h ] && echo "1") ifneq ($(HAVE_INTTYPES_H),) CFLAGS += -DHAVE_INTTYPES_H endif HAVE_NET_ROUTE_H := $(shell [ -f $(SYSROOT)/include/net/route.h ] && echo "1") ifneq ($(HAVE_NET_ROUTE_H),) CFLAGS += -DHAVE_NET_ROUTE_H endif HAVE_SYS_SYSCTL_H := \ $(shell [ -f $(SYSROOT)/include/sys/sysctl.h ] || \ [ -f $(SYSROOT)/include/$(MACHINE)/sys/sysctl.h ] \ && echo "1") ifneq ($(HAVE_SYS_SYSCTL_H),) CFLAGS += -DHAVE_SYS_SYSCTL_H endif CFLAGS += -DHAVE_STDBOOL_H HAVE_INET6 := 1 ifneq ($(HAVE_INET6),) CFLAGS += -DHAVE_INET6 endif ifeq ($(OS),win32) CFLAGS += -DHAVE_SELECT CFLAGS += -DHAVE_IO_H else HAVE_SYSLOG := $(shell [ -f $(SYSROOT)/include/syslog.h ] && echo "1") HAVE_DLFCN_H := $(shell [ -f $(SYSROOT)/include/dlfcn.h ] && echo "1") ifneq ($(OS),darwin) HAVE_EPOLL := $(shell [ -f $(SYSROOT)/include/sys/epoll.h ] || \ [ -f $(SYSROOT)/include/$(MACHINE)/sys/epoll.h ] \ && echo "1") endif HAVE_RESOLV := $(shell [ -f $(SYSROOT)/include/resolv.h ] && echo "1") ifneq ($(HAVE_RESOLV),) CFLAGS += -DHAVE_RESOLV endif ifneq ($(HAVE_SYSLOG),) CFLAGS += -DHAVE_SYSLOG endif HAVE_INET_NTOP := 1 CFLAGS += -DHAVE_FORK ifneq ($(HAVE_INET_NTOP),) CFLAGS += -DHAVE_INET_NTOP endif CFLAGS += -DHAVE_PWD_H ifneq ($(OS),darwin) CFLAGS += -DHAVE_POLL # Darwin: poll() does not support devices HAVE_INET_PTON := 1 endif ifneq ($(HAVE_INET_PTON),) CFLAGS += -DHAVE_INET_PTON endif CFLAGS += -DHAVE_SELECT -DHAVE_SELECT_H CFLAGS += -DHAVE_SETRLIMIT CFLAGS += -DHAVE_SIGNAL CFLAGS += -DHAVE_SYS_TIME_H ifneq ($(HAVE_EPOLL),) CFLAGS += -DHAVE_EPOLL endif ifneq ($(HAVE_KQUEUE),) CFLAGS += -DHAVE_KQUEUE endif CFLAGS += -DHAVE_UNAME CFLAGS += -DHAVE_UNISTD_H CFLAGS += -DHAVE_STRINGS_H endif ifneq ($(HAVE_ARC4RANDOM),) CFLAGS += -DHAVE_ARC4RANDOM endif ############################################################################## # # Misc tools section # CCACHE := $(shell [ -e /usr/bin/ccache ] 2>/dev/null \ || [ -e /opt/local/bin/ccache ] \ && echo "ccache") CFLAGS += -DVERSION=\"$(VERSION)\" CFLAGS += \ -DVER_MAJOR=$(VER_MAJOR) \ -DVER_MINOR=$(VER_MINOR) \ -DVER_PATCH=$(VER_PATCH) # Enable gcov Coverage testing # # - generated during build: .gcno files # - generated during exec: .gcda files # ifneq ($(GCOV),) CFLAGS += -fprofile-arcs -ftest-coverage LFLAGS += -fprofile-arcs -ftest-coverage # Disable ccache CCACHE := endif # gprof - GNU Profiling # # - generated during exec: gmon.out # ifneq ($(GPROF),) CFLAGS += -pg LFLAGS += -pg # Disable ccache CCACHE := endif CC := $(CCACHE) $(CC) CFLAGS += $(EXTRA_CFLAGS) LFLAGS += $(EXTRA_LFLAGS) BUILD := build-$(ARCH) default: all .PHONY: distclean distclean: @rm -rf build* *core* @rm -f *stamp $(BIN) @rm -f `find . -name "*.[oda]"` `find . -name "*.so"` @rm -f `find . -name "*~"` `find . -name "\.\#*"` @rm -f `find . -name "*.orig"` `find . -name "*.rej"` @rm -f `find . -name "*.previous"` `find . -name "*.gcov"` @rm -f `find . -name "*.exe"` `find . -name "*.dll"` @rm -f `find . -name "*.dylib"` .PHONY: info info: @echo "info - $(PROJECT) version $(VERSION)" @echo " MODULES: $(MODULES)" # @echo " SRCS: $(SRCS)" @echo " MACHINE: $(MACHINE)" @echo " ARCH: $(ARCH)" @echo " OS: $(OS)" @echo " BUILD: $(BUILD)" @echo " CCACHE: $(CCACHE)" @echo " CC: $(CC_NAME) $(CC_SHORTVER)" @echo " CFLAGS: $(CFLAGS)" @echo " DFLAGS: $(DFLAGS)" @echo " LFLAGS: $(LFLAGS)" @echo " SH_LFLAGS: $(SH_LFLAGS)" @echo " MOD_LFLAGS: $(MOD_LFLAGS)" @echo " APP_LFLAGS: $(APP_LFLAGS)" @echo " LIBS: $(LIBS)" @echo " LIBRE_MK: $(LIBRE_MK)" @echo " LIBRE_INC: $(LIBRE_INC)" @echo " LIBRE_SO: $(LIBRE_SO)" @echo " USE_OPENSSL: $(USE_OPENSSL)" @echo " USE_OPENSSL_AES: $(USE_OPENSSL_AES)" @echo " USE_OPENSSL_HMAC: $(USE_OPENSSL_HMAC)" @echo " USE_TLS: $(USE_TLS)" @echo " USE_DTLS: $(USE_DTLS)" @echo " USE_DTLS_SRTP: $(USE_DTLS_SRTP)" @echo " USE_ZLIB: $(USE_ZLIB)" @echo " GCOV: $(GCOV)" @echo " GPROF: $(GPROF)" @echo " CROSS_COMPILE: $(CROSS_COMPILE)" @echo " SYSROOT: $(SYSROOT)" @echo " SYSROOT_ALT: $(SYSROOT_ALT)" @echo " LIB_SUFFIX: $(LIB_SUFFIX)" @echo " MOD_SUFFIX: $(MOD_SUFFIX)" @echo " BIN_SUFFIX: $(BIN_SUFFIX)" ############################################################################## # # Packaging section # TAR_SRC := $(PROJECT)-$(VERSION) release: @rm -rf ../$(TAR_SRC) @svn export . ../$(TAR_SRC) @if [ -f ../$(TAR_SRC)/mk/exclude ]; then \ cat ../$(TAR_SRC)/mk/exclude \ | sed 's|^|../$(TAR_SRC)/|' | xargs -t rm -rf ; \ rm -f ../$(TAR_SRC)/mk/exclude ; \ fi @cd .. && rm -f $(TAR_SRC).tar.gz \ && tar -zcf $(TAR_SRC).tar.gz $(TAR_SRC) \ && echo "created release tarball `pwd`/$(TAR_SRC).tar.gz" tar: @rm -rf ../$(TAR_SRC) @svn export . ../$(TAR_SRC) @cd .. && rm -f $(TAR_SRC).tar.gz \ && tar -zcf $(TAR_SRC).tar.gz $(TAR_SRC) \ && echo "created source tarball `pwd`/$(TAR_SRC).tar.gz" git_release: git archive --format=tar --prefix=$(PROJECT)-$(VERSION)/ v$(VERSION) \ | gzip > ../$(PROJECT)-$(VERSION).tar.gz git_snapshot: git archive --format=tar --prefix=$(PROJECT)-$(VERSION)/ HEAD \ | gzip > ../$(PROJECT)-$(VERSION).tar.gz # Debian .PHONY: deb deb: dpkg-buildpackage -rfakeroot --post-clean # RPM RPM := $(shell [ -d /usr/src/rpm ] 2>/dev/null && echo "rpm") ifeq ($(RPM),) RPM := $(shell [ -d /usr/src/redhat ] 2>/dev/null && echo "redhat") endif .PHONY: rpm rpm: tar sudo cp ../$(PROJECT)-$(VERSION).tar.gz /usr/src/$(RPM)/SOURCES sudo rpmbuild -ba rpm/$(PROJECT).spec ############################################################################## # # Library and header files location section - in prioritised order # # - relative path # - local installation # - system installation # LIBRE_PATH := ../re # Include path LIBRE_INC := $(shell [ -f $(LIBRE_PATH)/include/re.h ] && \ echo "$(LIBRE_PATH)/include") ifeq ($(LIBRE_INC),) LIBRE_INC := $(shell [ -f /usr/local/include/re/re.h ] && \ echo "/usr/local/include/re") endif ifeq ($(LIBRE_INC),) LIBRE_INC := $(shell [ -f /usr/include/re/re.h ] && echo "/usr/include/re") endif # Library path LIBRE_SO := $(shell [ -f $(LIBRE_PATH)/libre$(LIB_SUFFIX) ] \ && echo "$(LIBRE_PATH)") ifeq ($(LIBRE_SO),) LIBRE_SO := $(shell [ -f /usr/local/lib/libre$(LIB_SUFFIX) ] \ && echo "/usr/local/lib") endif ifeq ($(LIBRE_SO),) LIBRE_SO := $(shell [ -f /usr/lib/libre$(LIB_SUFFIX) ] && echo "/usr/lib") endif ifeq ($(LIBRE_SO),) LIBRE_SO := $(shell [ -f /usr/lib64/libre$(LIB_SUFFIX) ] && echo "/usr/lib64") endif ############################################################################### # # Clang section # CLANG_OPTIONS := -Iinclude -I$(LIBRE_INC) $(CFLAGS) CLANG_IGNORE := CLANG_SRCS += $(filter-out $(CLANG_IGNORE), $(patsubst %,src/%,$(SRCS))) clang: @clang --analyze $(CLANG_OPTIONS) $(CLANG_SRCS) @rm -f *.plist ############################################################################### # # Documentation section # DOX_DIR=../$(PROJECT)-dox DOX_TAR=$(PROJECT)-dox-$(VERSION) $(DOX_DIR): @mkdir $@ $(DOX_DIR)/Doxyfile: mk/Doxyfile Makefile @cp $< $@ @perl -pi -e 's/PROJECT_NUMBER\s*=.*/PROJECT_NUMBER = $(VERSION)/' \ $(DOX_DIR)/Doxyfile .PHONY: dox: $(DOX_DIR) $(DOX_DIR)/Doxyfile @doxygen $(DOX_DIR)/Doxyfile 2>&1 | grep -v DEBUG_ ; true @cd .. && rm -f $(DOX_TAR).tar.gz && \ tar -zcf $(DOX_TAR).tar.gz $(PROJECT)-dox > /dev/null && \ echo "Doxygen docs in `pwd`/$(DOX_TAR).tar.gz" re-1.1.0/mk/win32/000077500000000000000000000000001373627245400135055ustar00rootroot00000000000000re-1.1.0/mk/win32/re.vcxproj000066400000000000000000000421311373627245400155310ustar00rootroot00000000000000 Debug Win32 Release Win32 re-win32 {40B28DF6-4B4A-411A-9EB7-8D80C2A29B9D} Win32Proj 8.1 StaticLibrary v140 MultiByte StaticLibrary v140 MultiByte <_ProjectFileVersion>14.0.25123.0 ..\..\$(Platform)\$(Configuration)\bin\ ..\..\$(Platform)\$(Configuration)\tmp\mk\win32\ ..\..\$(Platform)\$(Configuration)\bin\ ..\..\$(Platform)\$(Configuration)\tmp\mk\win32\ Disabled Default ..\..\include;..\..\..\misc;%(AdditionalIncludeDirectories) WIN32;_CONSOLE;HAVE_INET_PTON;HAVE_INET_NTOP;FD_SETSIZE=1024;HAVE_SELECT;HAVE_IO_H;_CRT_SECURE_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;_DEBUG;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebug Level3 EditAndContinue 4142;%(DisableSpecificWarnings) $(IntDir)%(RelativeDir)%(Filename).obj Iphlpapi.lib %(AdditionalOptions) ws2_32.lib;%(AdditionalDependencies) ..\..\include;..\..\..\misc;%(AdditionalIncludeDirectories) WIN32;_CONSOLE;HAVE_INET_PTON;HAVE_INET_NTOP;FD_SETSIZE=1024;HAVE_SELECT;HAVE_IO_H;_CRT_SECURE_NO_DEPRECATE;WIN32_LEAN_AND_MEAN;NDEBUG;%(PreprocessorDefinitions) MultiThreaded Level3 ProgramDatabase 4142;%(DisableSpecificWarnings) $(IntDir)%(RelativeDir)%(Filename).obj ws2_32.lib;%(AdditionalDependencies) re-1.1.0/mk/win32/re.vcxproj.filters000066400000000000000000000740601373627245400172060ustar00rootroot00000000000000 {24ec5e26-1680-4fb0-8766-02a6834e676d} {f97cb0a3-607e-4302-a41d-cbac8aad6bd7} {57a0561c-ea6e-45d0-a034-72d1fabd97ca} {974b5b66-04cf-4237-9e6c-f706acb078bd} {0115caca-c0eb-469d-9f51-1a8a53a4cf36} {e46ff0a0-f978-4356-8103-804f228ce65d} {7f1f0174-24be-4049-bccd-c03c00e5bacc} {b277177e-4cd2-4028-8e24-b12237bd4122} {d1a8c49e-d20f-4bca-ab49-7dd50593c09a} {469bd25d-3ebd-474b-b13c-6914bfa44f2e} {793d3b1b-a4a5-49a7-aeb4-fc3be5944d3b} {da8a92af-e8a6-45c6-9708-aa18e3adb084} {5d4ea0d3-afee-4f12-8a79-ac4c8447777a} {ed8fd6a9-58aa-47c4-b1f7-34e1ac182b87} {e29353b1-55db-453e-91c6-f7f37748147c} {f3ce68eb-55bb-46b2-8d3f-1b1048fad972} {c6620f98-9fd7-45ea-9b58-cc2c4a3b6f2d} {60bd6b3a-4f71-48fb-a161-10ce29910d93} {8643b7af-6ec5-43b4-b0fe-399a79d445cc} {f7c75b83-f284-430c-b18c-b2633914a750} {779e44ed-0935-450c-a3dd-7bdef53a1179} {1bff8dd5-d220-4f10-8c26-64c82522d824} {9ea33625-0411-4065-a16b-ce3f49d3ca15} {955109b7-ec66-49d3-b4cf-0ba816e095e4} {31d6e0d5-7c9d-4ca7-a911-4be6b45291b0} {249dc72a-9cb9-45fa-bdd1-742fafb1d831} {e6d208f2-2ef2-49bc-a13a-d66885a1dd61} {fc6552c5-e5cd-4f77-9daa-77361089d71f} {eb84b7a5-764a-43c4-98b1-90c989cbe8e7} {09180e07-9646-454c-ae1a-7bfdc9ec96cc} {4d08218c-9027-430b-bec4-5b435620ca77} {3489e5fe-f25d-4410-a351-8c485609a49a} {c33fec0f-9764-431d-9fe3-46704e850f6b} {fb912922-9084-4a6a-b36f-cf7039fbdf59} {bf38bff6-d13b-40e9-908f-73aa6bd29dc2} {8723551b-d33a-464f-b15e-d477b65d8d34} {c9915c2b-1f49-45bc-9552-8679bd697f23} {c8b66d98-2787-4b05-be8c-d4acd28dbf9e} {ac3c7db7-cb2d-4191-9c45-f499de912f51} {9a795fdf-efaf-4ea1-b7c7-671616e84f0a} {2af593ee-1fa6-44c1-b6ca-01d437c39397} {37c6bc4f-8fd1-40cf-b04c-77ae5d7f80c6} {6aae1e31-0191-4c51-a470-c43be58ae837} {f029350a-d256-4dda-889b-430f65615047} {c1c4b382-5196-45dc-9185-1b321a34caa2} {7590d26c-f92a-4da7-89be-857b74649099} {394be8ad-8a7d-4ed8-93a5-cf4a1c9f5c37} {26df0238-b37e-4c97-97d4-549f23a4fb1b} {3f9bcd72-2513-4e1e-826e-6df1e9c3fa3e} {7a203acf-4223-4b71-8705-995eac29db75} {5bb58e89-4183-4380-993a-d01d1bc3d9d9} include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include include src\turn src\stun src\srtp src\sipsess src\sipevent src\sip src\sdp src\sa src\rtp src\mqueue src\mod src\md5 src\main src\ice src\dns src\bfcp src\uri src\uri src\udp src\udp src\turn src\turn src\turn src\tmr src\telev src\tcp src\tcp src\sys src\sys src\sys src\sys src\sys src\sys src\stun src\stun src\stun src\stun src\stun src\stun src\stun src\stun src\stun src\stun src\stun src\stun src\srtp src\srtp src\srtp src\srtp src\srtp src\sipsess src\sipsess src\sipsess src\sipsess src\sipsess src\sipsess src\sipsess src\sipsess src\sipsess src\sipsess src\sipreg src\sipevent src\sipevent src\sipevent src\sipevent src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sip src\sdp src\sdp src\sdp src\sdp src\sdp src\sdp src\sdp src\sa src\sa src\sa src\sa src\rtp src\rtp src\rtp src\rtp src\rtp src\rtp src\rtp src\rtp src\rtp src\rtp src\odict src\odict src\odict src\net src\net src\net src\net src\net src\net src\net\win32 src\natbd src\natbd src\natbd src\natbd src\natbd src\natbd src\msg src\msg src\mqueue src\mqueue\win32 src\mod src\mod\win32 src\mem src\md5 src\md5 src\mbuf src\main src\main src\main src\lock\win32 src\list src\json src\json src\json src\jbuf src\ice src\ice src\ice src\ice src\ice src\ice src\ice src\ice src\ice src\ice src\ice src\httpauth src\httpauth src\http src\http src\http src\http src\hmac src\hmac src\hash src\hash src\fmt src\fmt src\fmt src\fmt src\fmt src\fmt src\fmt src\fmt src\fmt src\fmt src\dns src\dns src\dns src\dns src\dns src\dns src\dns src\dns\win32 src\dbg src\crc32 src\conf src\bfcp src\bfcp src\bfcp src\bfcp src\bfcp src\aes src\base64 src\sha src\srtp re-1.1.0/rpm/000077500000000000000000000000001373627245400127325ustar00rootroot00000000000000re-1.1.0/rpm/re.spec000066400000000000000000000021751373627245400142210ustar00rootroot00000000000000%define name re %define ver 1.1.0 %define rel 1 Summary: Generic library for real-time communications with async IO support Name: %name Version: %ver Release: %rel License: BSD Group: Applications/Devel Source0: file://%{name}-%{version}.tar.gz URL: http://www.creytiv.com/ Vendor: Creytiv Packager: Alfred E. Heggestad BuildRoot: /var/tmp/%{name}-build-root %description Generic library for real-time communications with async IO support %package devel Summary: libre development files Group: Development/Libraries Requires: %{name} = %{version} %description devel libre development files. %prep %setup %build make RELEASE=1 %install rm -rf $RPM_BUILD_ROOT make DESTDIR=$RPM_BUILD_ROOT install \ %ifarch x86_64 LIBDIR=/usr/lib64 %endif %clean rm -rf $RPM_BUILD_ROOT %post -p /sbin/ldconfig %postun -p /sbin/ldconfig %files %defattr(644,root,root,755) %attr(755,root,root) %{_libdir}/libre*.so* %files devel %defattr(644,root,root,755) %{_includedir}/re/*.h /usr/share/re/re.mk %{_libdir}/libre*.a %{_libdir}/pkgconfig/libre.pc %changelog * Fri Nov 5 2010 Alfred E. Heggestad - - Initial build. re-1.1.0/src/000077500000000000000000000000001373627245400127235ustar00rootroot00000000000000re-1.1.0/src/aes/000077500000000000000000000000001373627245400134735ustar00rootroot00000000000000re-1.1.0/src/aes/apple/000077500000000000000000000000001373627245400145745ustar00rootroot00000000000000re-1.1.0/src/aes/apple/aes.c000066400000000000000000000050411373627245400155100ustar00rootroot00000000000000/** * @file apple/aes.c AES using Apple CommonCrypto API * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include struct aes { CCCryptorRef cryptor; uint8_t key[64]; size_t key_bytes; }; static void destructor(void *arg) { struct aes *st = arg; if (st->cryptor) CCCryptorRelease(st->cryptor); } int aes_alloc(struct aes **stp, enum aes_mode mode, const uint8_t *key, size_t key_bits, const uint8_t *iv) { struct aes *st; size_t key_bytes = key_bits / 8; CCCryptorStatus status; int err = 0; if (!stp || !key) return EINVAL; if (mode != AES_MODE_CTR) return ENOTSUP; st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; if (key_bytes > sizeof(st->key)) { err = EINVAL; goto out; } st->key_bytes = key_bytes; memcpy(st->key, key, st->key_bytes); /* used for both encryption and decryption because CTR is symmetric */ status = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR, kCCAlgorithmAES, ccNoPadding, iv, key, key_bytes, NULL, 0, 0, kCCModeOptionCTR_BE, &st->cryptor); if (status != kCCSuccess) { err = EPROTO; goto out; } out: if (err) mem_deref(st); else *stp = st; return err; } void aes_set_iv(struct aes *st, const uint8_t *iv) { CCCryptorStatus status; if (!st) return; /* we must reset the state when updating IV */ if (st->cryptor) { CCCryptorRelease(st->cryptor); st->cryptor = NULL; } status = CCCryptorCreateWithMode(kCCEncrypt, kCCModeCTR, kCCAlgorithmAES, ccNoPadding, iv, st->key, st->key_bytes, NULL, 0, 0, kCCModeOptionCTR_BE, &st->cryptor); if (status != kCCSuccess) { re_fprintf(stderr, "aes: CCCryptorCreateWithMode error (%d)\n", status); } } int aes_encr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len) { CCCryptorStatus status; size_t moved; if (!st || !out || !in) return EINVAL; status = CCCryptorUpdate(st->cryptor, in, len, out, len, &moved); if (status != kCCSuccess) { re_fprintf(stderr, "aes: CCCryptorUpdate error (%d)\n", status); return EPROTO; } return 0; } int aes_decr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len) { return aes_encr(st, out, in, len); } int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen) { (void)aes; (void)tag; (void)taglen; return ENOSYS; } int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen) { (void)aes; (void)tag; (void)taglen; return ENOSYS; } re-1.1.0/src/aes/mod.mk000066400000000000000000000003051373627245400146010ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # ifneq ($(USE_OPENSSL_AES),) SRCS += aes/openssl/aes.c else ifneq ($(USE_APPLE_COMMONCRYPTO),) SRCS += aes/apple/aes.c else SRCS += aes/stub.c endif re-1.1.0/src/aes/openssl/000077500000000000000000000000001373627245400151565ustar00rootroot00000000000000re-1.1.0/src/aes/openssl/aes.c000066400000000000000000000106001373627245400160670ustar00rootroot00000000000000/** * @file openssl/aes.c AES (Advanced Encryption Standard) using OpenSSL * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include struct aes { EVP_CIPHER_CTX *ctx; enum aes_mode mode; bool encr; }; static const EVP_CIPHER *aes_cipher(enum aes_mode mode, size_t key_bits) { if (mode == AES_MODE_CTR) { switch (key_bits) { case 128: return EVP_aes_128_ctr(); case 192: return EVP_aes_192_ctr(); case 256: return EVP_aes_256_ctr(); default: return NULL; } } else if (mode == AES_MODE_GCM) { switch (key_bits) { case 128: return EVP_aes_128_gcm(); case 256: return EVP_aes_256_gcm(); default: return NULL; } } else { return NULL; } } static inline bool set_crypt_dir(struct aes *aes, bool encr) { if (aes->encr != encr) { /* update the encrypt/decrypt direction */ if (!EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, NULL, encr)) { ERR_clear_error(); return false; } aes->encr = encr; } return true; } static void destructor(void *arg) { struct aes *st = arg; #if OPENSSL_VERSION_NUMBER >= 0x10100000L if (st->ctx) EVP_CIPHER_CTX_free(st->ctx); #else if (st->ctx) EVP_CIPHER_CTX_cleanup(st->ctx); mem_deref(st->ctx); #endif } int aes_alloc(struct aes **aesp, enum aes_mode mode, const uint8_t *key, size_t key_bits, const uint8_t *iv) { const EVP_CIPHER *cipher; struct aes *st; int err = 0, r; if (!aesp || !key) return EINVAL; cipher = aes_cipher(mode, key_bits); if (!cipher) return ENOTSUP; st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; st->mode = mode; st->encr = true; #if OPENSSL_VERSION_NUMBER >= 0x10100000L st->ctx = EVP_CIPHER_CTX_new(); if (!st->ctx) { ERR_clear_error(); err = ENOMEM; goto out; } #else st->ctx = mem_zalloc(sizeof(*st->ctx), NULL); if (!st->ctx) { err = ENOMEM; goto out; } EVP_CIPHER_CTX_init(st->ctx); #endif r = EVP_EncryptInit_ex(st->ctx, cipher, NULL, key, iv); if (!r) { ERR_clear_error(); err = EPROTO; } out: if (err) mem_deref(st); else *aesp = st; return err; } void aes_set_iv(struct aes *aes, const uint8_t *iv) { int r; if (!aes || !iv) return; r = EVP_CipherInit_ex(aes->ctx, NULL, NULL, NULL, iv, -1); if (!r) ERR_clear_error(); } int aes_encr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len) { int c_len = (int)len; if (!aes || !in) return EINVAL; if (!set_crypt_dir(aes, true)) return EPROTO; if (!EVP_EncryptUpdate(aes->ctx, out, &c_len, in, (int)len)) { ERR_clear_error(); return EPROTO; } return 0; } int aes_decr(struct aes *aes, uint8_t *out, const uint8_t *in, size_t len) { int c_len = (int)len; if (!aes || !in) return EINVAL; if (!set_crypt_dir(aes, false)) return EPROTO; if (!EVP_DecryptUpdate(aes->ctx, out, &c_len, in, (int)len)) { ERR_clear_error(); return EPROTO; } return 0; } /** * Get the authentication tag for an AEAD cipher (e.g. GCM) * * @param aes AES Context * @param tag Authentication tag * @param taglen Length of Authentication tag * * @return 0 if success, otherwise errorcode */ int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen) { int tmplen; if (!aes || !tag || !taglen) return EINVAL; switch (aes->mode) { case AES_MODE_GCM: if (!EVP_EncryptFinal_ex(aes->ctx, NULL, &tmplen)) { ERR_clear_error(); return EPROTO; } if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_GET_TAG, (int)taglen, tag)) { ERR_clear_error(); return EPROTO; } return 0; default: return ENOTSUP; } } /** * Authenticate a decryption tag for an AEAD cipher (e.g. GCM) * * @param aes AES Context * @param tag Authentication tag * @param taglen Length of Authentication tag * * @return 0 if success, otherwise errorcode * * @retval EAUTH if authentication failed */ int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen) { int tmplen; if (!aes || !tag || !taglen) return EINVAL; switch (aes->mode) { case AES_MODE_GCM: if (!EVP_CIPHER_CTX_ctrl(aes->ctx, EVP_CTRL_GCM_SET_TAG, (int)taglen, (void *)tag)) { ERR_clear_error(); return EPROTO; } if (EVP_DecryptFinal_ex(aes->ctx, NULL, &tmplen) <= 0) { ERR_clear_error(); return EAUTH; } return 0; default: return ENOTSUP; } } re-1.1.0/src/aes/stub.c000066400000000000000000000017261373627245400146220ustar00rootroot00000000000000/** * @file aes/stub.c AES stub * * Copyright (C) 2010 Creytiv.com */ #include #include int aes_alloc(struct aes **stp, enum aes_mode mode, const uint8_t *key, size_t key_bits, const uint8_t iv[AES_BLOCK_SIZE]) { (void)stp; (void)mode; (void)key; (void)key_bits; (void)iv; return ENOSYS; } void aes_set_iv(struct aes *st, const uint8_t iv[AES_BLOCK_SIZE]) { (void)st; (void)iv; } int aes_encr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len) { (void)st; (void)out; (void)in; (void)len; return ENOSYS; } int aes_decr(struct aes *st, uint8_t *out, const uint8_t *in, size_t len) { (void)st; (void)out; (void)in; (void)len; return ENOSYS; } int aes_get_authtag(struct aes *aes, uint8_t *tag, size_t taglen) { (void)aes; (void)tag; (void)taglen; return ENOSYS; } int aes_authenticate(struct aes *aes, const uint8_t *tag, size_t taglen) { (void)aes; (void)tag; (void)taglen; return ENOSYS; } re-1.1.0/src/base64/000077500000000000000000000000001373627245400140075ustar00rootroot00000000000000re-1.1.0/src/base64/b64.c000066400000000000000000000052711373627245400145530ustar00rootroot00000000000000/** * @file b64.c Base64 encoding/decoding functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include static const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; /** * Base-64 encode a buffer * * @param in Input buffer * @param ilen Length of input buffer * @param out Output buffer * @param olen Size of output buffer, actual written on return * * @return 0 if success, otherwise errorcode */ int base64_encode(const uint8_t *in, size_t ilen, char *out, size_t *olen) { const uint8_t *in_end = in + ilen; const char *o = out; if (!in || !out || !olen) return EINVAL; if (*olen < 4 * ((ilen+2)/3)) return EOVERFLOW; for (; in < in_end; ) { uint32_t v; int pad = 0; v = *in++ << 16; if (in < in_end) { v |= *in++ << 8; } else { ++pad; } if (in < in_end) { v |= *in++ << 0; } else { ++pad; } *out++ = b64_table[v>>18 & 0x3f]; *out++ = b64_table[v>>12 & 0x3f]; *out++ = (pad >= 2) ? '=' : b64_table[v>>6 & 0x3f]; *out++ = (pad >= 1) ? '=' : b64_table[v>>0 & 0x3f]; } *olen = out - o; return 0; } int base64_print(struct re_printf *pf, const uint8_t *ptr, size_t len) { char buf[256]; if (!pf || !ptr) return EINVAL; while (len > 0) { size_t l, sz = sizeof(buf); int err; l = min(len, 3 * (sizeof(buf)/4)); err = base64_encode(ptr, l, buf, &sz); if (err) return err; err = pf->vph(buf, sz, pf->arg); if (err) return err; ptr += l; len -= l; } return 0; } /* convert char -> 6-bit value */ static inline uint32_t b64val(char c) { if ('A' <= c && c <= 'Z') return c - 'A' + 0; else if ('a' <= c && c <= 'z') return c - 'a' + 26; else if ('0' <= c && c <= '9') return c - '0' + 52; else if ('+' == c) return 62; else if ('/' == c) return 63; else if ('=' == c) return 1<<24; /* special trick */ else return 0; } /** * Decode a Base-64 encoded string * * @param in Input buffer * @param ilen Length of input buffer * @param out Output buffer * @param olen Size of output buffer, actual written on return * * @return 0 if success, otherwise errorcode */ int base64_decode(const char *in, size_t ilen, uint8_t *out, size_t *olen) { const char *in_end = in + ilen; const uint8_t *o = out; if (!in || !out || !olen) return EINVAL; if (*olen < 3 * (ilen/4)) return EOVERFLOW; for (;in+3 < in_end; ) { uint32_t v; v = b64val(*in++) << 18; v |= b64val(*in++) << 12; v |= b64val(*in++) << 6; v |= b64val(*in++) << 0; *out++ = v>>16; if (!(v & (1<<30))) *out++ = (v>>8) & 0xff; if (!(v & (1<<24))) *out++ = (v>>0) & 0xff; } *olen = out - o; return 0; } re-1.1.0/src/base64/mod.mk000066400000000000000000000001061373627245400151140ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += base64/b64.c re-1.1.0/src/bfcp/000077500000000000000000000000001373627245400136355ustar00rootroot00000000000000re-1.1.0/src/bfcp/attr.c000066400000000000000000000345071373627245400147640ustar00rootroot00000000000000/** * @file bfcp/attr.c BFCP Attributes * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "bfcp.h" enum { BFCP_ATTR_HDR_SIZE = 2, }; static void destructor(void *arg) { struct bfcp_attr *attr = arg; switch (attr->type) { case BFCP_ERROR_CODE: mem_deref(attr->v.errcode.details); break; case BFCP_ERROR_INFO: case BFCP_PART_PROV_INFO: case BFCP_STATUS_INFO: case BFCP_USER_DISP_NAME: case BFCP_USER_URI: mem_deref(attr->v.str); break; case BFCP_SUPPORTED_ATTRS: mem_deref(attr->v.supattr.attrv); break; case BFCP_SUPPORTED_PRIMS: mem_deref(attr->v.supprim.primv); break; default: break; } list_flush(&attr->attrl); list_unlink(&attr->le); } static int attr_encode(struct mbuf *mb, bool mand, enum bfcp_attrib type, const void *v) { const struct bfcp_reqstatus *reqstatus = v; const struct bfcp_errcode *errcode = v; const struct bfcp_supattr *supattr = v; const struct bfcp_supprim *supprim = v; const enum bfcp_priority *priority = v; const uint16_t *u16 = v; size_t start, len, i; int err; start = mb->pos; mb->pos += BFCP_ATTR_HDR_SIZE; switch (type) { case BFCP_BENEFICIARY_ID: case BFCP_FLOOR_ID: case BFCP_FLOOR_REQUEST_ID: case BFCP_BENEFICIARY_INFO: case BFCP_FLOOR_REQ_INFO: case BFCP_REQUESTED_BY_INFO: case BFCP_FLOOR_REQ_STATUS: case BFCP_OVERALL_REQ_STATUS: err = mbuf_write_u16(mb, htons(*u16)); break; case BFCP_PRIORITY: err = mbuf_write_u8(mb, *priority << 5); err |= mbuf_write_u8(mb, 0x00); break; case BFCP_REQUEST_STATUS: err = mbuf_write_u8(mb, reqstatus->status); err |= mbuf_write_u8(mb, reqstatus->qpos); break; case BFCP_ERROR_CODE: err = mbuf_write_u8(mb, errcode->code); if (errcode->details && errcode->len) err |= mbuf_write_mem(mb, errcode->details, errcode->len); break; case BFCP_ERROR_INFO: case BFCP_PART_PROV_INFO: case BFCP_STATUS_INFO: case BFCP_USER_DISP_NAME: case BFCP_USER_URI: err = mbuf_write_str(mb, v); break; case BFCP_SUPPORTED_ATTRS: for (i=0, err=0; iattrc; i++) err |= mbuf_write_u8(mb, supattr->attrv[i] << 1); break; case BFCP_SUPPORTED_PRIMS: for (i=0, err=0; iprimc; i++) err |= mbuf_write_u8(mb, supprim->primv[i]); break; default: err = EINVAL; break; } /* header */ len = mb->pos - start; mb->pos = start; err |= mbuf_write_u8(mb, (type<<1) | (mand ? 1 : 0)); err |= mbuf_write_u8(mb, len); mb->pos += (len - BFCP_ATTR_HDR_SIZE); /* padding */ while ((mb->pos - start) & 0x03) err |= mbuf_write_u8(mb, 0x00); return err; } /** * Encode BFCP Attributes with variable arguments * * @param mb Mbuf to encode into * @param attrc Number of attributes * @param ap Variable argument of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_attrs_vencode(struct mbuf *mb, unsigned attrc, va_list *ap) { unsigned i; if (!mb) return EINVAL; for (i=0; ipos; if (type == BFCP_ENCODE_HANDLER) { const struct bfcp_encode *enc = v; if (enc->ench) { err = enc->ench(mb, enc->arg); if (err) return err; } continue; } err = attr_encode(mb, type>>7, type & 0x7f, v); if (err) return err; if (subc == 0) continue; err = bfcp_attrs_vencode(mb, subc, ap); if (err) return err; /* update total length for grouped attributes */ len = mb->pos - start; mb->pos = start + 1; err = mbuf_write_u8(mb, (uint8_t)len); if (err) return err; mb->pos += (len - BFCP_ATTR_HDR_SIZE); } return 0; } /** * Encode BFCP Attributes * * @param mb Mbuf to encode into * @param attrc Number of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_attrs_encode(struct mbuf *mb, unsigned attrc, ...) { va_list ap; int err; va_start(ap, attrc); err = bfcp_attrs_vencode(mb, attrc, &ap); va_end(ap); return err; } static int attr_decode(struct bfcp_attr **attrp, struct mbuf *mb, struct bfcp_unknown_attr *uma) { struct bfcp_attr *attr; union bfcp_union *v; size_t i, start, len; int err = 0; uint8_t b; if (mbuf_get_left(mb) < BFCP_ATTR_HDR_SIZE) return EBADMSG; attr = mem_zalloc(sizeof(*attr), destructor); if (!attr) return ENOMEM; start = mb->pos; b = mbuf_read_u8(mb); attr->type = b >> 1; attr->mand = b & 1; len = mbuf_read_u8(mb); if (len < BFCP_ATTR_HDR_SIZE) goto badmsg; len -= BFCP_ATTR_HDR_SIZE; if (mbuf_get_left(mb) < len) goto badmsg; v = &attr->v; switch (attr->type) { case BFCP_BENEFICIARY_ID: case BFCP_FLOOR_ID: case BFCP_FLOOR_REQUEST_ID: if (len < 2) goto badmsg; v->u16 = ntohs(mbuf_read_u16(mb)); break; case BFCP_PRIORITY: if (len < 2) goto badmsg; v->priority = mbuf_read_u8(mb) >> 5; (void)mbuf_read_u8(mb); break; case BFCP_REQUEST_STATUS: if (len < 2) goto badmsg; v->reqstatus.status = mbuf_read_u8(mb); v->reqstatus.qpos = mbuf_read_u8(mb); break; case BFCP_ERROR_CODE: if (len < 1) goto badmsg; v->errcode.code = mbuf_read_u8(mb); v->errcode.len = len - 1; if (v->errcode.len == 0) break; v->errcode.details = mem_alloc(v->errcode.len, NULL); if (!v->errcode.details) { err = ENOMEM; goto error; } (void)mbuf_read_mem(mb, v->errcode.details, v->errcode.len); break; case BFCP_ERROR_INFO: case BFCP_PART_PROV_INFO: case BFCP_STATUS_INFO: case BFCP_USER_DISP_NAME: case BFCP_USER_URI: err = mbuf_strdup(mb, &v->str, len); break; case BFCP_SUPPORTED_ATTRS: v->supattr.attrc = len; v->supattr.attrv = mem_alloc(len*sizeof(*v->supattr.attrv), NULL); if (!v->supattr.attrv) { err = ENOMEM; goto error; } for (i=0; isupattr.attrv[i] = mbuf_read_u8(mb) >> 1; break; case BFCP_SUPPORTED_PRIMS: v->supprim.primc = len; v->supprim.primv = mem_alloc(len * sizeof(*v->supprim.primv), NULL); if (!v->supprim.primv) { err = ENOMEM; goto error; } for (i=0; isupprim.primv[i] = mbuf_read_u8(mb); break; /* grouped attributes */ case BFCP_BENEFICIARY_INFO: case BFCP_FLOOR_REQ_INFO: case BFCP_REQUESTED_BY_INFO: case BFCP_FLOOR_REQ_STATUS: case BFCP_OVERALL_REQ_STATUS: if (len < 2) goto badmsg; v->u16 = ntohs(mbuf_read_u16(mb)); err = bfcp_attrs_decode(&attr->attrl, mb, len - 2, uma); break; default: mb->pos += len; if (!attr->mand) break; if (uma && uma->typec < ARRAY_SIZE(uma->typev)) uma->typev[uma->typec++] = attr->type<<1; break; } if (err) goto error; /* padding */ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb)) ++mb->pos; *attrp = attr; return 0; badmsg: err = EBADMSG; error: mem_deref(attr); return err; } int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len, struct bfcp_unknown_attr *uma) { int err = 0; size_t end; if (!attrl || !mb || mbuf_get_left(mb) < len) return EINVAL; end = mb->end; mb->end = mb->pos + len; while (mbuf_get_left(mb) >= BFCP_ATTR_HDR_SIZE) { struct bfcp_attr *attr; err = attr_decode(&attr, mb, uma); if (err) break; list_append(attrl, &attr->le, attr); } mb->end = end; return err; } struct bfcp_attr *bfcp_attrs_find(const struct list *attrl, enum bfcp_attrib type) { struct le *le = list_head(attrl); while (le) { struct bfcp_attr *attr = le->data; le = le->next; if (attr->type == type) return attr; } return NULL; } struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl, bfcp_attr_h *h, void *arg) { struct le *le = list_head(attrl); while (le) { struct bfcp_attr *attr = le->data; le = le->next; if (h && h(attr, arg)) return attr; } return NULL; } /** * Get a BFCP sub-attribute from a BFCP attribute * * @param attr BFCP attribute * @param type Attribute type * * @return Matching BFCP attribute if found, otherwise NULL */ struct bfcp_attr *bfcp_attr_subattr(const struct bfcp_attr *attr, enum bfcp_attrib type) { if (!attr) return NULL; return bfcp_attrs_find(&attr->attrl, type); } /** * Apply a function handler to all sub-attributes in a BFCP attribute * * @param attr BFCP attribute * @param h Handler * @param arg Handler argument * * @return BFCP attribute returned by handler, or NULL */ struct bfcp_attr *bfcp_attr_subattr_apply(const struct bfcp_attr *attr, bfcp_attr_h *h, void *arg) { if (!attr) return NULL; return bfcp_attrs_apply(&attr->attrl, h, arg); } /** * Print a BFCP attribute * * @param pf Print function * @param attr BFCP attribute * * @return 0 if success, otherwise errorcode */ int bfcp_attr_print(struct re_printf *pf, const struct bfcp_attr *attr) { const union bfcp_union *v; size_t i; int err; if (!attr) return 0; err = re_hprintf(pf, "%c%-28s", attr->mand ? '*' : ' ', bfcp_attr_name(attr->type)); v = &attr->v; switch (attr->type) { case BFCP_BENEFICIARY_ID: case BFCP_FLOOR_ID: case BFCP_FLOOR_REQUEST_ID: err |= re_hprintf(pf, "%u", v->u16); break; case BFCP_PRIORITY: err |= re_hprintf(pf, "%d", v->priority); break; case BFCP_REQUEST_STATUS: err |= re_hprintf(pf, "%s (%d), qpos=%u", bfcp_reqstatus_name(v->reqstatus.status), v->reqstatus.status, v->reqstatus.qpos); break; case BFCP_ERROR_CODE: err |= re_hprintf(pf, "%d (%s)", v->errcode.code, bfcp_errcode_name(v->errcode.code)); if (v->errcode.code == BFCP_UNKNOWN_MAND_ATTR) { for (i=0; ierrcode.len; i++) { uint8_t type = v->errcode.details[i] >> 1; err |= re_hprintf(pf, " %s", bfcp_attr_name(type)); } } break; case BFCP_ERROR_INFO: case BFCP_PART_PROV_INFO: case BFCP_STATUS_INFO: case BFCP_USER_DISP_NAME: case BFCP_USER_URI: err |= re_hprintf(pf, "\"%s\"", v->str); break; case BFCP_SUPPORTED_ATTRS: err |= re_hprintf(pf, "%zu:", v->supattr.attrc); for (i=0; isupattr.attrc; i++) { const enum bfcp_attrib type = v->supattr.attrv[i]; err |= re_hprintf(pf, " %s", bfcp_attr_name(type)); } break; case BFCP_SUPPORTED_PRIMS: err |= re_hprintf(pf, "%zu:", v->supprim.primc); for (i=0; isupprim.primc; i++) { const enum bfcp_prim prim = v->supprim.primv[i]; err |= re_hprintf(pf, " %s", bfcp_prim_name(prim)); } break; /* Grouped Attributes */ case BFCP_BENEFICIARY_INFO: err |= re_hprintf(pf, "beneficiary-id=%u", v->beneficiaryid); break; case BFCP_FLOOR_REQ_INFO: err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid); break; case BFCP_REQUESTED_BY_INFO: err |= re_hprintf(pf, "requested-by-id=%u", v->reqbyid); break; case BFCP_FLOOR_REQ_STATUS: err |= re_hprintf(pf, "floor-id=%u", v->floorid); break; case BFCP_OVERALL_REQ_STATUS: err |= re_hprintf(pf, "floor-request-id=%u", v->floorreqid); break; default: err |= re_hprintf(pf, "???"); break; } return err; } int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl, unsigned level) { struct le *le; int err = 0; for (le=list_head(attrl); le; le=le->next) { const struct bfcp_attr *attr = le->data; unsigned i; for (i=0; iattrl, level + 1); } return err; } /** * Get the BFCP attribute name * * @param type BFCP attribute type * * @return String with BFCP attribute name */ const char *bfcp_attr_name(enum bfcp_attrib type) { switch (type) { case BFCP_BENEFICIARY_ID: return "BENEFICIARY-ID"; case BFCP_FLOOR_ID: return "FLOOR-ID"; case BFCP_FLOOR_REQUEST_ID: return "FLOOR-REQUEST-ID"; case BFCP_PRIORITY: return "PRIORITY"; case BFCP_REQUEST_STATUS: return "REQUEST-STATUS"; case BFCP_ERROR_CODE: return "ERROR-CODE"; case BFCP_ERROR_INFO: return "ERROR-INFO"; case BFCP_PART_PROV_INFO: return "PARTICIPANT-PROVIDED-INFO"; case BFCP_STATUS_INFO: return "STATUS-INFO"; case BFCP_SUPPORTED_ATTRS: return "SUPPORTED-ATTRIBUTES"; case BFCP_SUPPORTED_PRIMS: return "SUPPORTED-PRIMITIVES"; case BFCP_USER_DISP_NAME: return "USER-DISPLAY-NAME"; case BFCP_USER_URI: return "USER-URI"; case BFCP_BENEFICIARY_INFO: return "BENEFICIARY-INFORMATION"; case BFCP_FLOOR_REQ_INFO: return "FLOOR-REQUEST-INFORMATION"; case BFCP_REQUESTED_BY_INFO: return "REQUESTED-BY-INFORMATION"; case BFCP_FLOOR_REQ_STATUS: return "FLOOR-REQUEST-STATUS"; case BFCP_OVERALL_REQ_STATUS: return "OVERALL-REQUEST-STATUS"; default: return "???"; } } /** * Get the BFCP Request status name * * @param status Request status * * @return String with BFCP Request status name */ const char *bfcp_reqstatus_name(enum bfcp_reqstat status) { switch (status) { case BFCP_PENDING: return "Pending"; case BFCP_ACCEPTED: return "Accepted"; case BFCP_GRANTED: return "Granted"; case BFCP_DENIED: return "Denied"; case BFCP_CANCELLED: return "Cancelled"; case BFCP_RELEASED: return "Released"; case BFCP_REVOKED: return "Revoked"; default: return "???"; } } /** * Get the BFCP Error code name * * @param code BFCP Error code * * @return String with error code */ const char *bfcp_errcode_name(enum bfcp_err code) { switch (code) { case BFCP_CONF_NOT_EXIST: return "Conference does not Exist"; case BFCP_USER_NOT_EXIST: return "User does not Exist"; case BFCP_UNKNOWN_PRIM: return "Unknown Primitive"; case BFCP_UNKNOWN_MAND_ATTR: return "Unknown Mandatory Attribute"; case BFCP_UNAUTH_OPERATION: return "Unauthorized Operation"; case BFCP_INVALID_FLOOR_ID: return "Invalid Floor ID"; case BFCP_FLOOR_REQ_ID_NOT_EXIST: return "Floor Request ID Does Not Exist"; case BFCP_MAX_FLOOR_REQ_REACHED: return "You have Already Reached the Maximum Number " "of Ongoing Floor Requests for this Floor"; case BFCP_USE_TLS: return "Use TLS"; case BFCP_PARSE_ERROR: return "Unable to Parse Message"; case BFCP_USE_DTLS: return "Use DTLS"; case BFCP_UNSUPPORTED_VERSION: return "Unsupported Version"; case BFCP_BAD_LENGTH: return "Incorrect Message Length"; case BFCP_GENERIC_ERROR: return "Generic Error"; default: return "???"; } } re-1.1.0/src/bfcp/bfcp.h000066400000000000000000000023371373627245400147250ustar00rootroot00000000000000/** * @file bfcp.h Internal interface to Binary Floor Control Protocol (BFCP) * * Copyright (C) 2010 Creytiv.com */ struct bfcp_strans { enum bfcp_prim prim; uint32_t confid; uint16_t tid; uint16_t userid; }; struct bfcp_conn { struct bfcp_strans st; struct list ctransl; struct tmr tmr1; struct tmr tmr2; struct udp_sock *us; struct mbuf *mb; bfcp_recv_h *recvh; void *arg; enum bfcp_transp tp; unsigned txc; uint16_t tid; }; /* attributes */ int bfcp_attrs_decode(struct list *attrl, struct mbuf *mb, size_t len, struct bfcp_unknown_attr *uma); struct bfcp_attr *bfcp_attrs_find(const struct list *attrl, enum bfcp_attrib type); struct bfcp_attr *bfcp_attrs_apply(const struct list *attrl, bfcp_attr_h *h, void *arg); int bfcp_attrs_print(struct re_printf *pf, const struct list *attrl, unsigned level); /* connection */ int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb); /* request */ bool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg); int bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver, enum bfcp_prim prim, uint32_t confid, uint16_t userid, bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap); re-1.1.0/src/bfcp/conn.c000066400000000000000000000047161373627245400147460ustar00rootroot00000000000000/** * @file bfcp/conn.c BFCP Connection * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "bfcp.h" static void destructor(void *arg) { struct bfcp_conn *bc = arg; list_flush(&bc->ctransl); tmr_cancel(&bc->tmr1); tmr_cancel(&bc->tmr2); mem_deref(bc->us); mem_deref(bc->mb); } static bool strans_cmp(const struct bfcp_strans *st, const struct bfcp_msg *msg) { if (st->tid != msg->tid) return false; if (st->prim != msg->prim) return false; if (st->confid != msg->confid) return false; if (st->userid != msg->userid) return false; return true; } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct bfcp_conn *bc = arg; struct bfcp_msg *msg; int err; err = bfcp_msg_decode(&msg, mb); if (err) return; msg->src = *src; if (bfcp_handle_response(bc, msg)) goto out; if (bc->mb && strans_cmp(&bc->st, msg)) { (void)bfcp_send(bc, &msg->src, bc->mb); goto out; } if (bc->recvh) bc->recvh(msg, bc->arg); out: mem_deref(msg); } /** * Create BFCP connection * * @param bcp Pointer to BFCP connection * @param tp BFCP Transport type * @param laddr Optional listening address/port * @param tls TLS Context (optional) * @param recvh Receive handler * @param arg Receive handler argument * * @return 0 if success, otherwise errorcode */ int bfcp_listen(struct bfcp_conn **bcp, enum bfcp_transp tp, struct sa *laddr, struct tls *tls, bfcp_recv_h *recvh, void *arg) { struct bfcp_conn *bc; int err; (void)tls; if (!bcp) return EINVAL; bc = mem_zalloc(sizeof(*bc), destructor); if (!bc) return ENOMEM; bc->tp = tp; bc->recvh = recvh; bc->arg = arg; switch (bc->tp) { case BFCP_UDP: err = udp_listen(&bc->us, laddr, udp_recv_handler, bc); if (err) goto out; if (laddr) { err = udp_local_get(bc->us, laddr); if (err) goto out; } break; default: err = ENOSYS; goto out; } out: if (err) mem_deref(bc); else *bcp = bc; return err; } int bfcp_send(struct bfcp_conn *bc, const struct sa *dst, struct mbuf *mb) { if (!bc || !dst || !mb) return EINVAL; switch (bc->tp) { case BFCP_UDP: return udp_send(bc->us, dst, mb); default: return ENOSYS; } } void *bfcp_sock(const struct bfcp_conn *bc) { return bc ? bc->us : NULL; } re-1.1.0/src/bfcp/mod.mk000066400000000000000000000002301373627245400147400ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += bfcp/attr.c SRCS += bfcp/conn.c SRCS += bfcp/msg.c SRCS += bfcp/reply.c SRCS += bfcp/request.c re-1.1.0/src/bfcp/msg.c000066400000000000000000000141041373627245400145670ustar00rootroot00000000000000/** * @file bfcp/msg.c BFCP Message * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "bfcp.h" enum { BFCP_HDR_SIZE = 12, }; static void destructor(void *arg) { struct bfcp_msg *msg = arg; list_flush(&msg->attrl); } static int hdr_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim, uint16_t len, uint32_t confid, uint16_t tid, uint16_t userid) { int err; err = mbuf_write_u8(mb, (ver << 5) | ((r ? 1 : 0) << 4)); err |= mbuf_write_u8(mb, prim); err |= mbuf_write_u16(mb, htons(len)); err |= mbuf_write_u32(mb, htonl(confid)); err |= mbuf_write_u16(mb, htons(tid)); err |= mbuf_write_u16(mb, htons(userid)); return err; } static int hdr_decode(struct bfcp_msg *msg, struct mbuf *mb) { uint8_t b; if (mbuf_get_left(mb) < BFCP_HDR_SIZE) return ENODATA; b = mbuf_read_u8(mb); msg->ver = b >> 5; msg->r = (b >> 4) & 1; msg->f = (b >> 3) & 1; msg->prim = mbuf_read_u8(mb); msg->len = ntohs(mbuf_read_u16(mb)); msg->confid = ntohl(mbuf_read_u32(mb)); msg->tid = ntohs(mbuf_read_u16(mb)); msg->userid = ntohs(mbuf_read_u16(mb)); if (msg->ver != BFCP_VER1 && msg->ver != BFCP_VER2) return EBADMSG; /* fragmentation not supported */ if (msg->f) return ENOSYS; if (mbuf_get_left(mb) < (size_t)(4*msg->len)) return ENODATA; return 0; } /** * Encode a BFCP message with variable arguments * * @param mb Mbuf to encode into * @param ver Protocol version * @param r Transaction responder flag * @param prim BFCP Primitive * @param confid Conference ID * @param tid Transaction ID * @param userid User ID * @param attrc Number of attributes * @param ap Variable argument of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_msg_vencode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim, uint32_t confid, uint16_t tid, uint16_t userid, unsigned attrc, va_list *ap) { size_t start, len; int err; if (!mb) return EINVAL; start = mb->pos; mb->pos += BFCP_HDR_SIZE; err = bfcp_attrs_vencode(mb, attrc, ap); if (err) return err; /* header */ len = mb->pos - start - BFCP_HDR_SIZE; mb->pos = start; err = hdr_encode(mb, ver, r, prim, (uint16_t)(len/4), confid, tid, userid); mb->pos += len; return err; } /** * Encode a BFCP message * * @param mb Mbuf to encode into * @param ver Protocol version * @param r Transaction responder flag * @param prim BFCP Primitive * @param confid Conference ID * @param tid Transaction ID * @param userid User ID * @param attrc Number of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_msg_encode(struct mbuf *mb, uint8_t ver, bool r, enum bfcp_prim prim, uint32_t confid, uint16_t tid, uint16_t userid, unsigned attrc, ...) { va_list ap; int err; va_start(ap, attrc); err = bfcp_msg_vencode(mb, ver, r, prim, confid, tid, userid, attrc, &ap); va_end(ap); return err; } /** * Decode a BFCP message from a buffer * * @param msgp Pointer to allocated and decoded BFCP message * @param mb Mbuf to decode from * * @return 0 if success, otherwise errorcode */ int bfcp_msg_decode(struct bfcp_msg **msgp, struct mbuf *mb) { struct bfcp_msg *msg; size_t start; int err; if (!msgp || !mb) return EINVAL; msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) return ENOMEM; start = mb->pos; err = hdr_decode(msg, mb); if (err) { mb->pos = start; goto out; } err = bfcp_attrs_decode(&msg->attrl, mb, 4*msg->len, &msg->uma); if (err) goto out; out: if (err) mem_deref(msg); else *msgp = msg; return err; } /** * Get a BFCP attribute from a BFCP message * * @param msg BFCP message * @param type Attribute type * * @return Matching BFCP attribute if found, otherwise NULL */ struct bfcp_attr *bfcp_msg_attr(const struct bfcp_msg *msg, enum bfcp_attrib type) { if (!msg) return NULL; return bfcp_attrs_find(&msg->attrl, type); } /** * Apply a function handler to all attributes in a BFCP message * * @param msg BFCP message * @param h Handler * @param arg Handler argument * * @return BFCP attribute returned by handler, or NULL */ struct bfcp_attr *bfcp_msg_attr_apply(const struct bfcp_msg *msg, bfcp_attr_h *h, void *arg) { if (!msg) return NULL; return bfcp_attrs_apply(&msg->attrl, h, arg); } /** * Print a BFCP message * * @param pf Print function * @param msg BFCP message * * @return 0 if success, otherwise errorcode */ int bfcp_msg_print(struct re_printf *pf, const struct bfcp_msg *msg) { int err; if (!msg) return 0; err = re_hprintf(pf, "%s (confid=%u tid=%u userid=%u)\n", bfcp_prim_name(msg->prim), msg->confid, msg->tid, msg->userid); err |= bfcp_attrs_print(pf, &msg->attrl, 0); return err; } /** * Get the BFCP primitive name * * @param prim BFCP primitive * * @return String with BFCP primitive name */ const char *bfcp_prim_name(enum bfcp_prim prim) { switch (prim) { case BFCP_FLOOR_REQUEST: return "FloorRequest"; case BFCP_FLOOR_RELEASE: return "FloorRelease"; case BFCP_FLOOR_REQUEST_QUERY: return "FloorRequestQuery"; case BFCP_FLOOR_REQUEST_STATUS: return "FloorRequestStatus"; case BFCP_USER_QUERY: return "UserQuery"; case BFCP_USER_STATUS: return "UserStatus"; case BFCP_FLOOR_QUERY: return "FloorQuery"; case BFCP_FLOOR_STATUS: return "FloorStatus"; case BFCP_CHAIR_ACTION: return "ChairAction"; case BFCP_CHAIR_ACTION_ACK: return "ChairActionAck"; case BFCP_HELLO: return "Hello"; case BFCP_HELLO_ACK: return "HelloAck"; case BFCP_ERROR: return "Error"; case BFCP_FLOOR_REQ_STATUS_ACK: return "FloorRequestStatusAck"; case BFCP_FLOOR_STATUS_ACK: return "FloorStatusAck"; case BFCP_GOODBYE: return "Goodbye"; case BFCP_GOODBYE_ACK: return "GoodbyeAck"; default: return "???"; } } re-1.1.0/src/bfcp/reply.c000066400000000000000000000044501373627245400151370ustar00rootroot00000000000000/** * @file bfcp/reply.c BFCP Reply * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "bfcp.h" enum { BFCP_T2 = 10000, }; static void tmr_handler(void *arg) { struct bfcp_conn *bc = arg; bc->mb = mem_deref(bc->mb); } /** * Send a BFCP response * * @param bc BFCP connection * @param req BFCP request message * @param prim BFCP Primitive * @param attrc Number of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_reply(struct bfcp_conn *bc, const struct bfcp_msg *req, enum bfcp_prim prim, unsigned attrc, ...) { va_list ap; int err; if (!bc || !req) return EINVAL; bc->mb = mem_deref(bc->mb); tmr_cancel(&bc->tmr2); bc->mb = mbuf_alloc(64); if (!bc->mb) return ENOMEM; va_start(ap, attrc); err = bfcp_msg_vencode(bc->mb, req->ver, true, prim, req->confid, req->tid, req->userid, attrc, &ap); va_end(ap); if (err) goto out; bc->mb->pos = 0; err = bfcp_send(bc, &req->src, bc->mb); if (err) goto out; bc->st.prim = req->prim; bc->st.confid = req->confid; bc->st.tid = req->tid; bc->st.userid = req->userid; tmr_start(&bc->tmr2, BFCP_T2, tmr_handler, bc); out: if (err) bc->mb = mem_deref(bc->mb); return err; } /** * Send a BFCP error response with details * * @param bc BFCP connection * @param req BFCP request message * @param code Error code * @param details Error details * @param len Details length * * @return 0 if success, otherwise errorcode */ int bfcp_edreply(struct bfcp_conn *bc, const struct bfcp_msg *req, enum bfcp_err code, const uint8_t *details, size_t len) { struct bfcp_errcode errcode; errcode.code = code; errcode.details = (uint8_t *)details; errcode.len = len; return bfcp_reply(bc, req, BFCP_ERROR, 1, BFCP_ERROR_CODE, 0, &errcode); } /** * Send a BFCP error response * * @param bc BFCP connection * @param req BFCP request message * @param code Error code * * @return 0 if success, otherwise errorcode */ int bfcp_ereply(struct bfcp_conn *bc, const struct bfcp_msg *req, enum bfcp_err code) { return bfcp_edreply(bc, req, code, NULL, 0); } re-1.1.0/src/bfcp/request.c000066400000000000000000000105551373627245400154770ustar00rootroot00000000000000/** * @file bfcp/request.c BFCP Request * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "bfcp.h" enum { BFCP_T1 = 500, BFCP_TXC = 4, }; struct bfcp_ctrans { struct le le; struct sa dst; struct mbuf *mb; bfcp_resp_h *resph; void *arg; uint32_t confid; uint16_t userid; uint16_t tid; }; static void tmr_handler(void *arg); static void dummy_resp_handler(int err, const struct bfcp_msg *msg, void *arg) { (void)err; (void)msg; (void)arg; } static void destructor(void *arg) { struct bfcp_ctrans *ct = arg; list_unlink(&ct->le); mem_deref(ct->mb); } static void dispatch(struct bfcp_conn *bc) { struct le *le = bc->ctransl.head; while (le) { struct bfcp_ctrans *ct = le->data; int err; le = le->next; err = bfcp_send(bc, &ct->dst, ct->mb); if (err) { ct->resph(err, NULL, ct->arg); mem_deref(ct); continue; } tmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc); bc->txc = 1; break; } } static void tmr_handler(void *arg) { struct bfcp_conn *bc = arg; struct bfcp_ctrans *ct; uint32_t timeout; int err; ct = list_ledata(bc->ctransl.head); if (!ct) return; timeout = BFCP_T1<txc; if (++bc->txc > BFCP_TXC) { err = ETIMEDOUT; goto out; } err = bfcp_send(bc, &ct->dst, ct->mb); if (err) goto out; tmr_start(&bc->tmr1, timeout, tmr_handler, bc); return; out: ct->resph(err, NULL, ct->arg); mem_deref(ct); dispatch(bc); } bool bfcp_handle_response(struct bfcp_conn *bc, const struct bfcp_msg *msg) { struct bfcp_ctrans *ct; if (!bc || !msg) return false; ct = list_ledata(bc->ctransl.head); if (!ct) return false; if (msg->tid != ct->tid) return false; if (msg->confid != ct->confid) return false; if (msg->userid != ct->userid) return false; tmr_cancel(&bc->tmr1); ct->resph(0, msg, ct->arg); mem_deref(ct); dispatch(bc); return true; } int bfcp_vrequest(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver, enum bfcp_prim prim, uint32_t confid, uint16_t userid, bfcp_resp_h *resph, void *arg, unsigned attrc, va_list *ap) { struct bfcp_ctrans *ct; int err; if (!bc || !dst) return EINVAL; ct = mem_zalloc(sizeof(*ct), destructor); if (!ct) return ENOMEM; if (bc->tid == 0) bc->tid = 1; ct->dst = *dst; ct->confid = confid; ct->userid = userid; ct->tid = bc->tid++; ct->resph = resph ? resph : dummy_resp_handler; ct->arg = arg; ct->mb = mbuf_alloc(128); if (!ct->mb) { err = ENOMEM; goto out; } err = bfcp_msg_vencode(ct->mb, ver, false, prim, confid, ct->tid, userid, attrc, ap); if (err) goto out; ct->mb->pos = 0; if (!bc->ctransl.head) { err = bfcp_send(bc, &ct->dst, ct->mb); if (err) goto out; tmr_start(&bc->tmr1, BFCP_T1, tmr_handler, bc); bc->txc = 1; } list_append(&bc->ctransl, &ct->le, ct); out: if (err) mem_deref(ct); return err; } /** * Send a BFCP request * * @param bc BFCP connection * @param dst Destination address * @param ver BFCP Version * @param prim BFCP Primitive * @param confid Conference ID * @param userid User ID * @param resph Response handler * @param arg Response handler argument * @param attrc Number of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_request(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver, enum bfcp_prim prim, uint32_t confid, uint16_t userid, bfcp_resp_h *resph, void *arg, unsigned attrc, ...) { va_list ap; int err; va_start(ap, attrc); err = bfcp_vrequest(bc, dst, ver, prim, confid, userid, resph, arg, attrc, &ap); va_end(ap); return err; } /** * Send a BFCP notification/subsequent response * * @param bc BFCP connection * @param dst Destination address * @param ver BFCP Version * @param prim BFCP Primitive * @param confid Conference ID * @param userid User ID * @param attrc Number of attributes * * @return 0 if success, otherwise errorcode */ int bfcp_notify(struct bfcp_conn *bc, const struct sa *dst, uint8_t ver, enum bfcp_prim prim, uint32_t confid, uint16_t userid, unsigned attrc, ...) { va_list ap; int err; va_start(ap, attrc); err = bfcp_vrequest(bc, dst, ver, prim, confid, userid, NULL, NULL, attrc, &ap); va_end(ap); return err; } re-1.1.0/src/conf/000077500000000000000000000000001373627245400136505ustar00rootroot00000000000000re-1.1.0/src/conf/conf.c000066400000000000000000000124431373627245400147450ustar00rootroot00000000000000/** * @file conf.c Configuration file parser * * Copyright (C) 2010 Creytiv.com */ #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_IO_H #include #endif #include #include #include #include #include #ifdef WIN32 #define open _open #define read _read #define close _close #endif /** * Defines a Configuration state. The configuration data is stored in a * linear buffer which can be used for reading key-value pairs of * configuration data. The config data can be strings or numeric values. */ struct conf { struct mbuf *mb; }; static int load_file(struct mbuf *mb, const char *filename) { int err = 0, fd = open(filename, O_RDONLY); if (fd < 0) return errno; for (;;) { uint8_t buf[1024]; const ssize_t n = read(fd, (void *)buf, sizeof(buf)); if (n < 0) { err = errno; break; } else if (n == 0) break; err |= mbuf_write_mem(mb, buf, n); } (void)close(fd); return err; } static void conf_destructor(void *data) { struct conf *conf = data; mem_deref(conf->mb); } /** * Load configuration from file * * @param confp Configuration object to be allocated * @param filename Name of configuration file * * @return 0 if success, otherwise errorcode */ int conf_alloc(struct conf **confp, const char *filename) { struct conf *conf; int err = 0; if (!confp) return EINVAL; conf = mem_zalloc(sizeof(*conf), conf_destructor); if (!conf) return ENOMEM; conf->mb = mbuf_alloc(1024); if (!conf->mb) { err = ENOMEM; goto out; } err |= mbuf_write_u8(conf->mb, '\n'); if (filename) err |= load_file(conf->mb, filename); out: if (err) mem_deref(conf); else *confp = conf; return err; } /** * Allocate configuration from a buffer * * @param confp Configuration object to be allocated * @param buf Buffer containing configuration * @param sz Size of configuration buffer * * @return 0 if success, otherwise errorcode */ int conf_alloc_buf(struct conf **confp, const uint8_t *buf, size_t sz) { struct conf *conf; int err; err = conf_alloc(&conf, NULL); if (err) return err; err = mbuf_write_mem(conf->mb, buf, sz); if (err) mem_deref(conf); else *confp = conf; return err; } /** * Get the value of a configuration item PL string * * @param conf Configuration object * @param name Name of config item key * @param pl Value of config item, if present * * @return 0 if success, otherwise errorcode */ int conf_get(const struct conf *conf, const char *name, struct pl *pl) { char expr[512]; struct pl spl; if (!conf || !name || !pl) return EINVAL; spl.p = (const char *)conf->mb->buf; spl.l = conf->mb->end; (void)re_snprintf(expr, sizeof(expr), "[\r\n]+[ \t]*%s[ \t]+[~ \t\r\n]+", name); return re_regex(spl.p, spl.l, expr, NULL, NULL, NULL, pl); } /** * Get the value of a configuration item string * * @param conf Configuration object * @param name Name of config item key * @param str Value of config item, if present * @param size Size of string to store value * * @return 0 if success, otherwise errorcode */ int conf_get_str(const struct conf *conf, const char *name, char *str, size_t size) { struct pl pl; int err; if (!conf || !name || !str || !size) return EINVAL; err = conf_get(conf, name, &pl); if (err) return err; return pl_strcpy(&pl, str, size); } /** * Get the numeric value of a configuration item * * @param conf Configuration object * @param name Name of config item key * @param num Returned numeric value of config item, if present * * @return 0 if success, otherwise errorcode */ int conf_get_u32(const struct conf *conf, const char *name, uint32_t *num) { struct pl pl; int err; if (!conf || !name || !num) return EINVAL; err = conf_get(conf, name, &pl); if (err) return err; *num = pl_u32(&pl); return 0; } /** * Get the boolean value of a configuration item * * @param conf Configuration object * @param name Name of config item key * @param val Returned boolean value of config item, if present * * @return 0 if success, otherwise errorcode */ int conf_get_bool(const struct conf *conf, const char *name, bool *val) { struct pl pl; int err; if (!conf || !name || !val) return EINVAL; err = conf_get(conf, name, &pl); if (err) return err; if (!pl_strcasecmp(&pl, "true")) *val = true; else if (!pl_strcasecmp(&pl, "yes")) *val = true; else if (!pl_strcasecmp(&pl, "1")) *val = true; else *val = false; return 0; } /** * Apply a function handler to all config items of a certain key * * @param conf Configuration object * @param name Name of config item key * @param ch Config item handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int conf_apply(const struct conf *conf, const char *name, conf_h *ch, void *arg) { char expr[512]; struct pl pl, val; int err = 0; if (!conf || !name || !ch) return EINVAL; pl.p = (const char *)conf->mb->buf; pl.l = conf->mb->end; (void)re_snprintf(expr, sizeof(expr), "[\r\n]+[ \t]*%s[ \t]+[~ \t\r\n]+", name); while (!re_regex(pl.p, pl.l, expr, NULL, NULL, NULL, &val)) { err = ch(&val, arg); if (err) break; pl.l -= val.p + val.l - pl.p; pl.p = val.p + val.l; } return err; } re-1.1.0/src/conf/mod.mk000066400000000000000000000001051373627245400147540ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += conf/conf.c re-1.1.0/src/crc32/000077500000000000000000000000001373627245400136375ustar00rootroot00000000000000re-1.1.0/src/crc32/crc32.c000066400000000000000000000133231373627245400147210ustar00rootroot00000000000000/** * @file crc32.c CRC32 Implementation * * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or * code or tables extracted from it, as desired without restriction. */ /* * First, the polynomial itself and its table of feedback terms. The * polynomial is * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 * * Note that we take it "backwards" and put the highest-order term in * the lowest-order bit. The X^32 term is "implied"; the LSB is the * X^31 term, etc. The X^0 term (usually shown as "+1") results in * the MSB being 1 * * Note that the usual hardware shift register implementation, which * is what we're using (we're merely optimizing it by doing eight-bit * chunks at a time) shifts bits into the lowest-order term. In our * implementation, that means shifting towards the right. Why do we * do it this way? Because the calculated CRC must be transmitted in * order from highest-order term to lowest-order term. UARTs transmit * characters in order from LSB to MSB. By storing the CRC this way * we hand it to the UART in the order low-byte to high-byte; the UART * sends each low-bit to hight-bit; and the result is transmission bit * by bit from highest- to lowest-order term without requiring any bit * shuffling on our part. Reception works similarly * * The feedback terms table consists of 256, 32-bit entries. Notes * * The table can be generated at runtime if desired; code to do so * is shown later. It might not be obvious, but the feedback * terms simply represent the results of eight shift/xor opera * tions for all combinations of data and CRC register values * * The values must be right-shifted by eight bits by the "updcrc * logic; the shift must be unsigned (bring in zeroes). On some * hardware you could probably optimize the shift in assembler by * using byte-swap instructions * polynomial $edb88320 * * * CRC32 code derived from work by Gary S. Brown. */ #include #include static const uint32_t crc32_tab[] = { 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d }; /** * A function that calculates the CRC-32 based on the table above is * given below for documentation purposes. An equivalent implementation * of this function that's actually used in the kernel can be found * in sys/libkern.h, where it can be inlined. * * @param crc Initial CRC value * @param buf Buffer to generate CRC from * @param size Number of bytes in buffer * * @return CRC value */ uint32_t crc32(uint32_t crc, const void *buf, uint32_t size) { const uint8_t *p = buf; crc = ~crc; while (size--) crc = crc32_tab[(crc ^ *p++) & 0xff] ^ (crc >> 8); return crc ^ ~0U; } re-1.1.0/src/crc32/mod.mk000066400000000000000000000001411373627245400147430ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # ifeq ($(USE_ZLIB),) SRCS += crc32/crc32.c endif re-1.1.0/src/dbg/000077500000000000000000000000001373627245400134575ustar00rootroot00000000000000re-1.1.0/src/dbg/dbg.c000066400000000000000000000122711373627245400143620ustar00rootroot00000000000000/** * @file dbg.c Debug printing * * Copyright (C) 2010 Creytiv.com */ #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_PTHREAD #include #include #endif #include #include #include #include #include #define DEBUG_MODULE "dbg" #define DEBUG_LEVEL 0 #include /** Debug configuration */ static struct { uint64_t tick; /**< Init ticks */ int level; /**< Current debug level */ enum dbg_flags flags; /**< Debug flags */ dbg_print_h *ph; /**< Optional print handler */ void *arg; /**< Handler argument */ FILE *f; /**< Logfile */ #ifdef HAVE_PTHREAD pthread_mutex_t mutex; /**< Thread locking */ #endif } dbg = { 0, DBG_INFO, DBG_ANSI, NULL, NULL, NULL, #ifdef HAVE_PTHREAD PTHREAD_MUTEX_INITIALIZER, #endif }; #ifdef HAVE_PTHREAD static inline void dbg_lock(void) { pthread_mutex_lock(&dbg.mutex); } static inline void dbg_unlock(void) { pthread_mutex_unlock(&dbg.mutex); } #else #define dbg_lock() /**< Stub */ #define dbg_unlock() /**< Stub */ #endif /** * Initialise debug printing * * @param level Debug level * @param flags Debug flags */ void dbg_init(int level, enum dbg_flags flags) { dbg.tick = tmr_jiffies(); dbg.level = level; dbg.flags = flags; } /** * Close debugging */ void dbg_close(void) { if (dbg.f) { (void)fclose(dbg.f); dbg.f = NULL; } } /** * Set debug logfile * * @param name Name of the logfile, NULL to close * * @return 0 if success, otherwise errorcode */ int dbg_logfile_set(const char *name) { time_t t; dbg_close(); if (!name) return 0; dbg.f = fopen(name, "a+"); if (!dbg.f) return errno; (void)time(&t); (void)re_fprintf(dbg.f, "\n===== Log Started: %s", ctime(&t)); (void)fflush(dbg.f); return 0; } /** * Set optional debug print handler * * @param ph Print handler * @param arg Handler argument */ void dbg_handler_set(dbg_print_h *ph, void *arg) { dbg.ph = ph; dbg.arg = arg; } /* NOTE: This function should not allocate memory */ static void dbg_vprintf(int level, const char *fmt, va_list ap) { if (level > dbg.level) return; /* Print handler? */ if (dbg.ph) return; dbg_lock(); if (dbg.flags & DBG_ANSI) { switch (level) { case DBG_WARNING: (void)re_fprintf(stderr, "\x1b[31m"); /* Red */ break; case DBG_NOTICE: (void)re_fprintf(stderr, "\x1b[33m"); /* Yellow */ break; case DBG_INFO: (void)re_fprintf(stderr, "\x1b[32m"); /* Green */ break; default: break; } } if (dbg.flags & DBG_TIME) { const uint64_t ticks = tmr_jiffies(); if (0 == dbg.tick) dbg.tick = tmr_jiffies(); (void)re_fprintf(stderr, "[%09llu] ", ticks - dbg.tick); } (void)re_vfprintf(stderr, fmt, ap); if (dbg.flags & DBG_ANSI && level < DBG_DEBUG) (void)re_fprintf(stderr, "\x1b[;m"); dbg_unlock(); } /* Formatted output to print handler and/or logfile */ static void dbg_fmt_vprintf(int level, const char *fmt, va_list ap) { char buf[256]; int len; if (level > dbg.level) return; if (!dbg.ph && !dbg.f) return; dbg_lock(); len = re_vsnprintf(buf, sizeof(buf), fmt, ap); if (len <= 0) goto out; /* Print handler? */ if (dbg.ph) { dbg.ph(level, buf, len, dbg.arg); } /* Output to file */ if (dbg.f) { if (fwrite(buf, 1, len, dbg.f) > 0) (void)fflush(dbg.f); } out: dbg_unlock(); } /** * Print a formatted debug message * * @param level Debug level * @param fmt Formatted string */ void dbg_printf(int level, const char *fmt, ...) { va_list ap; va_start(ap, fmt); dbg_vprintf(level, fmt, ap); va_end(ap); va_start(ap, fmt); dbg_fmt_vprintf(level, fmt, ap); va_end(ap); } /** * Print a formatted debug message to /dev/null * * @param fmt Formatted string */ void dbg_noprintf(const char *fmt, ...) { (void)fmt; } /** * Print a formatted warning message * * @param fmt Formatted string */ void dbg_warning(const char *fmt, ...) { va_list ap; va_start(ap, fmt); dbg_vprintf(DBG_WARNING, fmt, ap); va_end(ap); va_start(ap, fmt); dbg_fmt_vprintf(DBG_WARNING, fmt, ap); va_end(ap); } /** * Print a formatted notice message * * @param fmt Formatted string */ void dbg_notice(const char *fmt, ...) { va_list ap; va_start(ap, fmt); dbg_vprintf(DBG_NOTICE, fmt, ap); va_end(ap); va_start(ap, fmt); dbg_fmt_vprintf(DBG_NOTICE, fmt, ap); va_end(ap); } /** * Print a formatted info message * * @param fmt Formatted string */ void dbg_info(const char *fmt, ...) { va_list ap; va_start(ap, fmt); dbg_vprintf(DBG_INFO, fmt, ap); va_end(ap); va_start(ap, fmt); dbg_fmt_vprintf(DBG_INFO, fmt, ap); va_end(ap); } /** * Get the name of the debug level * * @param level Debug level * * @return String with debug level name */ const char *dbg_level_str(int level) { switch (level) { case DBG_EMERG: return "EMERGENCY"; case DBG_ALERT: return "ALERT"; case DBG_CRIT: return "CRITICAL"; case DBG_ERR: return "ERROR"; case DBG_WARNING: return "WARNING"; case DBG_NOTICE: return "NOTICE"; case DBG_INFO: return "INFO"; case DBG_DEBUG: return "DEBUG"; default: return "???"; } } re-1.1.0/src/dbg/mod.mk000066400000000000000000000001031373627245400145610ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += dbg/dbg.c re-1.1.0/src/dns/000077500000000000000000000000001373627245400135075ustar00rootroot00000000000000re-1.1.0/src/dns/client.c000066400000000000000000000411701373627245400151340ustar00rootroot00000000000000/** * @file dns/client.c DNS Client * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "dnsc" #define DEBUG_LEVEL 5 #include enum { NTX_MAX = 20, QUERY_HASH_SIZE = 16, TCP_HASH_SIZE = 2, CONN_TIMEOUT = 10 * 1000, IDLE_TIMEOUT = 30 * 1000, SRVC_MAX = 32, }; struct tcpconn { struct le le; struct list ql; struct tmr tmr; struct sa srv; struct tcp_conn *conn; struct mbuf *mb; bool connected; uint16_t flen; struct dnsc *dnsc; /* parent */ }; struct dns_query { struct le le; struct le le_tc; struct tmr tmr; struct mbuf mb; struct list rrlv[3]; char *name; const struct sa *srvv; const uint32_t *srvc; struct tcpconn *tc; struct dnsc *dnsc; /* parent */ struct dns_query **qp; /* app ref */ uint32_t ntx; uint16_t id; uint16_t type; uint16_t dnsclass; uint8_t opcode; dns_query_h *qh; void *arg; }; struct dnsquery { struct dnshdr hdr; char *name; uint16_t type; uint16_t dnsclass; }; struct dnsc { struct dnsc_conf conf; struct hash *ht_query; struct hash *ht_tcpconn; struct udp_sock *us; struct udp_sock *us6; struct sa srvv[SRVC_MAX]; uint32_t srvc; }; static const struct dnsc_conf default_conf = { QUERY_HASH_SIZE, TCP_HASH_SIZE, CONN_TIMEOUT, IDLE_TIMEOUT, }; static void tcpconn_close(struct tcpconn *tc, int err); static int send_tcp(struct dns_query *q); static void udp_timeout_handler(void *arg); static bool rr_unlink_handler(struct le *le, void *arg) { struct dnsrr *rr = le->data; (void)arg; list_unlink(&rr->le_priv); mem_deref(rr); return false; } static void query_abort(struct dns_query *q) { if (q->tc) { list_unlink(&q->le_tc); q->tc = mem_deref(q->tc); } tmr_cancel(&q->tmr); hash_unlink(&q->le); } static void query_destructor(void *data) { struct dns_query *q = data; uint32_t i; query_abort(q); mbuf_reset(&q->mb); mem_deref(q->name); for (i=0; irrlv); i++) (void)list_apply(&q->rrlv[i], true, rr_unlink_handler, NULL); } static void query_handler(struct dns_query *q, int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl) { /* deref here - before calling handler */ if (q->qp) *q->qp = NULL; /* The handler must only be called _once_ */ if (q->qh) { q->qh(err, hdr, ansl, authl, addl, q->arg); q->qh = NULL; } /* in case we have more (than one) q refs */ query_abort(q); } static bool query_close_handler(struct le *le, void *arg) { struct dns_query *q = le->data; (void)arg; query_handler(q, ECONNABORTED, NULL, NULL, NULL, NULL); mem_deref(q); return false; } static bool query_cmp_handler(struct le *le, void *arg) { struct dns_query *q = le->data; struct dnsquery *dq = arg; if (q->id != dq->hdr.id) return false; if (q->opcode != dq->hdr.opcode) return false; if (q->type != dq->type) return false; if (q->dnsclass != dq->dnsclass) return false; if (str_casecmp(q->name, dq->name)) return false; return true; } static int reply_recv(struct dnsc *dnsc, struct mbuf *mb) { struct dns_query *q = NULL; uint32_t i, j, nv[3]; struct dnsquery dq; int err = 0; if (!dnsc || !mb) return EINVAL; dq.name = NULL; if (dns_hdr_decode(mb, &dq.hdr) || !dq.hdr.qr) { err = EBADMSG; goto out; } err = dns_dname_decode(mb, &dq.name, 0); if (err) goto out; if (mbuf_get_left(mb) < 4) { err = EBADMSG; goto out; } dq.type = ntohs(mbuf_read_u16(mb)); dq.dnsclass = ntohs(mbuf_read_u16(mb)); q = list_ledata(hash_lookup(dnsc->ht_query, hash_joaat_str_ci(dq.name), query_cmp_handler, &dq)); if (!q) { err = ENOENT; goto out; } /* try next server */ if (dq.hdr.rcode == DNS_RCODE_SRV_FAIL && q->ntx < *q->srvc) { if (!q->tc) /* try next UDP server immediately */ tmr_start(&q->tmr, 0, udp_timeout_handler, q); err = EPROTO; goto out; } nv[0] = dq.hdr.nans; nv[1] = dq.hdr.nauth; nv[2] = dq.hdr.nadd; for (i=0; irrlv[i], &rr->le_priv, rr); } } if (q->type == DNS_QTYPE_AXFR) { struct dnsrr *rrh, *rrt; rrh = list_ledata(list_head(&q->rrlv[0])); rrt = list_ledata(list_tail(&q->rrlv[0])); /* Wait for last AXFR reply with terminating SOA record */ if (dq.hdr.rcode == DNS_RCODE_OK && dq.hdr.nans > 0 && (!rrt || rrt->type != DNS_TYPE_SOA || rrh == rrt)) { DEBUG_INFO("waiting for last SOA record in reply\n"); goto out; } } query_handler(q, 0, &dq.hdr, &q->rrlv[0], &q->rrlv[1], &q->rrlv[2]); mem_deref(q); out: mem_deref(dq.name); return err; } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { (void)src; (void)reply_recv(arg, mb); } static void tcp_recv_handler(struct mbuf *mbrx, void *arg) { struct tcpconn *tc = arg; struct mbuf *mb = tc->mb; int err = 0; size_t n; next: /* frame length */ if (!tc->flen) { n = min(2 - mb->end, mbuf_get_left(mbrx)); err = mbuf_write_mem(mb, mbuf_buf(mbrx), n); if (err) goto error; mbrx->pos += n; if (mb->end < 2) return; mb->pos = 0; tc->flen = ntohs(mbuf_read_u16(mb)); mb->pos = 0; mb->end = 0; } /* content */ n = min(tc->flen - mb->end, mbuf_get_left(mbrx)); err = mbuf_write_mem(mb, mbuf_buf(mbrx), n); if (err) goto error; mbrx->pos += n; if (mb->end < tc->flen) return; mb->pos = 0; err = reply_recv(tc->dnsc, mb); if (err) goto error; /* reset tcp buffer */ tc->flen = 0; mb->pos = 0; mb->end = 0; /* more data ? */ if (mbuf_get_left(mbrx) > 0) { DEBUG_INFO("%u bytes of tcp data left\n", mbuf_get_left(mbrx)); goto next; } return; error: tcpconn_close(tc, err); } static void tcpconn_timeout_handler(void *arg) { struct tcpconn *tc = arg; DEBUG_NOTICE("tcp (%J) %s timeout \n", &tc->srv, tc->connected ? "idle" : "connect"); tcpconn_close(tc, ETIMEDOUT); } static void tcp_estab_handler(void *arg) { struct tcpconn *tc = arg; struct le *le = list_head(&tc->ql); int err = 0; DEBUG_INFO("connection (%J) established\n", &tc->srv); while (le) { struct dns_query *q = le->data; le = le->next; q->mb.pos = 0; err = tcp_send(tc->conn, &q->mb); if (err) break; DEBUG_INFO("tcp send %J\n", &tc->srv); } if (err) { tcpconn_close(tc, err); return; } tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout, tcpconn_timeout_handler, tc); tc->connected = true; } static void tcp_close_handler(int err, void *arg) { struct tcpconn *tc = arg; DEBUG_NOTICE("connection (%J) closed: %m\n", &tc->srv, err); tcpconn_close(tc, err); } static bool tcpconn_cmp_handler(struct le *le, void *arg) { const struct tcpconn *tc = le->data; /* avoid trying this connection if dead */ if (!tc->conn) return false; return sa_cmp(&tc->srv, arg, SA_ALL); } static bool tcpconn_fail_handler(struct le *le, void *arg) { struct dns_query *q = le->data; int err = *((int *)arg); list_unlink(&q->le_tc); q->tc = mem_deref(q->tc); if (q->ntx >= *q->srvc) { DEBUG_WARNING("all servers failed, giving up!!\n"); err = err ? err : ECONNREFUSED; goto out; } /* try next server(s) */ err = send_tcp(q); if (err) { DEBUG_WARNING("all servers failed, giving up\n"); goto out; } out: if (err) { query_handler(q, err, NULL, NULL, NULL, NULL); mem_deref(q); } return false; } static void tcpconn_close(struct tcpconn *tc, int err) { if (!tc) return; /* avoid trying this connection again (e.g. same address) */ tc->conn = mem_deref(tc->conn); (void)list_apply(&tc->ql, true, tcpconn_fail_handler, &err); mem_deref(tc); } static void tcpconn_destructor(void *arg) { struct tcpconn *tc = arg; hash_unlink(&tc->le); tmr_cancel(&tc->tmr); mem_deref(tc->conn); mem_deref(tc->mb); } static int tcpconn_alloc(struct tcpconn **tcpp, struct dnsc *dnsc, const struct sa *srv) { struct tcpconn *tc; int err = ENOMEM; if (!tcpp || !dnsc || !srv) return EINVAL; tc = mem_zalloc(sizeof(struct tcpconn), tcpconn_destructor); if (!tc) goto out; hash_append(dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), &tc->le, tc); tc->srv = *srv; tc->dnsc = dnsc; tc->mb = mbuf_alloc(1500); if (!tc->mb) goto out; err = tcp_connect(&tc->conn, srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, tc); if (err) goto out; tmr_start(&tc->tmr, tc->dnsc->conf.conn_timeout, tcpconn_timeout_handler, tc); out: if (err) mem_deref(tc); else *tcpp = tc; return err; } static int send_tcp(struct dns_query *q) { const struct sa *srv; struct tcpconn *tc; int err = 0; if (!q) return EINVAL; while (q->ntx < *q->srvc) { srv = &q->srvv[q->ntx++]; DEBUG_NOTICE("trying tcp server#%u: %J\n", q->ntx-1, srv); tc = list_ledata(hash_lookup(q->dnsc->ht_tcpconn, sa_hash(srv, SA_ALL), tcpconn_cmp_handler, (void *)srv)); if (!tc) { err = tcpconn_alloc(&tc, q->dnsc, srv); if (err) continue; } if (tc->connected) { q->mb.pos = 0; err = tcp_send(tc->conn, &q->mb); if (err) { tcpconn_close(tc, err); continue; } tmr_start(&tc->tmr, tc->dnsc->conf.idle_timeout, tcpconn_timeout_handler, tc); DEBUG_NOTICE("tcp send %J\n", srv); } list_append(&tc->ql, &q->le_tc, q); q->tc = mem_ref(tc); break; } return err; } static void tcp_timeout_handler(void *arg) { struct dns_query *q = arg; query_handler(q, ETIMEDOUT, NULL, NULL, NULL, NULL); mem_deref(q); } static int send_udp(struct dns_query *q) { const struct sa *srv; int err = ETIMEDOUT; uint32_t i; if (!q) return EINVAL; for (i=0; i<*q->srvc; i++) { struct udp_sock *us; srv = &q->srvv[q->ntx++%*q->srvc]; DEBUG_INFO("trying udp server#%u: %J\n", i, srv); switch (sa_af(srv)) { case AF_INET: us = q->dnsc->us; break; case AF_INET6: us = q->dnsc->us6; break; default: continue; } q->mb.pos = 0; err = udp_send(us, srv, &q->mb); if (!err) break; } return err; } static void udp_timeout_handler(void *arg) { struct dns_query *q = arg; int err = ETIMEDOUT; if (q->ntx >= NTX_MAX) goto out; err = send_udp(q); if (err) goto out; tmr_start(&q->tmr, 1000<ntx - 2), udp_timeout_handler, q); out: if (err) { query_handler(q, err, NULL, NULL, NULL, NULL); mem_deref(q); } } static int query(struct dns_query **qp, struct dnsc *dnsc, uint8_t opcode, const char *name, uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr, int proto, const struct sa *srvv, const uint32_t *srvc, bool aa, bool rd, dns_query_h *qh, void *arg) { struct dns_query *q = NULL; struct dnshdr hdr; int err = 0; uint32_t i; if (!dnsc || !name || !srvv || !srvc || !(*srvc)) return EINVAL; if (DNS_QTYPE_AXFR == type) proto = IPPROTO_TCP; q = mem_zalloc(sizeof(*q), query_destructor); if (!q) goto nmerr; hash_append(dnsc->ht_query, hash_joaat_str_ci(name), &q->le, q); tmr_init(&q->tmr); mbuf_init(&q->mb); for (i=0; irrlv); i++) list_init(&q->rrlv[i]); err = str_dup(&q->name, name); if (err) goto error; q->srvv = srvv; q->srvc = srvc; q->id = rand_u16(); q->type = type; q->opcode = opcode; q->dnsclass = dnsclass; q->dnsc = dnsc; memset(&hdr, 0, sizeof(hdr)); hdr.id = q->id; hdr.opcode = q->opcode; hdr.aa = aa; hdr.rd = rd; hdr.nq = 1; hdr.nans = ans_rr ? 1 : 0; if (proto == IPPROTO_TCP) q->mb.pos += 2; err = dns_hdr_encode(&q->mb, &hdr); if (err) goto error; err = dns_dname_encode(&q->mb, name, NULL, 0, false); if (err) goto error; err |= mbuf_write_u16(&q->mb, htons(type)); err |= mbuf_write_u16(&q->mb, htons(dnsclass)); if (err) goto error; if (ans_rr) { err = dns_rr_encode(&q->mb, ans_rr, 0, NULL, 0); if (err) goto error; } q->qh = qh; q->arg = arg; switch (proto) { case IPPROTO_TCP: q->mb.pos = 0; (void)mbuf_write_u16(&q->mb, htons(q->mb.end - 2)); err = send_tcp(q); if (err) goto error; tmr_start(&q->tmr, 60 * 1000, tcp_timeout_handler, q); break; case IPPROTO_UDP: err = send_udp(q); if (err) goto error; tmr_start(&q->tmr, 500, udp_timeout_handler, q); break; default: err = EPROTONOSUPPORT; goto error; } if (qp) { q->qp = qp; *qp = q; } return 0; nmerr: err = ENOMEM; error: mem_deref(q); return err; } /** * Query a DNS name * * @param qp Pointer to allocated DNS query * @param dnsc DNS Client * @param name DNS name * @param type DNS Resource Record type * @param dnsclass DNS Class * @param rd Recursion Desired (RD) flag * @param qh Query handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int dnsc_query(struct dns_query **qp, struct dnsc *dnsc, const char *name, uint16_t type, uint16_t dnsclass, bool rd, dns_query_h *qh, void *arg) { if (!dnsc) return EINVAL; return query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass, NULL, IPPROTO_UDP, dnsc->srvv, &dnsc->srvc, false, rd, qh, arg); } /** * Query a DNS name SRV record * * @param qp Pointer to allocated DNS query * @param dnsc DNS Client * @param name DNS name * @param type DNS Resource Record type * @param dnsclass DNS Class * @param proto Protocol * @param srvv DNS Nameservers * @param srvc Number of DNS nameservers * @param rd Recursion Desired (RD) flag * @param qh Query handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int dnsc_query_srv(struct dns_query **qp, struct dnsc *dnsc, const char *name, uint16_t type, uint16_t dnsclass, int proto, const struct sa *srvv, const uint32_t *srvc, bool rd, dns_query_h *qh, void *arg) { return query(qp, dnsc, DNS_OPCODE_QUERY, name, type, dnsclass, NULL, proto, srvv, srvc, false, rd, qh, arg); } /** * Send a DNS query with NOTIFY opcode * * @param qp Pointer to allocated DNS query * @param dnsc DNS Client * @param name DNS name * @param type DNS Resource Record type * @param dnsclass DNS Class * @param ans_rr Answer Resource Record * @param proto Protocol * @param srvv DNS Nameservers * @param srvc Number of DNS nameservers * @param qh Query handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int dnsc_notify(struct dns_query **qp, struct dnsc *dnsc, const char *name, uint16_t type, uint16_t dnsclass, const struct dnsrr *ans_rr, int proto, const struct sa *srvv, const uint32_t *srvc, dns_query_h *qh, void *arg) { return query(qp, dnsc, DNS_OPCODE_NOTIFY, name, type, dnsclass, ans_rr, proto, srvv, srvc, true, false, qh, arg); } static void dnsc_destructor(void *data) { struct dnsc *dnsc = data; (void)hash_apply(dnsc->ht_query, query_close_handler, NULL); hash_flush(dnsc->ht_tcpconn); mem_deref(dnsc->ht_tcpconn); mem_deref(dnsc->ht_query); mem_deref(dnsc->us6); mem_deref(dnsc->us); } /** * Allocate a DNS Client * * @param dcpp Pointer to allocated DNS Client * @param conf Optional DNS configuration, NULL for default * @param srvv DNS servers * @param srvc Number of DNS Servers * * @return 0 if success, otherwise errorcode */ int dnsc_alloc(struct dnsc **dcpp, const struct dnsc_conf *conf, const struct sa *srvv, uint32_t srvc) { struct dnsc *dnsc; struct sa laddr, laddr6; int err; if (!dcpp) return EINVAL; dnsc = mem_zalloc(sizeof(*dnsc), dnsc_destructor); if (!dnsc) return ENOMEM; if (conf) dnsc->conf = *conf; else dnsc->conf = default_conf; err = dnsc_srv_set(dnsc, srvv, srvc); if (err) goto out; sa_set_str(&laddr, "0.0.0.0", 0); sa_set_str(&laddr6, "::", 0); err = udp_listen(&dnsc->us, &laddr, udp_recv_handler, dnsc); err |= udp_listen(&dnsc->us6, &laddr6, udp_recv_handler, dnsc); if (err) goto out; err = hash_alloc(&dnsc->ht_query, dnsc->conf.query_hash_size); if (err) goto out; err = hash_alloc(&dnsc->ht_tcpconn, dnsc->conf.tcp_hash_size); if (err) goto out; out: if (err) mem_deref(dnsc); else *dcpp = dnsc; return err; } /** * Set the DNS Servers on a DNS Client * * @param dnsc DNS Client * @param srvv DNS Nameservers * @param srvc Number of nameservers * * @return 0 if success, otherwise errorcode */ int dnsc_srv_set(struct dnsc *dnsc, const struct sa *srvv, uint32_t srvc) { uint32_t i; if (!dnsc) return EINVAL; dnsc->srvc = min((uint32_t)ARRAY_SIZE(dnsc->srvv), srvc); if (srvv) { for (i=0; isrvc; i++) dnsc->srvv[i] = srvv[i]; } return 0; } re-1.1.0/src/dns/cstr.c000066400000000000000000000021531373627245400146270ustar00rootroot00000000000000/** * @file cstr.c DNS character strings encoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include /** * Encode a DNS character string into a memory buffer * * @param mb Memory buffer to encode into * @param str Character string * * @return 0 if success, otherwise errorcode */ int dns_cstr_encode(struct mbuf *mb, const char *str) { uint8_t len; int err = 0; if (!mb || !str) return EINVAL; len = (uint8_t)strlen(str); err |= mbuf_write_u8(mb, len); err |= mbuf_write_mem(mb, (const uint8_t *)str, len); return err; } /** * Decode a DNS character string from a memory buffer * * @param mb Memory buffer to decode from * @param str Pointer to allocated character string * * @return 0 if success, otherwise errorcode */ int dns_cstr_decode(struct mbuf *mb, char **str) { uint8_t len; if (!mb || !str || (mbuf_get_left(mb) < 1)) return EINVAL; len = mbuf_read_u8(mb); if (mbuf_get_left(mb) < len) return EBADMSG; return mbuf_strdup(mb, str, len); } re-1.1.0/src/dns/darwin/000077500000000000000000000000001373627245400147735ustar00rootroot00000000000000re-1.1.0/src/dns/darwin/srv.c000066400000000000000000000032701373627245400157530ustar00rootroot00000000000000/** * @file darwin/srv.c Get DNS Server IP code for Mac OS X * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include "../dns.h" #define __CF_USE_FRAMEWORK_INCLUDES__ #include int get_darwin_dns(char *domain, size_t dsize, struct sa *nsv, uint32_t *n) { #if TARGET_OS_IPHONE (void)domain; (void)dsize; (void)nsv; (void)n; return ENOSYS; #else SCDynamicStoreContext context = {0, NULL, NULL, NULL, NULL}; CFArrayRef addresses, domains; SCDynamicStoreRef store; CFStringRef key, dom; CFDictionaryRef dict; uint32_t c, i; int err = ENOENT; if (!nsv || !n) return EINVAL; store = SCDynamicStoreCreate(NULL, CFSTR("get_darwin_dns"), NULL, &context); if (!store) return ENOENT; key = CFSTR("State:/Network/Global/DNS"); dict = SCDynamicStoreCopyValue(store, key); if (!dict) goto out1; addresses = CFDictionaryGetValue(dict, kSCPropNetDNSServerAddresses); if (!addresses) goto out; c = (uint32_t)CFArrayGetCount(addresses); *n = min(*n, c); for (i=0; i<*n; i++) { CFStringRef address = CFArrayGetValueAtIndex(addresses, i); char str[64]; CFStringGetCString(address, str, sizeof(str), kCFStringEncodingUTF8); err = sa_set_str(&nsv[i], str, DNS_PORT); if (err) break; } domains = CFDictionaryGetValue(dict, kSCPropNetDNSSearchDomains); if (!domains) goto out; if (CFArrayGetCount(domains) < 1) goto out; dom = CFArrayGetValueAtIndex(domains, 0); CFStringGetCString(dom, domain, dsize, kCFStringEncodingUTF8); out: CFRelease(dict); out1: CFRelease(store); return err; #endif } re-1.1.0/src/dns/dname.c000066400000000000000000000074261373627245400147500ustar00rootroot00000000000000/** * @file dname.c DNS domain names * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #define COMP_MASK 0xc0 #define OFFSET_MASK 0x3fff #define COMP_LOOP 255 struct dname { struct le he; size_t pos; char *name; }; static void destructor(void *arg) { struct dname *dn = arg; hash_unlink(&dn->he); mem_deref(dn->name); } static void dname_append(struct hash *ht_dname, const char *name, size_t pos) { struct dname *dn; if (!ht_dname || pos > OFFSET_MASK || !*name) return; dn = mem_zalloc(sizeof(*dn), destructor); if (!dn) return; if (str_dup(&dn->name, name)) { mem_deref(dn); return; } hash_append(ht_dname, hash_joaat_str_ci(name), &dn->he, dn); dn->pos = pos; } static bool lookup_handler(struct le *le, void *arg) { struct dname *dn = le->data; return 0 == str_casecmp(dn->name, arg); } static inline struct dname *dname_lookup(struct hash *ht_dname, const char *name) { return list_ledata(hash_lookup(ht_dname, hash_joaat_str_ci(name), lookup_handler, (void *)name)); } static inline int dname_encode_pointer(struct mbuf *mb, size_t pos) { return mbuf_write_u16(mb, htons(pos | (COMP_MASK<<8))); } /** * Encode a DNS Domain name into a memory buffer * * @param mb Memory buffer * @param name Domain name * @param ht_dname Domain name hashtable * @param start Start position * @param comp Enable compression * * @return 0 if success, otherwise errorcode */ int dns_dname_encode(struct mbuf *mb, const char *name, struct hash *ht_dname, size_t start, bool comp) { struct dname *dn; size_t pos; int err; if (!mb || !name) return EINVAL; dn = dname_lookup(ht_dname, name); if (dn && comp) return dname_encode_pointer(mb, dn->pos); pos = mb->pos; if (!dn) dname_append(ht_dname, name, pos - start); err = mbuf_write_u8(mb, 0); if ('.' == name[0] && '\0' == name[1]) return err; while (err == 0) { const size_t lablen = mb->pos - pos - 1; if ('\0' == *name) { if (!lablen) break; mb->buf[pos] = lablen; err |= mbuf_write_u8(mb, 0); break; } else if ('.' == *name) { if (!lablen) return EINVAL; mb->buf[pos] = lablen; dn = dname_lookup(ht_dname, name + 1); if (dn && comp) { err |= dname_encode_pointer(mb, dn->pos); break; } pos = mb->pos; if (!dn) dname_append(ht_dname, name + 1, pos - start); err |= mbuf_write_u8(mb, 0); } else { err |= mbuf_write_u8(mb, *name); } ++name; } return err; } /** * Decode a DNS domain name from a memory buffer * * @param mb Memory buffer to decode from * @param name Pointer to allocated string with domain name * @param start Start position * * @return 0 if success, otherwise errorcode */ int dns_dname_decode(struct mbuf *mb, char **name, size_t start) { uint32_t i = 0, loopc = 0; bool comp = false; size_t pos = 0; char buf[256]; if (!mb || !name) return EINVAL; while (mb->pos < mb->end) { uint8_t len = mb->buf[mb->pos++]; if (!len) { if (comp) mb->pos = pos; buf[i++] = '\0'; *name = mem_alloc(i, NULL); if (!*name) return ENOMEM; str_ncpy(*name, buf, i); return 0; } else if ((len & COMP_MASK) == COMP_MASK) { uint16_t offset; if (loopc++ > COMP_LOOP) break; --mb->pos; if (mbuf_get_left(mb) < 2) break; offset = ntohs(mbuf_read_u16(mb)) & OFFSET_MASK; if (!comp) { pos = mb->pos; comp = true; } mb->pos = offset + start; continue; } else if (len > mbuf_get_left(mb)) break; else if (len + i + 2 > sizeof(buf)) break; if (i > 0) buf[i++] = '.'; while (len--) buf[i++] = mb->buf[mb->pos++]; } return EINVAL; } re-1.1.0/src/dns/dns.h000066400000000000000000000005761373627245400144540ustar00rootroot00000000000000/** * @file dns.h Internal DNS header file * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_RESOLV int get_resolv_dns(char *domain, size_t dsize, struct sa *nsv, uint32_t *n); #endif #ifdef WIN32 int get_windns(char *domain, size_t dsize, struct sa *nav, uint32_t *n); #endif #ifdef DARWIN int get_darwin_dns(char *domain, size_t dsize, struct sa *nsv, uint32_t *n); #endif re-1.1.0/src/dns/hdr.c000066400000000000000000000061621373627245400144350ustar00rootroot00000000000000/** * @file dns/hdr.c DNS header encoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include enum { QUERY_RESPONSE = 15, OPCODE = 11, AUTH_ANSWER = 10, TRUNCATED = 9, RECURSION_DESIRED = 8, RECURSION_AVAILABLE = 7, ZERO = 4 }; /** * Encode a DNS header * * @param mb Memory buffer to encode header into * @param hdr DNS header * * @return 0 if success, otherwise errorcode */ int dns_hdr_encode(struct mbuf *mb, const struct dnshdr *hdr) { uint16_t flags = 0; int err = 0; if (!mb || !hdr) return EINVAL; flags |= hdr->qr <opcode <aa <tc <rd <ra <z <rcode; err |= mbuf_write_u16(mb, htons(hdr->id)); err |= mbuf_write_u16(mb, htons(flags)); err |= mbuf_write_u16(mb, htons(hdr->nq)); err |= mbuf_write_u16(mb, htons(hdr->nans)); err |= mbuf_write_u16(mb, htons(hdr->nauth)); err |= mbuf_write_u16(mb, htons(hdr->nadd)); return err; } /** * Decode a DNS header from a memory buffer * * @param mb Memory buffer to decode header from * @param hdr DNS header (output) * * @return 0 if success, otherwise errorcode */ int dns_hdr_decode(struct mbuf *mb, struct dnshdr *hdr) { uint16_t flags = 0; if (!mb || !hdr || (mbuf_get_left(mb) < DNS_HEADER_SIZE)) return EINVAL; hdr->id = ntohs(mbuf_read_u16(mb)); flags = ntohs(mbuf_read_u16(mb)); hdr->qr = 0x1 & (flags >> QUERY_RESPONSE); hdr->opcode = 0xf & (flags >> OPCODE); hdr->aa = 0x1 & (flags >> AUTH_ANSWER); hdr->tc = 0x1 & (flags >> TRUNCATED); hdr->rd = 0x1 & (flags >> RECURSION_DESIRED); hdr->ra = 0x1 & (flags >> RECURSION_AVAILABLE); hdr->z = 0x7 & (flags >> ZERO); hdr->rcode = 0xf & (flags >> 0); hdr->nq = ntohs(mbuf_read_u16(mb)); hdr->nans = ntohs(mbuf_read_u16(mb)); hdr->nauth = ntohs(mbuf_read_u16(mb)); hdr->nadd = ntohs(mbuf_read_u16(mb)); return 0; } /** * Get the string of a DNS opcode * * @param opcode DNS opcode * * @return Opcode string */ const char *dns_hdr_opcodename(uint8_t opcode) { switch (opcode) { case DNS_OPCODE_QUERY: return "QUERY"; case DNS_OPCODE_IQUERY: return "IQUERY"; case DNS_OPCODE_STATUS: return "STATUS"; case DNS_OPCODE_NOTIFY: return "NOTIFY"; default: return "??"; } } /** * Get the string of a DNS response code * * @param rcode Response code * * @return Response code string */ const char *dns_hdr_rcodename(uint8_t rcode) { switch (rcode) { case DNS_RCODE_OK: return "OK"; case DNS_RCODE_FMT_ERR: return "Format Error"; case DNS_RCODE_SRV_FAIL: return "Server Failure"; case DNS_RCODE_NAME_ERR: return "Name Error"; case DNS_RCODE_NOT_IMPL: return "Not Implemented"; case DNS_RCODE_REFUSED: return "Refused"; case DNS_RCODE_NOT_AUTH: return "Server Not Authoritative for zone"; default: return "??"; } } re-1.1.0/src/dns/mod.mk000066400000000000000000000006671373627245400146300ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += dns/client.c SRCS += dns/cstr.c SRCS += dns/dname.c SRCS += dns/hdr.c SRCS += dns/ns.c SRCS += dns/rr.c SRCS += dns/rrlist.c ifneq ($(HAVE_RESOLV),) SRCS += dns/res.c endif ifeq ($(OS),win32) SRCS += dns/win32/srv.c endif ifeq ($(OS),darwin) SRCS += dns/darwin/srv.c # add libraries for darwin dns servers LFLAGS += -framework SystemConfiguration -framework CoreFoundation endif re-1.1.0/src/dns/ns.c000066400000000000000000000051241373627245400142750ustar00rootroot00000000000000/** * @file ns.c DNS Nameserver configuration * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include "dns.h" #ifdef __ANDROID__ #include #endif #define DEBUG_MODULE "ns" #define DEBUG_LEVEL 5 #include static int parse_resolv_conf(char *domain, size_t dsize, struct sa *srvv, uint32_t *n) { FILE *f; struct pl dom = pl_null; uint32_t i = 0; int err = 0; if (!srvv || !n || !*n) return EINVAL; f = fopen("/etc/resolv.conf", "r"); if (!f) return errno; for (;;) { char line[128]; struct pl srv; size_t len; if (1 != fscanf(f, "%127[^\n]\n", line)) break; if ('#' == line[0]) continue; len = str_len(line); /* Set domain if not already set */ if (!pl_isset(&dom)) { if (0 == re_regex(line, len, "domain [^ ]+", &dom)) { (void)pl_strcpy(&dom, domain, dsize); } if (0 == re_regex(line, len, "search [^ ]+", &dom)) { (void)pl_strcpy(&dom, domain, dsize); } } /* Use the first entry */ if (i < *n && 0 == re_regex(line, len, "nameserver [^\n]+", &srv)) { err = sa_set(&srvv[i], &srv, DNS_PORT); if (err) { DEBUG_WARNING("sa_set: %r (%m)\n", &srv, err); } ++i; } } *n = i; (void)fclose(f); return err; } #ifdef __ANDROID__ static int get_android_dns(struct sa *nsv, uint32_t *n) { char prop[PROP_NAME_MAX] = {0}, value[PROP_VALUE_MAX] = {0}; uint32_t i, count = 0; int err; for (i=0; i<*n; i++) { re_snprintf(prop, sizeof(prop), "net.dns%u", 1+i); if (__system_property_get(prop, value)) { err = sa_set_str(&nsv[count], value, DNS_PORT); if (!err) ++count; } } if (count == 0) return ENOENT; *n = count; return 0; } #endif /** * Get the DNS domain and nameservers * * @param domain Returned domain name * @param dsize Size of domain name buffer * @param srvv Returned nameservers * @param n Nameservers capacity, actual on return * * @return 0 if success, otherwise errorcode */ int dns_srv_get(char *domain, size_t dsize, struct sa *srvv, uint32_t *n) { int err; /* Try them all in prioritized order */ #ifdef HAVE_RESOLV err = get_resolv_dns(domain, dsize, srvv, n); if (!err) return 0; #endif #ifdef DARWIN err = get_darwin_dns(domain, dsize, srvv, n); if (!err) return 0; #endif err = parse_resolv_conf(domain, dsize, srvv, n); if (!err) return 0; #ifdef WIN32 err = get_windns(domain, dsize, srvv, n); #endif #ifdef __ANDROID__ err = get_android_dns(srvv, n); #endif return err; } re-1.1.0/src/dns/res.c000066400000000000000000000022211373627245400144410ustar00rootroot00000000000000/** * @file res.c Get DNS Server IP using resolv * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #include #include #include #include #include #include #include #include #include #include "dns.h" int get_resolv_dns(char *domain, size_t dsize, struct sa *nsv, uint32_t *n) { struct __res_state state; uint32_t i; int ret, err; #ifdef OPENBSD ret = res_init(); state = _res; #else memset(&state, 0, sizeof(state)); ret = res_ninit(&state); #endif if (0 != ret) return ENOENT; if (state.dnsrch[0]) str_ncpy(domain, state.dnsrch[0], dsize); else if ((char *)state.defdname) str_ncpy(domain, state.defdname, dsize); if (!state.nscount) { err = ENOENT; goto out; } err = 0; for (i=0; i #include #include #include #include #include #include #include #include static void rr_destructor(void *data) { struct dnsrr *rr = data; mem_deref(rr->name); switch (rr->type) { case DNS_TYPE_NS: mem_deref(rr->rdata.ns.nsdname); break; case DNS_TYPE_CNAME: mem_deref(rr->rdata.cname.cname); break; case DNS_TYPE_SOA: mem_deref(rr->rdata.soa.mname); mem_deref(rr->rdata.soa.rname); break; case DNS_TYPE_PTR: mem_deref(rr->rdata.ptr.ptrdname); break; case DNS_TYPE_MX: mem_deref(rr->rdata.mx.exchange); break; case DNS_TYPE_TXT: mem_deref(rr->rdata.txt.data); break; case DNS_TYPE_SRV: mem_deref(rr->rdata.srv.target); break; case DNS_TYPE_NAPTR: mem_deref(rr->rdata.naptr.flags); mem_deref(rr->rdata.naptr.services); mem_deref(rr->rdata.naptr.regexp); mem_deref(rr->rdata.naptr.replace); break; } } /** * Allocate a new DNS Resource Record (RR) * * @return Newly allocated Resource Record, or NULL if no memory */ struct dnsrr *dns_rr_alloc(void) { return mem_zalloc(sizeof(struct dnsrr), rr_destructor); } /** * Encode a DNS Resource Record * * @param mb Memory buffer to encode into * @param rr DNS Resource Record * @param ttl_offs TTL Offset * @param ht_dname Domain name hash-table * @param start Start position * * @return 0 if success, otherwise errorcode */ int dns_rr_encode(struct mbuf *mb, const struct dnsrr *rr, int64_t ttl_offs, struct hash *ht_dname, size_t start) { uint32_t ttl; size_t start_rdata, dlen, len; char *ptr; int err = 0; if (!mb || !rr) return EINVAL; ttl = (uint32_t)((rr->ttl > ttl_offs) ? (rr->ttl - ttl_offs) : 0); err |= dns_dname_encode(mb, rr->name, ht_dname, start, true); err |= mbuf_write_u16(mb, htons(rr->type)); err |= mbuf_write_u16(mb, htons(rr->dnsclass)); err |= mbuf_write_u32(mb, htonl(ttl)); err |= mbuf_write_u16(mb, htons(rr->rdlen)); start_rdata = mb->pos; switch (rr->type) { case DNS_TYPE_A: err |= mbuf_write_u32(mb, htonl(rr->rdata.a.addr)); break; case DNS_TYPE_NS: err |= dns_dname_encode(mb, rr->rdata.ns.nsdname, ht_dname, start, true); break; case DNS_TYPE_CNAME: err |= dns_dname_encode(mb, rr->rdata.cname.cname, ht_dname, start, true); break; case DNS_TYPE_SOA: err |= dns_dname_encode(mb, rr->rdata.soa.mname, ht_dname, start, true); err |= dns_dname_encode(mb, rr->rdata.soa.rname, ht_dname, start, true); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.serial)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.refresh)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.retry)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.expire)); err |= mbuf_write_u32(mb, htonl(rr->rdata.soa.ttlmin)); break; case DNS_TYPE_PTR: err |= dns_dname_encode(mb, rr->rdata.ptr.ptrdname, ht_dname, start, true); break; case DNS_TYPE_MX: err |= mbuf_write_u16(mb, htons(rr->rdata.mx.pref)); err |= dns_dname_encode(mb, rr->rdata.mx.exchange, ht_dname, start, true); break; case DNS_TYPE_TXT: ptr = rr->rdata.txt.data; dlen = str_len(rr->rdata.txt.data); do { uint8_t slen = min(dlen, 0xff); err |= mbuf_write_u8(mb, slen); err |= mbuf_write_mem(mb, (uint8_t *)ptr, slen); ptr += slen; dlen -= slen; } while (dlen > 0); break; case DNS_TYPE_AAAA: err |= mbuf_write_mem(mb, rr->rdata.aaaa.addr, 16); break; case DNS_TYPE_SRV: err |= mbuf_write_u16(mb, htons(rr->rdata.srv.pri)); err |= mbuf_write_u16(mb, htons(rr->rdata.srv.weight)); err |= mbuf_write_u16(mb, htons(rr->rdata.srv.port)); err |= dns_dname_encode(mb, rr->rdata.srv.target, ht_dname, start, false); break; case DNS_TYPE_NAPTR: err |= mbuf_write_u16(mb, htons(rr->rdata.naptr.order)); err |= mbuf_write_u16(mb, htons(rr->rdata.naptr.pref)); err |= dns_cstr_encode(mb, rr->rdata.naptr.flags); err |= dns_cstr_encode(mb, rr->rdata.naptr.services); err |= dns_cstr_encode(mb, rr->rdata.naptr.regexp); err |= dns_dname_encode(mb, rr->rdata.naptr.replace, ht_dname, start, false); break; default: err = EINVAL; break; } len = mb->pos - start_rdata; if (len > 0xffff) return EOVERFLOW; mb->pos = start_rdata - 2; err |= mbuf_write_u16(mb, htons(len)); mb->pos += len; return err; } /** * Decode a DNS Resource Record (RR) from a memory buffer * * @param mb Memory buffer to decode from * @param rr Pointer to allocated Resource Record * @param start Start position * * @return 0 if success, otherwise errorcode */ int dns_rr_decode(struct mbuf *mb, struct dnsrr **rr, size_t start) { int err = 0; struct dnsrr *lrr; uint16_t rdlen; char *ptr; if (!mb || !rr) return EINVAL; lrr = dns_rr_alloc(); if (!lrr) return ENOMEM; err = dns_dname_decode(mb, &lrr->name, start); if (err) goto error; if (mbuf_get_left(mb) < 10) goto fmerr; lrr->type = ntohs(mbuf_read_u16(mb)); lrr->dnsclass = ntohs(mbuf_read_u16(mb)); lrr->ttl = ntohl(mbuf_read_u32(mb)); lrr->rdlen = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < lrr->rdlen) goto fmerr; switch (lrr->type) { case DNS_TYPE_A: if (lrr->rdlen != 4) goto fmerr; lrr->rdata.a.addr = ntohl(mbuf_read_u32(mb)); break; case DNS_TYPE_NS: err = dns_dname_decode(mb, &lrr->rdata.ns.nsdname, start); if (err) goto error; break; case DNS_TYPE_CNAME: err = dns_dname_decode(mb, &lrr->rdata.cname.cname, start); if (err) goto error; break; case DNS_TYPE_SOA: err = dns_dname_decode(mb, &lrr->rdata.soa.mname, start); if (err) goto error; err = dns_dname_decode(mb, &lrr->rdata.soa.rname, start); if (err) goto error; if (mbuf_get_left(mb) < 20) goto fmerr; lrr->rdata.soa.serial = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.refresh = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.retry = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.expire = ntohl(mbuf_read_u32(mb)); lrr->rdata.soa.ttlmin = ntohl(mbuf_read_u32(mb)); break; case DNS_TYPE_PTR: err = dns_dname_decode(mb, &lrr->rdata.ptr.ptrdname, start); if (err) goto error; break; case DNS_TYPE_MX: if (mbuf_get_left(mb) < 2) goto fmerr; lrr->rdata.mx.pref = ntohs(mbuf_read_u16(mb)); err = dns_dname_decode(mb, &lrr->rdata.mx.exchange, start); if (err) goto error; break; case DNS_TYPE_TXT: ptr = lrr->rdata.txt.data = mem_alloc(lrr->rdlen + 1, NULL); if (!lrr->rdata.txt.data) { err = ENOMEM; goto error; } rdlen = lrr->rdlen; while (rdlen > 0) { uint8_t len = mbuf_read_u8(mb); if (len > --rdlen) goto fmerr; mbuf_read_mem(mb, (uint8_t *)ptr, len); ptr += len; rdlen -= len; } *ptr = '\0'; break; case DNS_TYPE_AAAA: if (lrr->rdlen != 16) goto fmerr; err = mbuf_read_mem(mb, lrr->rdata.aaaa.addr, 16); if (err) goto error; break; case DNS_TYPE_SRV: if (mbuf_get_left(mb) < 6) goto fmerr; lrr->rdata.srv.pri = ntohs(mbuf_read_u16(mb)); lrr->rdata.srv.weight = ntohs(mbuf_read_u16(mb)); lrr->rdata.srv.port = ntohs(mbuf_read_u16(mb)); err = dns_dname_decode(mb, &lrr->rdata.srv.target, start); if (err) goto error; break; case DNS_TYPE_NAPTR: if (mbuf_get_left(mb) < 4) goto fmerr; lrr->rdata.naptr.order = ntohs(mbuf_read_u16(mb)); lrr->rdata.naptr.pref = ntohs(mbuf_read_u16(mb)); err = dns_cstr_decode(mb, &lrr->rdata.naptr.flags); if (err) goto error; err = dns_cstr_decode(mb, &lrr->rdata.naptr.services); if (err) goto error; err = dns_cstr_decode(mb, &lrr->rdata.naptr.regexp); if (err) goto error; err = dns_dname_decode(mb, &lrr->rdata.naptr.replace, start); if (err) goto error; break; default: mb->pos += lrr->rdlen; break; } *rr = lrr; return 0; fmerr: err = EINVAL; error: mem_deref(lrr); return err; } /** * Compare two DNS Resource Records * * @param rr1 First Resource Record * @param rr2 Second Resource Record * @param rdata If true, also compares Resource Record data * * @return True if match, false if not match */ bool dns_rr_cmp(const struct dnsrr *rr1, const struct dnsrr *rr2, bool rdata) { if (!rr1 || !rr2) return false; if (rr1 == rr2) return true; if (rr1->type != rr2->type) return false; if (rr1->dnsclass != rr2->dnsclass) return false; if (str_casecmp(rr1->name, rr2->name)) return false; if (!rdata) return true; switch (rr1->type) { case DNS_TYPE_A: if (rr1->rdata.a.addr != rr2->rdata.a.addr) return false; break; case DNS_TYPE_NS: if (str_casecmp(rr1->rdata.ns.nsdname, rr2->rdata.ns.nsdname)) return false; break; case DNS_TYPE_CNAME: if (str_casecmp(rr1->rdata.cname.cname, rr2->rdata.cname.cname)) return false; break; case DNS_TYPE_SOA: if (str_casecmp(rr1->rdata.soa.mname, rr2->rdata.soa.mname)) return false; if (str_casecmp(rr1->rdata.soa.rname, rr2->rdata.soa.rname)) return false; if (rr1->rdata.soa.serial != rr2->rdata.soa.serial) return false; if (rr1->rdata.soa.refresh != rr2->rdata.soa.refresh) return false; if (rr1->rdata.soa.retry != rr2->rdata.soa.retry) return false; if (rr1->rdata.soa.expire != rr2->rdata.soa.expire) return false; if (rr1->rdata.soa.ttlmin != rr2->rdata.soa.ttlmin) return false; break; case DNS_TYPE_PTR: if (str_casecmp(rr1->rdata.ptr.ptrdname, rr2->rdata.ptr.ptrdname)) return false; break; case DNS_TYPE_MX: if (rr1->rdata.mx.pref != rr2->rdata.mx.pref) return false; if (str_casecmp(rr1->rdata.mx.exchange, rr2->rdata.mx.exchange)) return false; break; case DNS_TYPE_TXT: if (str_casecmp(rr1->rdata.txt.data, rr2->rdata.txt.data)) return false; break; case DNS_TYPE_AAAA: if (memcmp(rr1->rdata.aaaa.addr, rr2->rdata.aaaa.addr, 16)) return false; break; case DNS_TYPE_SRV: if (rr1->rdata.srv.pri != rr2->rdata.srv.pri) return false; if (rr1->rdata.srv.weight != rr2->rdata.srv.weight) return false; if (rr1->rdata.srv.port != rr2->rdata.srv.port) return false; if (str_casecmp(rr1->rdata.srv.target, rr2->rdata.srv.target)) return false; break; case DNS_TYPE_NAPTR: if (rr1->rdata.naptr.order != rr2->rdata.naptr.order) return false; if (rr1->rdata.naptr.pref != rr2->rdata.naptr.pref) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.flags, rr2->rdata.naptr.flags)) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.services, rr2->rdata.naptr.services)) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.regexp, rr2->rdata.naptr.regexp)) return false; /* todo check case sensitiveness */ if (str_casecmp(rr1->rdata.naptr.replace, rr2->rdata.naptr.replace)) return false; break; default: return false; } return true; } /** * Get the DNS Resource Record (RR) name * * @param type DNS Resource Record type * * @return DNS Resource Record name */ const char *dns_rr_typename(uint16_t type) { switch (type) { case DNS_TYPE_A: return "A"; case DNS_TYPE_NS: return "NS"; case DNS_TYPE_CNAME: return "CNAME"; case DNS_TYPE_SOA: return "SOA"; case DNS_TYPE_PTR: return "PTR"; case DNS_TYPE_MX: return "MX"; case DNS_TYPE_TXT: return "TXT"; case DNS_TYPE_AAAA: return "AAAA"; case DNS_TYPE_SRV: return "SRV"; case DNS_TYPE_NAPTR: return "NAPTR"; case DNS_QTYPE_IXFR: return "IXFR"; case DNS_QTYPE_AXFR: return "AXFR"; case DNS_QTYPE_ANY: return "ANY"; default: return "??"; } } /** * Get the DNS Resource Record (RR) class name * * @param dnsclass DNS Class * * @return DNS Class name */ const char *dns_rr_classname(uint16_t dnsclass) { switch (dnsclass) { case DNS_CLASS_IN: return "IN"; case DNS_QCLASS_ANY: return "ANY"; default: return "??"; } } /** * Print a DNS Resource Record * * @param pf Print function * @param rr DNS Resource Record * * @return 0 if success, otherwise errorcode */ int dns_rr_print(struct re_printf *pf, const struct dnsrr *rr) { static const size_t w = 24; struct sa sa; size_t n, l; int err; if (!pf || !rr) return EINVAL; l = str_len(rr->name); n = (w > l) ? w - l : 0; err = re_hprintf(pf, "%s.", rr->name); while (n--) err |= pf->vph(" ", 1, pf->arg); err |= re_hprintf(pf, " %10lld %-4s %-7s ", rr->ttl, dns_rr_classname(rr->dnsclass), dns_rr_typename(rr->type)); switch (rr->type) { case DNS_TYPE_A: sa_set_in(&sa, rr->rdata.a.addr, 0); err |= re_hprintf(pf, "%j", &sa); break; case DNS_TYPE_NS: err |= re_hprintf(pf, "%s.", rr->rdata.ns.nsdname); break; case DNS_TYPE_CNAME: err |= re_hprintf(pf, "%s.", rr->rdata.cname.cname); break; case DNS_TYPE_SOA: err |= re_hprintf(pf, "%s. %s. %u %u %u %u %u", rr->rdata.soa.mname, rr->rdata.soa.rname, rr->rdata.soa.serial, rr->rdata.soa.refresh, rr->rdata.soa.retry, rr->rdata.soa.expire, rr->rdata.soa.ttlmin); break; case DNS_TYPE_PTR: err |= re_hprintf(pf, "%s.", rr->rdata.ptr.ptrdname); break; case DNS_TYPE_MX: err |= re_hprintf(pf, "%3u %s.", rr->rdata.mx.pref, rr->rdata.mx.exchange); break; case DNS_TYPE_TXT: err |= re_hprintf(pf, "\"%s\"", rr->rdata.txt.data); break; case DNS_TYPE_AAAA: sa_set_in6(&sa, rr->rdata.aaaa.addr, 0); err |= re_hprintf(pf, "%j", &sa); break; case DNS_TYPE_SRV: err |= re_hprintf(pf, "%3u %3u %u %s.", rr->rdata.srv.pri, rr->rdata.srv.weight, rr->rdata.srv.port, rr->rdata.srv.target); break; case DNS_TYPE_NAPTR: err |= re_hprintf(pf, "%3u %3u \"%s\" \"%s\" \"%s\" %s.", rr->rdata.naptr.order, rr->rdata.naptr.pref, rr->rdata.naptr.flags, rr->rdata.naptr.services, rr->rdata.naptr.regexp, rr->rdata.naptr.replace); break; default: err |= re_hprintf(pf, "?"); break; } return err; } re-1.1.0/src/dns/rrlist.c000066400000000000000000000126661373627245400152050ustar00rootroot00000000000000/** * @file rrlist.c DNS Resource Records list * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include enum { CNAME_RECURSE_MAX = 16, }; struct sort { uint16_t type; uint32_t key; }; static uint32_t sidx(const struct dnsrr *rr, uint32_t key) { uint32_t addr[4]; switch (rr->type) { case DNS_TYPE_A: return rr->rdata.a.addr ^ key; case DNS_TYPE_AAAA: memcpy(addr, rr->rdata.aaaa.addr, 16); return addr[0] ^ addr[1] ^ addr[2] ^ addr[3] ^ key; case DNS_TYPE_SRV: return ((hash_fast_str(rr->rdata.srv.target) & 0xfff) ^ key) + rr->rdata.srv.weight; default: return 0; } } static bool std_sort_handler(struct le *le1, struct le *le2, void *arg) { struct dnsrr *rr1 = le1->data; struct dnsrr *rr2 = le2->data; struct sort *sort = arg; if (sort->type != rr1->type) return sort->type != rr2->type; if (sort->type != rr2->type) return true; switch (sort->type) { case DNS_TYPE_MX: return rr1->rdata.mx.pref <= rr2->rdata.mx.pref; case DNS_TYPE_SRV: if (rr1->rdata.srv.pri == rr2->rdata.srv.pri) return sidx(rr1, sort->key) >= sidx(rr2, sort->key); return rr1->rdata.srv.pri < rr2->rdata.srv.pri; case DNS_TYPE_NAPTR: if (rr1->rdata.naptr.order == rr2->rdata.naptr.order) return rr1->rdata.naptr.pref <= rr2->rdata.naptr.pref; return rr1->rdata.naptr.order < rr2->rdata.naptr.order; default: break; } return true; } static bool addr_sort_handler(struct le *le1, struct le *le2, void *arg) { struct dnsrr *rr1 = le1->data; struct dnsrr *rr2 = le2->data; struct sort *sort = arg; return sidx(rr1, sort->key) >= sidx(rr2, sort->key); } /** * Sort a list of DNS Resource Records * * @param rrl DNS Resource Record list * @param type DNS Record type * @param key Sort key */ void dns_rrlist_sort(struct list *rrl, uint16_t type, size_t key) { struct sort sort = {type, (uint32_t)key>>5}; list_sort(rrl, std_sort_handler, &sort); } /** * Sort a list of A/AAAA DNS Resource Records * * @param rrl DNS Resource Record list * @param key Sort key */ void dns_rrlist_sort_addr(struct list *rrl, size_t key) { struct sort sort = {0, (uint32_t)key>>5}; list_sort(rrl, addr_sort_handler, &sort); } static struct dnsrr *rrlist_apply(struct list *rrl, const char *name, uint16_t type1, uint16_t type2, uint16_t dnsclass, bool recurse, uint32_t depth, dns_rrlist_h *rrlh, void *arg) { struct le *le = list_head(rrl); if (depth > CNAME_RECURSE_MAX) return NULL; while (le) { struct dnsrr *rr = le->data; le = le->next; if (name && str_casecmp(name, rr->name)) continue; if (type1 != DNS_QTYPE_ANY && type2 != DNS_QTYPE_ANY && rr->type != type1 && rr->type != type2 && (rr->type != DNS_TYPE_CNAME || !recurse)) continue; if (dnsclass != DNS_QCLASS_ANY && rr->dnsclass != dnsclass) continue; if (!rrlh || rrlh(rr, arg)) return rr; if (recurse && DNS_QTYPE_ANY != type1 && DNS_QTYPE_ANY != type2 && DNS_TYPE_CNAME != type1 && DNS_TYPE_CNAME != type2 && DNS_TYPE_CNAME == rr->type) { rr = rrlist_apply(rrl, rr->rdata.cname.cname, type1, type2, dnsclass, recurse, ++depth, rrlh, arg); if (rr) return rr; } } return NULL; } /** * Apply a function handler to a list of DNS Resource Records * * @param rrl DNS Resource Record list * @param name If set, filter on domain name * @param type If not DNS_QTYPE_ANY, filter on record type * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class * @param recurse Cname recursion * @param rrlh Resource record handler * @param arg Handler argument * * @return Matching Resource Record or NULL */ struct dnsrr *dns_rrlist_apply(struct list *rrl, const char *name, uint16_t type, uint16_t dnsclass, bool recurse, dns_rrlist_h *rrlh, void *arg) { return rrlist_apply(rrl, name, type, type, dnsclass, recurse, 0, rrlh, arg); } /** * Apply a function handler to a list of DNS Resource Records (two types) * * @param rrl DNS Resource Record list * @param name If set, filter on domain name * @param type1 If not DNS_QTYPE_ANY, filter on record type * @param type2 If not DNS_QTYPE_ANY, filter on record type * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class * @param recurse Cname recursion * @param rrlh Resource record handler * @param arg Handler argument * * @return Matching Resource Record or NULL */ struct dnsrr *dns_rrlist_apply2(struct list *rrl, const char *name, uint16_t type1, uint16_t type2, uint16_t dnsclass, bool recurse, dns_rrlist_h *rrlh, void *arg) { return rrlist_apply(rrl, name, type1, type2, dnsclass, recurse, 0, rrlh, arg); } static bool find_handler(struct dnsrr *rr, void *arg) { uint16_t type = *(uint16_t *)arg; return rr->type == type; } /** * Find a DNS Resource Record in a list * * @param rrl Resource Record list * @param name If set, filter on domain name * @param type If not DNS_QTYPE_ANY, filter on record type * @param dnsclass If not DNS_QCLASS_ANY, filter on DNS class * @param recurse Cname recursion * * @return Matching Resource Record or NULL */ struct dnsrr *dns_rrlist_find(struct list *rrl, const char *name, uint16_t type, uint16_t dnsclass, bool recurse) { return rrlist_apply(rrl, name, type, type, dnsclass, recurse, 0, find_handler, &type); } re-1.1.0/src/dns/win32/000077500000000000000000000000001373627245400144515ustar00rootroot00000000000000re-1.1.0/src/dns/win32/srv.c000066400000000000000000000042361373627245400154340ustar00rootroot00000000000000/** * @file win32/srv.c Get DNS Server IP code for Windows * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "../dns.h" #define DEBUG_MODULE "win32/srv" #define DEBUG_LEVEL 5 #include int get_windns(char *domain, size_t dsize, struct sa *srvv, uint32_t *n) { FIXED_INFO * FixedInfo = NULL; ULONG ulOutBufLen; DWORD dwRetVal; IP_ADDR_STRING * pIPAddr; HANDLE hLib; union { FARPROC proc; DWORD (WINAPI *_GetNetworkParams)(FIXED_INFO*, DWORD*); } u; uint32_t i; int err; if (!srvv || !n || !*n) return EINVAL; hLib = LoadLibrary(TEXT("iphlpapi.dll")); if (!hLib) return ENOSYS; u.proc = GetProcAddress(hLib, TEXT("GetNetworkParams")); if (!u.proc) { err = ENOSYS; goto out; } FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, sizeof( FIXED_INFO )); ulOutBufLen = sizeof( FIXED_INFO ); if (ERROR_BUFFER_OVERFLOW == (*u._GetNetworkParams)(FixedInfo, &ulOutBufLen)) { GlobalFree( FixedInfo ); FixedInfo = (FIXED_INFO *)GlobalAlloc(GPTR, ulOutBufLen); } if ((dwRetVal = (*u._GetNetworkParams)( FixedInfo, &ulOutBufLen ))) { DEBUG_WARNING("couldn't get network params (%d)\n", dwRetVal); err = ENOENT; goto out; } str_ncpy(domain, FixedInfo->DomainName, dsize); #if 0 printf( "Host Name: %s\n", FixedInfo->HostName); printf( "Domain Name: %s\n", FixedInfo->DomainName); printf( "DNS Servers:\n" ); printf( "\t%s\n", FixedInfo->DnsServerList.IpAddress.String ); #endif i = 0; pIPAddr = &FixedInfo->DnsServerList; while (pIPAddr && strlen(pIPAddr->IpAddress.String) > 0) { err = sa_set_str(&srvv[i], pIPAddr->IpAddress.String, DNS_PORT); if (err) { DEBUG_WARNING("sa_set_str: %s (%m)\n", pIPAddr->IpAddress.String, err); } DEBUG_INFO("dns ip %u: %j\n", i, &srvv[i]); ++i; pIPAddr = pIPAddr ->Next; if (i >= *n) break; } *n = i; DEBUG_INFO("got %u nameservers\n", i); err = i>0 ? 0 : ENOENT; out: if (FixedInfo) GlobalFree(FixedInfo); FreeLibrary(hLib); return err; } re-1.1.0/src/fmt/000077500000000000000000000000001373627245400135115ustar00rootroot00000000000000re-1.1.0/src/fmt/ch.c000066400000000000000000000007121373627245400142470ustar00rootroot00000000000000/** * @file ch.c Character format functions * * Copyright (C) 2010 Creytiv.com */ #include #include /** * Convert an ASCII hex character to binary format * * @param ch ASCII hex character * * @return Binary value */ uint8_t ch_hex(char ch) { if ('0' <= ch && ch <= '9') return ch - '0'; else if ('A' <= ch && ch <= 'F') return ch - 'A' + 10; else if ('a' <= ch && ch <= 'f') return ch - 'a' + 10; return 0; } re-1.1.0/src/fmt/hexdump.c000066400000000000000000000020051373627245400153240ustar00rootroot00000000000000/** * @file hexdump.c Hexadecimal dumping * * Copyright (C) 2010 Creytiv.com */ #include #include #include /** * Hexadecimal dump of binary buffer. Similar output to HEXDUMP(1) * * @param f File stream for output (e.g. stderr, stdout) * @param p Pointer to data * @param len Number of bytes */ void hexdump(FILE *f, const void *p, size_t len) { const uint8_t *buf = p; uint32_t j; size_t i; if (!f || !buf) return; for (i=0; i < len; i += 16) { (void)re_fprintf(f, "%08x ", i); for (j=0; j<16; j++) { const size_t pos = i+j; if (pos < len) (void)re_fprintf(f, " %02x", buf[pos]); else (void)re_fprintf(f, " "); if (j == 7) (void)re_fprintf(f, " "); } (void)re_fprintf(f, " |"); for (j=0; j<16; j++) { const size_t pos = i+j; uint8_t v; if (pos >= len) break; v = buf[pos]; (void)re_fprintf(f, "%c", isprint(v) ? v : '.'); if (j == 7) (void)re_fprintf(f, " "); } (void)re_fprintf(f, "|\n"); } } re-1.1.0/src/fmt/mod.mk000066400000000000000000000003661373627245400146260ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += fmt/ch.c SRCS += fmt/hexdump.c SRCS += fmt/pl.c SRCS += fmt/print.c SRCS += fmt/prm.c SRCS += fmt/regex.c SRCS += fmt/str.c SRCS += fmt/str_error.c SRCS += fmt/time.c SRCS += fmt/unicode.c re-1.1.0/src/fmt/pl.c000066400000000000000000000214011373627245400142660ustar00rootroot00000000000000/** * @file pl.c Pointer-length functions * * Copyright (C) 2010 Creytiv.com */ #include #include #ifdef HAVE_STRINGS_H #define __EXTENSIONS__ 1 #include #endif #include #include #include #include #include #include /** Pointer-length NULL initialiser */ const struct pl pl_null = {NULL, 0}; /** * Initialise a pointer-length object from a NULL-terminated string * * @param pl Pointer-length object to be initialised * @param str NULL-terminated string */ void pl_set_str(struct pl *pl, const char *str) { if (!pl || !str) return; pl->p = str; pl->l = strlen(str); } /** * Initialise a pointer-length object from current position and * length of a memory buffer * * @param pl Pointer-length object to be initialised * @param mb Memory buffer */ void pl_set_mbuf(struct pl *pl, const struct mbuf *mb) { if (!pl || !mb) return; pl->p = (char *)mbuf_buf(mb); pl->l = mbuf_get_left(mb); } /** * Convert a pointer-length object to a numeric 32-bit value * * @param pl Pointer-length object * * @return 32-bit value */ uint32_t pl_u32(const struct pl *pl) { uint32_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const uint8_t c = *--p - '0'; if (c > 9) return 0; v += mul * c; mul *= 10; } return v; } /** * Convert a hex pointer-length object to a numeric 32-bit value * * @param pl Pointer-length object * * @return 32-bit value */ uint32_t pl_x32(const struct pl *pl) { uint32_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; uint8_t c; if ('0' <= ch && ch <= '9') c = ch - '0'; else if ('A' <= ch && ch <= 'F') c = ch - 'A' + 10; else if ('a' <= ch && ch <= 'f') c = ch - 'a' + 10; else return 0; v += mul * c; mul *= 16; } return v; } /** * Convert a pointer-length object to a numeric 64-bit value * * @param pl Pointer-length object * * @return 64-bit value */ uint64_t pl_u64(const struct pl *pl) { uint64_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const uint8_t c = *--p - '0'; if (c > 9) return 0; v += mul * c; mul *= 10; } return v; } /** * Convert a hex pointer-length object to a numeric 64-bit value * * @param pl Pointer-length object * * @return 64-bit value */ uint64_t pl_x64(const struct pl *pl) { uint64_t v=0, mul=1; const char *p; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; uint8_t c; if ('0' <= ch && ch <= '9') c = ch - '0'; else if ('A' <= ch && ch <= 'F') c = ch - 'A' + 10; else if ('a' <= ch && ch <= 'f') c = ch - 'a' + 10; else return 0; v += mul * c; mul *= 16; } return v; } /** * Convert a pointer-length object to floating point representation. * Both positive and negative numbers are supported, a string with a * minus sign ('-') is treated as a negative number. * * @param pl Pointer-length object * * @return Double value */ double pl_float(const struct pl *pl) { double v=0, mul=1; const char *p; bool neg = false; if (!pl || !pl->p) return 0; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; if ('0' <= ch && ch <= '9') { v += mul * (ch - '0'); mul *= 10; } else if (ch == '.') { v /= mul; mul = 1; } else if (ch == '-' && p == pl->p) { neg = true; } else { return 0; } } return neg ? -v : v; } /** * Check if pointer-length object is set * * @param pl Pointer-length object * * @return true if set, false if not set */ bool pl_isset(const struct pl *pl) { return pl ? pl->p && pl->l : false; } /** * Copy a pointer-length object to a NULL-terminated string * * @param pl Pointer-length object * @param str Buffer for NULL-terminated string * @param size Size of buffer * * @return 0 if success, otherwise errorcode */ int pl_strcpy(const struct pl *pl, char *str, size_t size) { size_t len; if (!pl || !pl->p || !str || !size) return EINVAL; len = min(pl->l, size-1); memcpy(str, pl->p, len); str[len] = '\0'; return 0; } /** * Duplicate a pointer-length object to a NULL-terminated string * * @param dst Pointer to destination string (set on return) * @param src Source pointer-length object * * @return 0 if success, otherwise errorcode */ int pl_strdup(char **dst, const struct pl *src) { char *p; if (!dst || !src || !src->p) return EINVAL; p = mem_alloc(src->l+1, NULL); if (!p) return ENOMEM; memcpy(p, src->p, src->l); p[src->l] = '\0'; *dst = p; return 0; } /** * Duplicate a pointer-length object to a new pointer-length object * * @param dst Destination pointer-length object (set on return) * @param src Source pointer-length object * * @return 0 if success, otherwise errorcode */ int pl_dup(struct pl *dst, const struct pl *src) { char *p; if (!dst || !src || !src->p) return EINVAL; p = mem_alloc(src->l, NULL); if (!p) return ENOMEM; memcpy(p, src->p, src->l); dst->p = p; dst->l = src->l; return 0; } /** * Compare a pointer-length object with a NULL-terminated string * (case-sensitive) * * @param pl Pointer-length object * @param str NULL-terminated string * * @return 0 if match, otherwise errorcode */ int pl_strcmp(const struct pl *pl, const char *str) { struct pl s; if (!pl || !str) return EINVAL; pl_set_str(&s, str); return pl_cmp(pl, &s); } /** * Compare a pointer-length object with a NULL-terminated string * (case-insensitive) * * @param pl Pointer-length object * @param str NULL-terminated string * * @return 0 if match, otherwise errorcode */ int pl_strcasecmp(const struct pl *pl, const char *str) { struct pl s; if (!pl || !str) return EINVAL; pl_set_str(&s, str); return pl_casecmp(pl, &s); } /** * Compare two pointer-length objects (case-sensitive) * * @param pl1 First pointer-length object * @param pl2 Second pointer-length object * * @return 0 if match, otherwise errorcode */ int pl_cmp(const struct pl *pl1, const struct pl *pl2) { if (!pl1 || !pl2) return EINVAL; /* Different length -> no match */ if (pl1->l != pl2->l) return EINVAL; /* Zero-length strings are always identical */ if (pl1->l == 0) return 0; /* * ~35% speed increase for fmt/pl test */ /* The two pl's are the same */ if (pl1 == pl2) return 0; /* Two different pl's pointing to same string */ if (pl1->p == pl2->p) return 0; return 0 == memcmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL; } #ifndef HAVE_STRINGS_H static int casecmp(const struct pl *pl, const char *str) { size_t i = 0; #define LOWER(d) ((d) | 0x20202020) const uint32_t *p1 = (uint32_t *)pl->p; const uint32_t *p2 = (uint32_t *)str; const size_t len = pl->l & ~0x3; /* Skip any unaligned pointers */ if (((size_t)pl->p) & (sizeof(void *) - 1)) goto next; if (((size_t)str) & (sizeof(void *) - 1)) goto next; /* Compare word-wise */ for (; il; i++) { if (tolower(pl->p[i]) != tolower(str[i])) return EINVAL; } return 0; } #endif /** * Compare two pointer-length objects (case-insensitive) * * @param pl1 First pointer-length object * @param pl2 Second pointer-length object * * @return 0 if match, otherwise errorcode */ int pl_casecmp(const struct pl *pl1, const struct pl *pl2) { if (!pl1 || !pl2) return EINVAL; /* Different length -> no match */ if (pl1->l != pl2->l) return EINVAL; /* Zero-length strings are always identical */ if (pl1->l == 0) return 0; /* * ~35% speed increase for fmt/pl test */ /* The two pl's are the same */ if (pl1 == pl2) return 0; /* Two different pl's pointing to same string */ if (pl1->p == pl2->p) return 0; #ifdef HAVE_STRINGS_H return 0 == strncasecmp(pl1->p, pl2->p, pl1->l) ? 0 : EINVAL; #else return casecmp(pl1, pl2->p); #endif } /** * Locate character in pointer-length string * * @param pl Pointer-length string * @param c Character to locate * * @return Pointer to first char if found, otherwise NULL */ const char *pl_strchr(const struct pl *pl, char c) { const char *p, *end; if (!pl) return NULL; end = pl->p + pl->l; for (p = pl->p; p < end; p++) { if (*p == c) return p; } return NULL; } /** * Locate the last occurrence of character in pointer-length string * * @param pl Pointer-length string * @param c Character to locate * * @return Pointer to last char if found, otherwise NULL */ const char *pl_strrchr(const struct pl *pl, char c) { const char *p, *end; if (!pl_isset(pl)) return NULL; end = pl->p + pl->l - 1; for (p = end; p >= pl->p; p--) { if (*p == c) return p; } return NULL; } re-1.1.0/src/fmt/print.c000066400000000000000000000326101373627245400150130ustar00rootroot00000000000000/** * @file fmt/print.c Formatted printing * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #ifdef _MSC_VER #include #ifndef isinf #define isinf(d) (!_finite(d)) #endif #ifndef isnan #define isnan(d) _isnan(d) #endif #endif #ifdef SOLARIS #include #undef isinf #define isinf(a) (fpclass((a)) == FP_NINF || fpclass((a)) == FP_PINF) #undef isnan #define isnan(a) isnand((a)) #endif enum length_modifier { LENMOD_NONE = 0, LENMOD_LONG = 1, LENMOD_LONG_LONG = 2, LENMOD_SIZE = 42, }; enum { DEC_SIZE = 42, NUM_SIZE = 64 }; static const char prfx_neg[] = "-"; static const char prfx_hex[] = "0x"; static const char str_nil[] = "(nil)"; static int write_padded(const char *p, size_t sz, size_t pad, char pch, bool plr, const char *prfx, re_vprintf_h *vph, void *arg) { const size_t prfx_len = str_len(prfx); int err = 0; pad -= MIN(pad, prfx_len); if (prfx && pch == '0') err |= vph(prfx, prfx_len, arg); while (!plr && (pad-- > sz)) err |= vph(&pch, 1, arg); if (prfx && pch != '0') err |= vph(prfx, prfx_len, arg); if (p && sz) err |= vph(p, sz, arg); while (plr && pad-- > sz) err |= vph(&pch, 1, arg); return err; } static uint32_t local_itoa(char *buf, uint64_t n, uint8_t base, bool uc) { char c, *p = buf + NUM_SIZE; uint32_t len = 1; const char a = uc ? 'A' : 'a'; *--p = '\0'; do { const uint64_t dv = n / base; const uint64_t mul = dv * base; c = (char)(n - mul); if (c < 10) *--p = '0' + c; else *--p = a + (c - 10); n = dv; ++len; } while (n != 0); memmove(buf, p, len); return len - 1; } static size_t local_ftoa(char *buf, double n, size_t dp) { char *p = buf; long long a = (long long)n; double b = n - (double)a; b = (b < 0) ? -b : b; /* integral part */ p += local_itoa(p, (a < 0) ? -a : a, 10, false); *p++ = '.'; /* decimal digits */ while (dp--) { char v; b *= 10; v = (char)b; b -= v; *p++ = '0' + (char)v; } *p = '\0'; return p - buf; } /** * Print a formatted string * * @param fmt Formatted string * @param ap Variable argument * @param vph Print handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode * * Extensions: * *
 *   %b  (char *, size_t)        Buffer string with pointer and length
 *   %r  (struct pl)             Pointer-length object
 *   %w  (uint8_t *, size_t)     Binary buffer to hexadecimal format
 *   %j  (struct sa *)           Socket address - address part only
 *   %J  (struct sa *)           Socket address and port - like 1.2.3.4:1234
 *   %H  (re_printf_h *, void *) Print handler with argument
 *   %v  (char *fmt, va_list *)  Variable argument list
 *   %m  (int)                   Describe an error code
 * 
* * Reserved for the future: * * %k * %y * */ int re_vhprintf(const char *fmt, va_list ap, re_vprintf_h *vph, void *arg) { uint8_t base, *bptr; char pch, ch, num[NUM_SIZE], addr[64], msg[256]; enum length_modifier lenmod = LENMOD_NONE; struct re_printf pf; bool fm = false, plr = false; const struct pl *pl; size_t pad = 0, fpad = -1, len, i; const char *str, *p = fmt, *p0 = fmt; const struct sa *sa; re_printf_h *ph; void *ph_arg; va_list *apl; int err = 0; void *ptr; uint64_t n; int64_t sn; bool uc = false; double dbl; if (!fmt || !vph) return EINVAL; pf.vph = vph; pf.arg = arg; for (;*p && !err; p++) { if (!fm) { if (*p != '%') continue; pch = ' '; plr = false; pad = 0; fpad = -1; lenmod = LENMOD_NONE; uc = false; if (p > p0) err |= vph(p0, p - p0, arg); fm = true; continue; } fm = false; base = 10; switch (*p) { case '-': plr = true; fm = true; break; case '.': fpad = pad; pad = 0; fm = true; break; case '%': ch = '%'; err |= vph(&ch, 1, arg); break; case 'b': str = va_arg(ap, const char *); len = va_arg(ap, size_t); err |= write_padded(str, str ? len : 0, pad, ' ', plr, NULL, vph, arg); break; case 'c': ch = va_arg(ap, int); err |= write_padded(&ch, 1, pad, ' ', plr, NULL, vph, arg); break; case 'd': case 'i': switch (lenmod) { case LENMOD_SIZE: sn = va_arg(ap, ssize_t); break; default: case LENMOD_LONG_LONG: sn = va_arg(ap, signed long long); break; case LENMOD_LONG: sn = va_arg(ap, signed long); break; case LENMOD_NONE: sn = va_arg(ap, signed); break; } len = local_itoa(num, (sn < 0) ? -sn : sn, base, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, (sn < 0) ? prfx_neg : NULL, vph, arg); break; case 'f': case 'F': dbl = va_arg(ap, double); if (fpad == (size_t)-1) { fpad = pad; pad = 0; } if (isinf(dbl)) { err |= write_padded("inf", 3, fpad, ' ', plr, NULL, vph, arg); } else if (isnan(dbl)) { err |= write_padded("nan", 3, fpad, ' ', plr, NULL, vph, arg); } else { len = local_ftoa(num, dbl, pad ? min(pad, DEC_SIZE) : 6); err |= write_padded(num, len, fpad, plr ? ' ' : pch, plr, (dbl<0) ? prfx_neg : NULL, vph, arg); } break; case 'H': ph = va_arg(ap, re_printf_h *); ph_arg = va_arg(ap, void *); if (ph) err |= ph(&pf, ph_arg); break; case 'l': ++lenmod; fm = true; break; case 'm': str = str_error(va_arg(ap, int), msg, sizeof(msg)); err |= write_padded(str, str_len(str), pad, ' ', plr, NULL, vph, arg); break; case 'p': ptr = va_arg(ap, void *); if (ptr) { len = local_itoa(num, (unsigned long int)ptr, 16, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, prfx_hex, vph, arg); } else { err |= write_padded(str_nil, sizeof(str_nil) - 1, pad, ' ', plr, NULL, vph, arg); } break; case 'r': pl = va_arg(ap, const struct pl *); err |= write_padded(pl ? pl->p : NULL, (pl && pl->p) ? pl->l : 0, pad, ' ', plr, NULL, vph, arg); break; case 's': str = va_arg(ap, const char *); err |= write_padded(str, str_len(str), pad, ' ', plr, NULL, vph, arg); break; case 'X': uc = true; /*@fallthrough@*/ case 'x': base = 16; /*@fallthrough@*/ case 'u': switch (lenmod) { case LENMOD_SIZE: n = va_arg(ap, size_t); break; default: case LENMOD_LONG_LONG: n = va_arg(ap, unsigned long long); break; case LENMOD_LONG: n = va_arg(ap, unsigned long); break; case LENMOD_NONE: n = va_arg(ap, unsigned); break; } len = local_itoa(num, n, base, uc); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, NULL, vph, arg); break; case 'v': str = va_arg(ap, char *); apl = va_arg(ap, va_list *); if (!str || !apl) break; err |= re_vhprintf(str, *apl, vph, arg); break; case 'W': uc = true; /*@fallthrough@*/ case 'w': bptr = va_arg(ap, uint8_t *); len = va_arg(ap, size_t); len = bptr ? len : 0; pch = plr ? ' ' : pch; while (!plr && pad-- > (len * 2)) err |= vph(&pch, 1, arg); for (i=0; i (len * 2)) err |= vph(&pch, 1, arg); break; case 'z': lenmod = LENMOD_SIZE; fm = true; break; case 'j': sa = va_arg(ap, struct sa *); if (!sa) break; if (sa_ntop(sa, addr, sizeof(addr))) { err |= write_padded("?", 1, pad, ' ', plr, NULL, vph, arg); break; } err |= write_padded(addr, strlen(addr), pad, ' ', plr, NULL, vph, arg); break; case 'J': sa = va_arg(ap, struct sa *); if (!sa) break; if (sa_ntop(sa, addr, sizeof(addr))) { err |= write_padded("?", 1, pad, ' ', plr, NULL, vph, arg); break; } #ifdef HAVE_INET6 if (AF_INET6 == sa_af(sa)) { ch = '['; err |= vph(&ch, 1, arg); } #endif err |= write_padded(addr, strlen(addr), pad, ' ', plr, NULL, vph, arg); #ifdef HAVE_INET6 if (AF_INET6 == sa_af(sa)) { ch = ']'; err |= vph(&ch, 1, arg); } #endif ch = ':'; err |= vph(&ch, 1, arg); len = local_itoa(num, sa_port(sa), 10, false); err |= write_padded(num, len, pad, plr ? ' ' : pch, plr, NULL, vph, arg); break; default: if (('0' <= *p) && (*p <= '9')) { if (!pad && ('0' == *p)) { pch = '0'; } else { pad *= 10; pad += *p - '0'; } fm = true; break; } ch = '?'; err |= vph(&ch, 1, arg); break; } if (!fm) p0 = p + 1; } if (!fm && p > p0) err |= vph(p0, p - p0, arg); return err; } static int print_handler(const char *p, size_t size, void *arg) { struct pl *pl = arg; if (size > pl->l) return ENOMEM; memcpy((void *)pl->p, p, size); pl_advance(pl, size); return 0; } struct dyn_print { char *str; char *p; size_t l; size_t size; }; static int print_handler_dyn(const char *p, size_t size, void *arg) { struct dyn_print *dp = arg; if (size > dp->l - 1) { const size_t new_size = MAX(dp->size + size, dp->size * 2); char *str = mem_realloc(dp->str, new_size); if (!str) return ENOMEM; dp->str = str; dp->l += new_size - dp->size; dp->p = dp->str + new_size - dp->l; dp->size = new_size; } memcpy(dp->p, p, size); dp->p += size; dp->l -= size; return 0; } struct strm_print { FILE *f; size_t n; }; static int print_handler_stream(const char *p, size_t size, void *arg) { struct strm_print *sp = arg; if (1 != fwrite(p, size, 1, sp->f)) return ENOMEM; sp->n += size; return 0; } /** * Print a formatted string to a file stream, using va_list * * @param stream File stream for the output * @param fmt Formatted string * @param ap Variable-arguments list * * @return The number of characters printed, or -1 if error */ int re_vfprintf(FILE *stream, const char *fmt, va_list ap) { struct strm_print sp; if (!stream) return -1; sp.f = stream; sp.n = 0; if (0 != re_vhprintf(fmt, ap, print_handler_stream, &sp)) return -1; return (int)sp.n; } /** * Print a formatted string to stdout, using va_list * * @param fmt Formatted string * @param ap Variable-arguments list * * @return The number of characters printed, or -1 if error */ int re_vprintf(const char *fmt, va_list ap) { return re_vfprintf(stdout, fmt, ap); } /** * Print a formatted string to a buffer, using va_list * * @param str Buffer for output string * @param size Size of buffer * @param fmt Formatted string * @param ap Variable-arguments list * * @return The number of characters printed, or -1 if error */ int re_vsnprintf(char *str, size_t size, const char *fmt, va_list ap) { struct pl pl; int err; if (!str || !size) return -1; pl.p = str; pl.l = size - 1; err = re_vhprintf(fmt, ap, print_handler, &pl); str[size - pl.l - 1] = '\0'; return err ? -1 : (int)(size - pl.l - 1); } /** * Print a formatted string to a dynamically allocated buffer, using va_list * * @param strp Pointer for output string * @param fmt Formatted string * @param ap Variable-arguments list * * @return 0 if success, otherwise errorcode */ int re_vsdprintf(char **strp, const char *fmt, va_list ap) { struct dyn_print dp; int err; if (!strp) return EINVAL; dp.size = 16; dp.str = mem_alloc(dp.size, NULL); if (!dp.str) return ENOMEM; dp.p = dp.str; dp.l = dp.size; err = re_vhprintf(fmt, ap, print_handler_dyn, &dp); if (err) goto out; *dp.p = '\0'; out: if (err) mem_deref(dp.str); else *strp = dp.str; return err; } /** * Print a formatted string * * @param pf Print backend * @param fmt Formatted string * * @return 0 if success, otherwise errorcode */ int re_hprintf(struct re_printf *pf, const char *fmt, ...) { va_list ap; int err; if (!pf) return EINVAL; va_start(ap, fmt); err = re_vhprintf(fmt, ap, pf->vph, pf->arg); va_end(ap); return err; } /** * Print a formatted string to a file stream * * @param stream File stream for output * @param fmt Formatted string * * @return The number of characters printed, or -1 if error */ int re_fprintf(FILE *stream, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = re_vfprintf(stream, fmt, ap); va_end(ap); return n; } /** * Print a formatted string to stdout * * @param fmt Formatted string * * @return The number of characters printed, or -1 if error */ int re_printf(const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = re_vprintf(fmt, ap); va_end(ap); return n; } /** * Print a formatted string to a buffer * * @param str Buffer for output string * @param size Size of buffer * @param fmt Formatted string * * @return The number of characters printed, or -1 if error */ int re_snprintf(char *str, size_t size, const char *fmt, ...) { va_list ap; int n; va_start(ap, fmt); n = re_vsnprintf(str, size, fmt, ap); va_end(ap); return n; } /** * Print a formatted string to a buffer * * @param strp Buffer pointer for output string * @param fmt Formatted string * * @return 0 if success, otherwise errorcode */ int re_sdprintf(char **strp, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = re_vsdprintf(strp, fmt, ap); va_end(ap); return err; } re-1.1.0/src/fmt/prm.c000066400000000000000000000036451373627245400144630ustar00rootroot00000000000000/** * @file prm.c Generic parameter decoding * * Copyright (C) 2010 Creytiv.com */ #include #include /** * Check if a semicolon separated parameter is present * * @param pl PL string to search * @param pname Parameter name * * @return true if found, false if not found */ bool fmt_param_exists(const struct pl *pl, const char *pname) { struct pl semi, eop; char expr[128]; if (!pl || !pname) return false; (void)re_snprintf(expr, sizeof(expr), "[;]*[ \t\r\n]*%s[ \t\r\n;=]*", pname); if (re_regex(pl->p, pl->l, expr, &semi, NULL, &eop)) return false; if (!eop.l && eop.p < pl->p + pl->l) return false; return semi.l > 0 || pl->p == semi.p; } /** * Fetch a semicolon separated parameter from a PL string * * @param pl PL string to search * @param pname Parameter name * @param val Parameter value, set on return * * @return true if found, false if not found */ bool fmt_param_get(const struct pl *pl, const char *pname, struct pl *val) { struct pl semi; char expr[128]; if (!pl || !pname) return false; (void)re_snprintf(expr, sizeof(expr), "[;]*[ \t\r\n]*%s[ \t\r\n]*=[ \t\r\n]*[~ \t\r\n;]+", pname); if (re_regex(pl->p, pl->l, expr, &semi, NULL, NULL, NULL, val)) return false; return semi.l > 0 || pl->p == semi.p; } /** * Apply a function handler for each semicolon separated parameter * * @param pl PL string to search * @param ph Parameter handler * @param arg Handler argument */ void fmt_param_apply(const struct pl *pl, fmt_param_h *ph, void *arg) { struct pl prmv, prm, semi, name, val; if (!pl || !ph) return; prmv = *pl; while (!re_regex(prmv.p, prmv.l, "[ \t\r\n]*[~;]+[;]*", NULL, &prm, &semi)) { pl_advance(&prmv, semi.p + semi.l - prmv.p); if (re_regex(prm.p, prm.l, "[^ \t\r\n=]+[ \t\r\n]*[=]*[ \t\r\n]*[~ \t\r\n]*", &name, NULL, NULL, NULL, &val)) break; ph(&name, &val, arg); } } re-1.1.0/src/fmt/regex.c000066400000000000000000000076611373627245400150010ustar00rootroot00000000000000/** * @file regex.c Implements basic regular expressions * * Copyright (C) 2010 Creytiv.com */ #include #include #include /** Defines a character range */ struct chr { uint8_t min; /**< Minimum value */ uint8_t max; /**< Maximum value */ }; static bool expr_match(const struct chr *chrv, uint32_t n, uint8_t c, bool neg) { uint32_t i; for (i=0; i chrv[i].max) continue; break; } return neg ? (i == n) : (i != n); } /** * Parse a string using basic regular expressions. Any number of matching * expressions can be given, and each match will be stored in a "struct pl" * pointer-length type. * * @param ptr String to parse * @param len Length of string * @param expr Regular expressions string * * @return 0 if success, otherwise errorcode * * Example: * * We parse the buffer for any numerical values, to get a match we must have * 1 or more occurences of the digits 0-9. The result is stored in 'num', * which is of pointer-length type and will point to the first location in * the buffer that contains "42". * *
 const char buf[] = "foo 42 bar";
 struct pl num;
 int err = re_regex(buf, strlen(buf), "[0-9]+", &num);

 here num contains a pointer to '42'
 * 
*/ int re_regex(const char *ptr, size_t len, const char *expr, ...) { struct chr chrv[64]; const char *p, *ep; bool fm, range = false, ec = false, neg = false, qesc = false; uint32_t n = 0; va_list ap; bool eesc; size_t l; if (!ptr || !expr) return EINVAL; again: eesc = false; fm = false; l = len--; p = ptr++; ep = expr; va_start(ap, expr); if (!l) goto out; for (; *ep; ep++) { if ('\\' == *ep && !eesc) { eesc = true; continue; } if (!fm) { /* Start of character class */ if ('[' == *ep && !eesc) { n = 0; fm = true; ec = false; neg = false; range = false; qesc = false; continue; } if (!l) break; if (tolower(*ep) != tolower(*p)) { va_end(ap); goto again; } eesc = false; ++p; --l; continue; } /* End of character class */ else if (ec) { uint32_t nm, nmin, nmax; struct pl lpl, *pl = va_arg(ap, struct pl *); bool quote = false, esc = false; /* Match 0 or more times */ if ('*' == *ep) { nmin = 0; nmax = -1; } /* Match 1 or more times */ else if ('+' == *ep) { nmin = 1; nmax = -1; } /* Match exactly n times */ else if ('1' <= *ep && *ep <= '9') { nmin = *ep - '0'; nmax = *ep - '0'; } else break; fm = false; lpl.p = p; lpl.l = 0; for (nm = 0; l && nm < nmax; nm++, p++, l--, lpl.l++) { if (qesc) { if (esc) { esc = false; continue; } switch (*p) { case '\\': esc = true; continue; case '"': quote = !quote; continue; } if (quote) continue; } if (!expr_match(chrv, n, tolower(*p), neg)) break; } /* Strip quotes */ if (qesc && lpl.l > 1 && lpl.p[0] == '"' && lpl.p[lpl.l - 1] == '"') { lpl.p += 1; lpl.l -= 2; nm -= 2; } if ((nm < nmin) || (nm > nmax)) { va_end(ap); goto again; } if (pl) *pl = lpl; eesc = false; continue; } if (eesc) { eesc = false; goto chr; } switch (*ep) { /* End of character class */ case ']': ec = true; continue; /* Negate with quote escape */ case '~': if (n) break; qesc = true; neg = true; continue; /* Negate */ case '^': if (n) break; neg = true; continue; /* Range */ case '-': if (!n || range) break; range = true; --n; continue; } chr: if (n >= ARRAY_SIZE(chrv)) break; chrv[n].max = tolower(*ep); if (range) range = false; else chrv[n].min = tolower(*ep); ++n; } out: va_end(ap); if (fm) return EINVAL; return *ep ? ENOENT : 0; } re-1.1.0/src/fmt/str.c000066400000000000000000000047661373627245400145020ustar00rootroot00000000000000/** * @file fmt/str.c String format functions * * Copyright (C) 2010 Creytiv.com */ #undef __STRICT_ANSI__ /* for mingw32 */ #include #ifdef HAVE_STRINGS_H #include #endif #include #include #include /** * Convert a ascii hex string to binary format * * @param hex Destinatin binary buffer * @param len Length of binary buffer * @param str Source ascii string * * @return 0 if success, otherwise errorcode */ int str_hex(uint8_t *hex, size_t len, const char *str) { size_t i; if (!hex || !str || (strlen(str) != (2 * len))) return EINVAL; for (i=0; i equal */ if (s1 == s2) return 0; if (!s1 || !s2) return 1; #ifdef WIN32 return _stricmp(s1, s2); #else return strcasecmp(s1, s2); #endif } /** * Calculate the length of a string, safe version. * * @param s String * * @return Length of the string */ size_t str_len(const char *s) { return s ? strlen(s) : 0; } re-1.1.0/src/fmt/str_error.c000066400000000000000000000015541373627245400157030ustar00rootroot00000000000000/** * @file str_error.c System error messages * * Copyright (C) 2010 Creytiv.com */ #define _GNU_SOURCE 1 #define __EXTENSIONS__ 1 #include #include #include /** * Look up an error message string corresponding to an error number. * * @param errnum Error Code * @param buf Buffer for storing error message * @param sz Buffer size * * @return Error message string */ const char *str_error(int errnum, char *buf, size_t sz) { const char *s; if (!buf || !sz) return NULL; buf[0] = '\0'; #ifdef HAVE_STRERROR_R #ifdef __GLIBC__ s = strerror_r(errnum, buf, sz); #else (void)strerror_r(errnum, buf, sz); s = buf; #endif #elif defined (WIN32) & !defined (__MINGW32__) (void)strerror_s(buf, sz, errnum); s = buf; #else /* fallback */ (void)errnum; s = "unknown error"; #endif buf[sz - 1] = '\0'; return s; } re-1.1.0/src/fmt/time.c000066400000000000000000000033141373627245400146140ustar00rootroot00000000000000/** * @file time.c Time formatting * * Copyright (C) 2010 Creytiv.com */ #include #include #include static const char *dayv[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; static const char *monv[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; /** * Print Greenwich Mean Time * * @param pf Print function for output * @param ts Time in seconds since the Epoch or NULL for current time * * @return 0 if success, otherwise errorcode */ int fmt_gmtime(struct re_printf *pf, void *ts) { const struct tm *tm; time_t t; if (!ts) { t = time(NULL); ts = &t; } tm = gmtime(ts); if (!tm) return EINVAL; return re_hprintf(pf, "%s, %02u %s %u %02u:%02u:%02u GMT", dayv[min((unsigned)tm->tm_wday, ARRAY_SIZE(dayv)-1)], tm->tm_mday, monv[min((unsigned)tm->tm_mon, ARRAY_SIZE(monv)-1)], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec); } /** * Print the human readable time * * @param pf Print function for output * @param seconds Pointer to number of seconds * * @return 0 if success, otherwise errorcode */ int fmt_human_time(struct re_printf *pf, const uint32_t *seconds) { /* max 136 years */ const uint32_t sec = *seconds%60; const uint32_t min = *seconds/60%60; const uint32_t hrs = *seconds/60/60%24; const uint32_t days = *seconds/60/60/24; int err = 0; if (days) err |= re_hprintf(pf, "%u day%s ", days, 1==days?"":"s"); if (hrs) { err |= re_hprintf(pf, "%u hour%s ", hrs, 1==hrs?"":"s"); } if (min) { err |= re_hprintf(pf, "%u min%s ", min, 1==min?"":"s"); } if (sec) { err |= re_hprintf(pf, "%u sec%s", sec, 1==sec?"":"s"); } return err; } re-1.1.0/src/fmt/unicode.c000066400000000000000000000072641373627245400153140ustar00rootroot00000000000000/** * @file unicode.c Unicode character coding * * Copyright (C) 2010 Creytiv.com */ #include #include #include static const char *hex_chars = "0123456789ABCDEF"; /** * UTF-8 encode * * @param pf Print function for output * @param str Input string to encode * * @return 0 if success, otherwise errorcode */ int utf8_encode(struct re_printf *pf, const char *str) { char ubuf[6] = "\\u00", ebuf[2] = "\\"; if (!pf) return EINVAL; if (!str) return 0; while (*str) { const uint8_t c = *str++; /* NOTE: must be unsigned 8-bit */ bool unicode = false; char ec = 0; int err; switch (c) { case '"': ec = '"'; break; case '\\': ec = '\\'; break; case '/': ec = '/'; break; case '\b': ec = 'b'; break; case '\f': ec = 'f'; break; case '\n': ec = 'n'; break; case '\r': ec = 'r'; break; case '\t': ec = 't'; break; default: if (c < ' ') { unicode = true; } /* chars in range 0x80-0xff are not escaped */ break; } if (unicode) { ubuf[4] = hex_chars[(c>>4) & 0xf]; ubuf[5] = hex_chars[c & 0xf]; err = pf->vph(ubuf, sizeof(ubuf), pf->arg); } else if (ec) { ebuf[1] = ec; err = pf->vph(ebuf, sizeof(ebuf), pf->arg); } else { err = pf->vph((char *)&c, 1, pf->arg); } if (err) return err; } return 0; } /** * UTF-8 decode * * @param pf Print function for output * @param pl Input buffer to decode * * @return 0 if success, otherwise errorcode */ int utf8_decode(struct re_printf *pf, const struct pl *pl) { int uhi = -1; size_t i; if (!pf) return EINVAL; if (!pl) return 0; for (i=0; il; i++) { char ch = pl->p[i]; int err; if (ch == '\\') { unsigned u = 0; char ubuf[4]; size_t ulen; ++i; if (i >= pl->l) return EBADMSG; ch = pl->p[i]; switch (ch) { case 'b': ch = '\b'; break; case 'f': ch = '\f'; break; case 'n': ch = '\n'; break; case 'r': ch = '\r'; break; case 't': ch = '\t'; break; case 'u': if (i+4 >= pl->l) return EBADMSG; if (!isxdigit(pl->p[i+1]) || !isxdigit(pl->p[i+2]) || !isxdigit(pl->p[i+3]) || !isxdigit(pl->p[i+4])) return EBADMSG; u |= ((uint16_t)ch_hex(pl->p[++i])) << 12; u |= ((uint16_t)ch_hex(pl->p[++i])) << 8; u |= ((uint16_t)ch_hex(pl->p[++i])) << 4; u |= ((uint16_t)ch_hex(pl->p[++i])) << 0; /* UTF-16 surrogate pair */ if (u >= 0xd800 && u <= 0xdbff) { uhi = (u - 0xd800) * 0x400; continue; } else if (u >= 0xdc00 && u <= 0xdfff) { if (uhi < 0) continue; u = uhi + u - 0xdc00 + 0x10000; } uhi = -1; ulen = utf8_byteseq(ubuf, u); err = pf->vph(ubuf, ulen, pf->arg); if (err) return err; continue; } } uhi = -1; err = pf->vph(&ch, 1, pf->arg); if (err) return err; } return 0; } /** * Encode Unicode code point into binary UTF-8 * * @param u Binary UTF-8 buffer * @param cp Unicode code point * * @return length of UTF-8 byte sequence */ size_t utf8_byteseq(char u[4], unsigned cp) { if (!u) return 0; if (cp <= 0x7f) { u[0] = cp; return 1; } else if (cp <= 0x7ff) { u[0] = 0xc0 | (cp>>6 & 0x1f); u[1] = 0x80 | (cp & 0x3f); return 2; } else if (cp <= 0xffff) { u[0] = 0xe0 | (cp>>12 & 0x0f); u[1] = 0x80 | (cp>>6 & 0x3f); u[2] = 0x80 | (cp & 0x3f); return 3; } else if (cp <= 0x10ffff) { u[0] = 0xf0 | (cp>>18 & 0x07); u[1] = 0x80 | (cp>>12 & 0x3f); u[2] = 0x80 | (cp>>6 & 0x3f); u[3] = 0x80 | (cp & 0x3f); return 4; } else { /* The replacement character (U+FFFD) */ u[0] = (char)0xef; u[1] = (char)0xbf; u[2] = (char)0xbd; return 3; } } re-1.1.0/src/hash/000077500000000000000000000000001373627245400136465ustar00rootroot00000000000000re-1.1.0/src/hash/func.c000066400000000000000000000175711373627245400147600ustar00rootroot00000000000000/** * @file func.c Hashmap functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include /** * Calculate hash-value using "Jenkins One-at-a-time" hash algorithm. * * @param key Pointer to key * @param len Key length * * @return Calculated hash-value */ uint32_t hash_joaat(const uint8_t *key, size_t len) { uint32_t hash = 0; size_t i; for (i = 0; i < len; i++) { hash += key[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } /** * Calculate hash-value for a case-insensitive string * * @param str String * @param len Length of string * * @return Calculated hash-value */ uint32_t hash_joaat_ci(const char *str, size_t len) { uint32_t hash = 0; size_t i; for (i = 0; i < len; i++) { hash += tolower(str[i]); hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } /** * Calculate hash-value for a NULL-terminated string * * @param str String * * @return Calculated hash-value */ uint32_t hash_joaat_str(const char *str) { uint32_t hash = 0; while (*str) { hash += *str++; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } /** * Calculate hash-value for a case-insensitive NULL-terminated string * * @param str String * * @return Calculated hash-value */ uint32_t hash_joaat_str_ci(const char *str) { uint32_t hash = 0; while (*str) { hash += tolower(*str++); hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } /** * Calculate hash-value for a pointer-length object * * @param pl Pointer-length object * * @return Calculated hash-value */ uint32_t hash_joaat_pl(const struct pl *pl) { return pl ? hash_joaat((const uint8_t *)pl->p, pl->l) : 0; } /** * Calculate hash-value for a case-insensitive pointer-length object * * @param pl Pointer-length object * * @return Calculated hash-value */ uint32_t hash_joaat_pl_ci(const struct pl *pl) { return pl ? hash_joaat_ci(pl->p, pl->l) : 0; } /* * My best guess at if you are big-endian or little-endian. This may * need adjustment. */ #if (defined(__BYTE_ORDER) && defined(__LITTLE_ENDIAN) && \ __BYTE_ORDER == __LITTLE_ENDIAN) || \ (defined(i386) || defined(__i386__) || defined(__i486__) || \ defined(__i586__) || defined(__i686__) || \ defined(vax) || defined(MIPSEL)) # define HASH_LITTLE_ENDIAN 1 # define HASH_BIG_ENDIAN 0 #elif (defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && \ __BYTE_ORDER == __BIG_ENDIAN) || \ (defined(sparc) || defined(POWERPC) || \ defined(mc68000) || defined(sel)) # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 1 #else # define HASH_LITTLE_ENDIAN 0 # define HASH_BIG_ENDIAN 0 #endif #define hashsize(n) ((uint32_t)1<<(n)) #define hashmask(n) (hashsize(n)-1) #define rot(x,k) (((x)<<(k)) | ((x)>>(32-(k)))) #define mix(a,b,c) { \ a -= c; a ^= rot(c, 4); c += b; \ b -= a; b ^= rot(a, 6); a += c; \ c -= b; c ^= rot(b, 8); b += a; \ a -= c; a ^= rot(c,16); c += b; \ b -= a; b ^= rot(a,19); a += c; \ c -= b; c ^= rot(b, 4); b += a; \ } #define final(a,b,c) \ { \ c ^= b; c -= rot(b,14); \ a ^= c; a -= rot(c,11); \ b ^= a; b -= rot(a,25); \ c ^= b; c -= rot(b,16); \ a ^= c; a -= rot(c,4); \ b ^= a; b -= rot(a,14); \ c ^= b; c -= rot(b,24); \ } static uint32_t hashlittle( const void *key, size_t length, uint32_t initval) { uint32_t a,b,c; union { const void *ptr; size_t i; } u; /* Set up the internal state */ a = b = c = 0xdeadbeef + ((uint32_t)length) + initval; u.ptr = key; if (HASH_LITTLE_ENDIAN && ((u.i & 0x3) == 0)) { const uint32_t *k = (const uint32_t *)key; while (length > 12) { a += k[0]; b += k[1]; c += k[2]; mix(a,b,c); length -= 12; k += 3; } #ifndef VALGRIND switch (length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=k[2]&0xffffff; b+=k[1]; a+=k[0]; break; case 10: c+=k[2]&0xffff; b+=k[1]; a+=k[0]; break; case 9 : c+=k[2]&0xff; b+=k[1]; a+=k[0]; break; case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=k[1]&0xffffff; a+=k[0]; break; case 6 : b+=k[1]&0xffff; a+=k[0]; break; case 5 : b+=k[1]&0xff; a+=k[0]; break; case 4 : a+=k[0]; break; case 3 : a+=k[0]&0xffffff; break; case 2 : a+=k[0]&0xffff; break; case 1 : a+=k[0]&0xff; break; case 0 : return c; } #else /* make valgrind happy */ const uint8_t *k8 = (const uint8_t *)k; switch (length) { case 12: c+=k[2]; b+=k[1]; a+=k[0]; break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=((uint32_t)k8[9])<<8; /* fall through */ case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[1]; a+=k[0]; break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=((uint32_t)k8[5])<<8; /* fall through */ case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]; break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=((uint32_t)k8[1])<<8; /* fall through */ case 1 : a+=k8[0]; break; case 0 : return c; } #endif /* !valgrind */ } else if (HASH_LITTLE_ENDIAN && ((u.i & 0x1) == 0)) { const uint16_t *k = (const uint16_t *)key; const uint8_t *k8; while (length > 12) { a += k[0] + (((uint32_t)k[1])<<16); b += k[2] + (((uint32_t)k[3])<<16); c += k[4] + (((uint32_t)k[5])<<16); mix(a,b,c); length -= 12; k += 6; } k8 = (const uint8_t *)k; switch (length) { case 12: c+=k[4]+(((uint32_t)k[5])<<16); b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 11: c+=((uint32_t)k8[10])<<16; /* fall through */ case 10: c+=k[4]; b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 9 : c+=k8[8]; /* fall through */ case 8 : b+=k[2]+(((uint32_t)k[3])<<16); a+=k[0]+(((uint32_t)k[1])<<16); break; case 7 : b+=((uint32_t)k8[6])<<16; /* fall through */ case 6 : b+=k[2]; a+=k[0]+(((uint32_t)k[1])<<16); break; case 5 : b+=k8[4]; /* fall through */ case 4 : a+=k[0]+(((uint32_t)k[1])<<16); break; case 3 : a+=((uint32_t)k8[2])<<16; /* fall through */ case 2 : a+=k[0]; break; case 1 : a+=k8[0]; break; case 0 : return c; } } else { const uint8_t *k = (const uint8_t *)key; while (length > 12) { a += k[0]; a += ((uint32_t)k[1])<<8; a += ((uint32_t)k[2])<<16; a += ((uint32_t)k[3])<<24; b += k[4]; b += ((uint32_t)k[5])<<8; b += ((uint32_t)k[6])<<16; b += ((uint32_t)k[7])<<24; c += k[8]; c += ((uint32_t)k[9])<<8; c += ((uint32_t)k[10])<<16; c += ((uint32_t)k[11])<<24; mix(a,b,c); length -= 12; k += 12; } /* all the case statements fall through */ switch (length) { case 12: c+=((uint32_t)k[11])<<24; case 11: c+=((uint32_t)k[10])<<16; case 10: c+=((uint32_t)k[9])<<8; case 9 : c+=k[8]; case 8 : b+=((uint32_t)k[7])<<24; case 7 : b+=((uint32_t)k[6])<<16; case 6 : b+=((uint32_t)k[5])<<8; case 5 : b+=k[4]; case 4 : a+=((uint32_t)k[3])<<24; case 3 : a+=((uint32_t)k[2])<<16; case 2 : a+=((uint32_t)k[1])<<8; case 1 : a+=k[0]; break; case 0 : return c; } } final(a,b,c); return c; } /** * Calculate hash-value using fast hash algorithm. * * @param k Pointer to key * @param len Key length * * @return Calculated hash-value */ uint32_t hash_fast(const char *k, size_t len) { static volatile int random_seed = 0x304a0012; if (!k) return 0; return hashlittle(k, len, random_seed); } /** * Calculate hash-value for a NULL-terminated string * * @param str String * * @return Calculated hash-value */ uint32_t hash_fast_str(const char *str) { return hash_fast(str, str_len(str)); } re-1.1.0/src/hash/hash.c000066400000000000000000000070021373627245400147340ustar00rootroot00000000000000/** * @file hash.c Hashmap table * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include /** Defines a hashmap table */ struct hash { struct list *bucket; /**< Bucket with linked lists */ uint32_t bsize; /**< Bucket size */ }; static void hash_destructor(void *data) { struct hash *h = data; mem_deref(h->bucket); } /** * Allocate a new hashmap table * * @param hp Address of hashmap pointer * @param bsize Bucket size * * @return 0 if success, otherwise errorcode */ int hash_alloc(struct hash **hp, uint32_t bsize) { struct hash *h; int err = 0; if (!hp || !bsize) return EINVAL; /* Validate bucket size */ if (bsize & (bsize-1)) return EINVAL; h = mem_zalloc(sizeof(*h), hash_destructor); if (!h) return ENOMEM; h->bsize = bsize; h->bucket = mem_zalloc(bsize*sizeof(*h->bucket), NULL); if (!h->bucket) { err = ENOMEM; goto out; } out: if (err) mem_deref(h); else *hp = h; return err; } /** * Add an element to the hashmap table * * @param h Hashmap table * @param key Hash key * @param le List element * @param data Element data */ void hash_append(struct hash *h, uint32_t key, struct le *le, void *data) { if (!h || !le) return; list_append(&h->bucket[key & (h->bsize-1)], le, data); } /** * Unlink an element from the hashmap table * * @param le List element */ void hash_unlink(struct le *le) { list_unlink(le); } /** * Apply a handler function to all elements in the hashmap with a matching key * * @param h Hashmap table * @param key Hash key * @param ah Apply handler * @param arg Handler argument * * @return List element if traversing stopped, otherwise NULL */ struct le *hash_lookup(const struct hash *h, uint32_t key, list_apply_h *ah, void *arg) { if (!h || !ah) return NULL; return list_apply(&h->bucket[key & (h->bsize-1)], true, ah, arg); } /** * Apply a handler function to all elements in the hashmap * * @param h Hashmap table * @param ah Apply handler * @param arg Handler argument * * @return List element if traversing stopped, otherwise NULL */ struct le *hash_apply(const struct hash *h, list_apply_h *ah, void *arg) { struct le *le = NULL; uint32_t i; if (!h || !ah) return NULL; for (i=0; (ibsize) && !le; i++) le = list_apply(&h->bucket[i], true, ah, arg); return le; } /** * Return bucket list for a given index * * @param h Hashmap table * @param key Hash key * * @return Bucket list if valid input, otherwise NULL */ struct list *hash_list(const struct hash *h, uint32_t key) { return h ? &h->bucket[key & (h->bsize - 1)] : NULL; } /** * Get hash bucket size * * @param h Hashmap table * * @return hash bucket size */ uint32_t hash_bsize(const struct hash *h) { return h ? h->bsize : 0; } /** * Flush a hashmap and free all elements * * @param h Hashmap table */ void hash_flush(struct hash *h) { uint32_t i; if (!h) return; for (i=0; ibsize; i++) list_flush(&h->bucket[i]); } /** * Clear a hashmap without dereferencing the elements * * @param h Hashmap table */ void hash_clear(struct hash *h) { uint32_t i; if (!h) return; for (i=0; ibsize; i++) list_clear(&h->bucket[i]); } /** * Calculate a valid hash size from a random size * * @param size Requested size * * @return Valid hash size */ uint32_t hash_valid_size(uint32_t size) { uint32_t x; for (x=0; (uint32_t)1< #include #include #include #include #include enum { KEY_SIZE = 256 }; struct hmac { CCHmacContext ctx; uint8_t key[KEY_SIZE]; size_t key_len; CCHmacAlgorithm algo; }; static void destructor(void *arg) { struct hmac *hmac = arg; memset(&hmac->ctx, 0, sizeof(hmac->ctx)); } int hmac_create(struct hmac **hmacp, enum hmac_hash hash, const uint8_t *key, size_t key_len) { struct hmac *hmac; CCHmacAlgorithm algo; if (!hmacp || !key || !key_len || key_len > KEY_SIZE) return EINVAL; switch (hash) { case HMAC_HASH_SHA1: algo = kCCHmacAlgSHA1; break; case HMAC_HASH_SHA256: algo = kCCHmacAlgSHA256; break; default: return ENOTSUP; } hmac = mem_zalloc(sizeof(*hmac), destructor); if (!hmac) return ENOMEM; memcpy(hmac->key, key, key_len); hmac->key_len = key_len; hmac->algo = algo; *hmacp = hmac; return 0; } int hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len, const uint8_t *data, size_t data_len) { if (!hmac || !md || !md_len || !data || !data_len) return EINVAL; /* reset state */ CCHmacInit(&hmac->ctx, hmac->algo, hmac->key, hmac->key_len); CCHmacUpdate(&hmac->ctx, data, data_len); CCHmacFinal(&hmac->ctx, md); return 0; } re-1.1.0/src/hmac/hmac.c000066400000000000000000000020261373627245400147070ustar00rootroot00000000000000/** * @file hmac/hmac.c HMAC-SHA1 * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include struct hmac { uint8_t key[SHA_DIGEST_LENGTH]; size_t key_len; }; static void destructor(void *arg) { struct hmac *hmac = arg; memset(hmac, 0, sizeof(*hmac)); } int hmac_create(struct hmac **hmacp, enum hmac_hash hash, const uint8_t *key, size_t key_len) { struct hmac *hmac; if (!hmacp || !key || !key_len) return EINVAL; if (hash != HMAC_HASH_SHA1) return ENOTSUP; if (key_len > SHA_DIGEST_LENGTH) return EINVAL; hmac = mem_zalloc(sizeof(*hmac), destructor); if (!hmac) return ENOMEM; memcpy(hmac->key, key, key_len); hmac->key_len = key_len; *hmacp = hmac; return 0; } int hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len, const uint8_t *data, size_t data_len) { if (!hmac || !md || !md_len || !data || !data_len) return EINVAL; hmac_sha1(hmac->key, hmac->key_len, data, data_len, md, md_len); return 0; } re-1.1.0/src/hmac/hmac_sha1.c000066400000000000000000000040061373627245400156230ustar00rootroot00000000000000/** * @file hmac_sha1.c Implements HMAC-SHA1 as of RFC 2202 * * Copyright (C) 2010 Creytiv.com */ #include #include #ifdef USE_OPENSSL #include #include #include #else #include #endif #include /** SHA-1 Block size */ #ifndef SHA_BLOCKSIZE #define SHA_BLOCKSIZE 64 #endif /** * Function to compute the digest * * @param k Secret key * @param lk Length of the key in bytes * @param d Data * @param ld Length of data in bytes * @param out Digest output * @param t Size of digest output */ void hmac_sha1(const uint8_t *k, /* secret key */ size_t lk, /* length of the key in bytes */ const uint8_t *d, /* data */ size_t ld, /* length of data in bytes */ uint8_t *out, /* output buffer, at least "t" bytes */ size_t t) { #ifdef USE_OPENSSL (void)t; if (!HMAC(EVP_sha1(), k, (int)lk, d, ld, out, NULL)) ERR_clear_error(); #else SHA_CTX ictx, octx; uint8_t isha[SHA_DIGEST_LENGTH], osha[SHA_DIGEST_LENGTH]; uint8_t key[SHA_DIGEST_LENGTH]; uint8_t buf[SHA_BLOCKSIZE]; size_t i; if (lk > SHA_BLOCKSIZE) { SHA_CTX tctx; SHA1_Init(&tctx); SHA1_Update(&tctx, k, lk); SHA1_Final(key, &tctx); k = key; lk = SHA_DIGEST_LENGTH; } /**** Inner Digest ****/ SHA1_Init(&ictx); /* Pad the key for inner digest */ for (i = 0 ; i < lk ; ++i) buf[i] = k[i] ^ 0x36; for (i = lk ; i < SHA_BLOCKSIZE ; ++i) buf[i] = 0x36; SHA1_Update(&ictx, buf, SHA_BLOCKSIZE); SHA1_Update(&ictx, d, ld); SHA1_Final(isha, &ictx); /**** Outer Digest ****/ SHA1_Init(&octx); /* Pad the key for outter digest */ for (i = 0 ; i < lk ; ++i) buf[i] = k[i] ^ 0x5c; for (i = lk ; i < SHA_BLOCKSIZE ; ++i) buf[i] = 0x5c; SHA1_Update(&octx, buf, SHA_BLOCKSIZE); SHA1_Update(&octx, isha, SHA_DIGEST_LENGTH); SHA1_Final(osha, &octx); /* truncate and print the results */ t = t > SHA_DIGEST_LENGTH ? SHA_DIGEST_LENGTH : t; memcpy(out, osha, t); #endif } re-1.1.0/src/hmac/mod.mk000066400000000000000000000003451373627245400147450ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += hmac/hmac_sha1.c ifneq ($(USE_OPENSSL_HMAC),) SRCS += hmac/openssl/hmac.c else ifneq ($(USE_APPLE_COMMONCRYPTO),) SRCS += hmac/apple/hmac.c else SRCS += hmac/hmac.c endif re-1.1.0/src/hmac/openssl/000077500000000000000000000000001373627245400153165ustar00rootroot00000000000000re-1.1.0/src/hmac/openssl/hmac.c000066400000000000000000000044041373627245400163740ustar00rootroot00000000000000/** * @file openssl/hmac.c HMAC using OpenSSL * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include struct hmac { HMAC_CTX *ctx; }; static void destructor(void *arg) { struct hmac *hmac = arg; #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) if (hmac->ctx) HMAC_CTX_free(hmac->ctx); #else if (hmac->ctx) HMAC_CTX_cleanup(hmac->ctx); mem_deref(hmac->ctx); #endif } int hmac_create(struct hmac **hmacp, enum hmac_hash hash, const uint8_t *key, size_t key_len) { struct hmac *hmac; const EVP_MD *evp; int err = 0; if (!hmacp || !key || !key_len) return EINVAL; switch (hash) { case HMAC_HASH_SHA1: evp = EVP_sha1(); break; case HMAC_HASH_SHA256: evp = EVP_sha256(); break; default: return ENOTSUP; } hmac = mem_zalloc(sizeof(*hmac), destructor); if (!hmac) return ENOMEM; #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) hmac->ctx = HMAC_CTX_new(); if (!hmac->ctx) { ERR_clear_error(); err = ENOMEM; goto out; } #else hmac->ctx = mem_zalloc(sizeof(*hmac->ctx), NULL); if (!hmac->ctx) { err = ENOMEM; goto out; } HMAC_CTX_init(hmac->ctx); #endif #if (OPENSSL_VERSION_NUMBER >= 0x00909000) if (!HMAC_Init_ex(hmac->ctx, key, (int)key_len, evp, NULL)) { ERR_clear_error(); err = EPROTO; } #else HMAC_Init_ex(hmac->ctx, key, (int)key_len, evp, NULL); #endif out: if (err) mem_deref(hmac); else *hmacp = hmac; return err; } int hmac_digest(struct hmac *hmac, uint8_t *md, size_t md_len, const uint8_t *data, size_t data_len) { unsigned int len = (unsigned int)md_len; if (!hmac || !md || !md_len || !data || !data_len) return EINVAL; #if (OPENSSL_VERSION_NUMBER >= 0x00909000) /* the HMAC context must be reset here */ if (!HMAC_Init_ex(hmac->ctx, 0, 0, 0, NULL)) goto error; if (!HMAC_Update(hmac->ctx, data, (int)data_len)) goto error; if (!HMAC_Final(hmac->ctx, md, &len)) goto error; return 0; error: ERR_clear_error(); return EPROTO; #else /* the HMAC context must be reset here */ HMAC_Init_ex(hmac->ctx, 0, 0, 0, NULL); HMAC_Update(hmac->ctx, data, (int)data_len); HMAC_Final(hmac->ctx, md, &len); return 0; #endif } re-1.1.0/src/http/000077500000000000000000000000001373627245400137025ustar00rootroot00000000000000re-1.1.0/src/http/auth.c000066400000000000000000000062461373627245400150170ustar00rootroot00000000000000/** * @file http/auth.c HTTP Authentication * * Copyright (C) 2011 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include enum { NONCE_EXPIRES = 300, NONCE_MIN_SIZE = 33, }; static uint64_t secret; static bool secret_set; /** * Print HTTP digest authentication challenge * * @param pf Print function for output * @param auth Authentication parameteres * * @return 0 if success, otherwise errorcode */ int http_auth_print_challenge(struct re_printf *pf, const struct http_auth *auth) { uint8_t key[MD5_SIZE]; uint64_t nv[2]; if (!auth) return 0; if (!secret_set) { secret = rand_u64(); secret_set = true; } nv[0] = time(NULL); nv[1] = secret; md5((uint8_t *)nv, sizeof(nv), key); return re_hprintf(pf, "Digest realm=\"%s\", nonce=\"%w%llx\", " "qop=\"auth\"%s", auth->realm, key, sizeof(key), nv[0], auth->stale ? ", stale=true" : ""); } static int chk_nonce(const struct pl *nonce, uint32_t expires) { uint8_t nkey[MD5_SIZE], ckey[MD5_SIZE]; uint64_t nv[2]; struct pl pl; int64_t age; unsigned i; if (!nonce || !nonce->p || nonce->l < NONCE_MIN_SIZE) return EINVAL; pl = *nonce; for (i=0; i expires) return ETIMEDOUT; return 0; } /** * Check HTTP digest authorization * * @param hval Authorization header value * @param method Request method * @param auth Authentication parameteres * @param authh Authentication handler * @param arg Authentication handler argument * * @return true if check is passed, otherwise false */ bool http_auth_check(const struct pl *hval, const struct pl *method, struct http_auth *auth, http_auth_h *authh, void *arg) { struct httpauth_digest_resp resp; uint8_t ha1[MD5_SIZE]; if (!hval || !method || !auth || !authh) return false; if (httpauth_digest_response_decode(&resp, hval)) return false; if (pl_strcasecmp(&resp.realm, auth->realm)) return false; if (chk_nonce(&resp.nonce, NONCE_EXPIRES)) { auth->stale = true; return false; } if (authh(&resp.username, ha1, arg)) return false; if (httpauth_digest_response_auth(&resp, method, ha1)) return false; return true; } /** * Check HTTP digest authorization of an HTTP request * * @param msg HTTP message * @param auth Authentication parameteres * @param authh Authentication handler * @param arg Authentication handler argument * * @return true if check is passed, otherwise false */ bool http_auth_check_request(const struct http_msg *msg, struct http_auth *auth, http_auth_h *authh, void *arg) { const struct http_hdr *hdr; if (!msg) return false; hdr = http_msg_hdr(msg, HTTP_HDR_AUTHORIZATION); if (!hdr) return false; return http_auth_check(&hdr->val, &msg->met, auth, authh, arg); } re-1.1.0/src/http/chunk.c000066400000000000000000000032421373627245400151570ustar00rootroot00000000000000/** * @file http/chunk.c Chunked Transfer Encoding * * Copyright (C) 2011 Creytiv.com */ #include #include #include #include "http.h" static int decode_chunk_size(struct http_chunk *chunk, struct mbuf *mb) { while (mbuf_get_left(mb)) { char ch = (char)mbuf_read_u8(mb); uint8_t c; if (ch == '\n') { if (chunk->digit) { chunk->digit = false; chunk->param = false; return 0; } else continue; } if (chunk->param) continue; if ('0' <= ch && ch <= '9') c = ch - '0'; else if ('A' <= ch && ch <= 'F') c = ch - 'A' + 10; else if ('a' <= ch && ch <= 'f') c = ch - 'a' + 10; else if (ch == '\r' || ch == ' ' || ch == '\t') continue; else if (ch == ';' && chunk->digit) { chunk->param = true; continue; } else return EPROTO; chunk->digit = true; chunk->size <<= 4; chunk->size += c; } return ENODATA; } static int decode_trailer(struct http_chunk *chunk, struct mbuf *mb) { while (mbuf_get_left(mb)) { char ch = (char)mbuf_read_u8(mb); if (ch == '\n') { if (++chunk->lf >= 2) return 0; } else if (ch != '\r') chunk->lf = 0; } return ENODATA; } int http_chunk_decode(struct http_chunk *chunk, struct mbuf *mb, size_t *size) { int err; if (!chunk || !mb || !size) return EINVAL; if (chunk->trailer) { err = decode_trailer(chunk, mb); if (err) return err; *size = 0; return 0; } err = decode_chunk_size(chunk, mb); if (err) return err; if (chunk->size == 0) { chunk->trailer = true; chunk->lf = 1; err = decode_trailer(chunk, mb); if (err) return err; } *size = chunk->size; chunk->size = 0; return 0; } re-1.1.0/src/http/client.c000066400000000000000000000324611373627245400153320ustar00rootroot00000000000000/** * @file http/client.c HTTP Client * * Copyright (C) 2011 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "http.h" enum { CONN_TIMEOUT = 30000, RECV_TIMEOUT = 60000, IDLE_TIMEOUT = 900000, BUFSIZE_MAX = 524288, CONN_BSIZE = 256, }; struct http_cli { struct list reql; struct hash *ht_conn; struct dnsc *dnsc; struct tls *tls; char *tls_hostname; struct sa laddr; #ifdef HAVE_INET6 struct sa laddr6; #endif }; struct conn; struct http_req { struct http_chunk chunk; struct sa srvv[16]; struct le le; struct http_req **reqp; struct http_cli *cli; struct http_msg *msg; struct dns_query *dq; struct conn *conn; struct mbuf *mbreq; struct mbuf *mb; char *host; http_resp_h *resph; http_data_h *datah; http_conn_h *connh; void *arg; size_t rx_len; unsigned srvc; uint16_t port; bool chunked; bool secure; bool close; }; struct conn { struct tmr tmr; struct sa addr; struct le he; struct http_req *req; struct tls_conn *sc; struct tcp_conn *tc; uint64_t usec; }; static void req_close(struct http_req *req, int err, const struct http_msg *msg); static int req_connect(struct http_req *req); static void timeout_handler(void *arg); static void cli_destructor(void *arg) { struct http_cli *cli = arg; struct le *le = cli->reql.head; while (le) { struct http_req *req = le->data; le = le->next; req_close(req, ECONNABORTED, NULL); } hash_flush(cli->ht_conn); mem_deref(cli->ht_conn); mem_deref(cli->dnsc); mem_deref(cli->tls); mem_deref(cli->tls_hostname); } static void req_destructor(void *arg) { struct http_req *req = arg; list_unlink(&req->le); mem_deref(req->msg); mem_deref(req->dq); mem_deref(req->conn); mem_deref(req->mbreq); mem_deref(req->mb); mem_deref(req->host); } static void conn_destructor(void *arg) { struct conn *conn = arg; tmr_cancel(&conn->tmr); hash_unlink(&conn->he); mem_deref(conn->sc); mem_deref(conn->tc); } static void conn_idle(struct conn *conn) { tmr_start(&conn->tmr, IDLE_TIMEOUT, timeout_handler, conn); conn->req = NULL; } static void req_close(struct http_req *req, int err, const struct http_msg *msg) { list_unlink(&req->le); req->dq = mem_deref(req->dq); req->datah = NULL; if (req->conn) { if (req->connh) req->connh(req->conn->tc, req->conn->sc, req->arg); if (err || req->close || req->connh) mem_deref(req->conn); else conn_idle(req->conn); req->conn = NULL; } req->connh = NULL; if (req->reqp) { *req->reqp = NULL; req->reqp = NULL; } if (req->resph) { if (msg) msg->mb->pos = 0; req->resph(err, msg, req->arg); req->resph = NULL; } mem_deref(req); } static void try_next(struct conn *conn, int err) { struct http_req *req = conn->req; bool retry = conn->usec > 1; mem_deref(conn); if (!req) return; req->conn = NULL; if (retry) ++req->srvc; if (req->srvc > 0 && !req->msg) { err = req_connect(req); if (!err) return; } req_close(req, err, NULL); } static int write_body_buf(struct http_msg *msg, const uint8_t *buf, size_t sz) { if ((msg->mb->pos + sz) > BUFSIZE_MAX) return EOVERFLOW; return mbuf_write_mem(msg->mb, buf, sz); } static int write_body(struct http_req *req, struct mbuf *mb) { const size_t size = min(mbuf_get_left(mb), req->rx_len); int err; if (size == 0) return 0; if (req->datah) err = req->datah(mbuf_buf(mb), size, req->msg, req->arg); else err = write_body_buf(req->msg, mbuf_buf(mb), size); if (err) return err; req->rx_len -= size; mb->pos += size; return 0; } static int req_recv(struct http_req *req, struct mbuf *mb, bool *last) { int err; *last = false; if (!req->chunked) { err = write_body(req, mb); if (err) return err; if (req->rx_len == 0) *last = true; return 0; } while (mbuf_get_left(mb)) { if (req->rx_len == 0) { err = http_chunk_decode(&req->chunk, mb, &req->rx_len); if (err == ENODATA) return 0; else if (err) return err; else if (req->rx_len == 0) { *last = true; return 0; } } err = write_body(req, mb); if (err) return err; } return 0; } static void timeout_handler(void *arg) { struct conn *conn = arg; try_next(conn, ETIMEDOUT); } static void estab_handler(void *arg) { struct conn *conn = arg; struct http_req *req = conn->req; int err; if (!req) return; err = tcp_send(conn->tc, req->mbreq); if (err) { try_next(conn, err); return; } tmr_start(&conn->tmr, RECV_TIMEOUT, timeout_handler, conn); } static void recv_handler(struct mbuf *mb, void *arg) { const struct http_hdr *hdr; struct conn *conn = arg; struct http_req *req = conn->req; size_t pos; bool last; int err; if (!req) return; if (req->msg) { err = req_recv(req, mb, &last); if (err || last) goto out; return; } if (req->mb) { const size_t len = mbuf_get_left(mb); if ((mbuf_get_left(req->mb) + len) > BUFSIZE_MAX) { err = EOVERFLOW; goto out; } pos = req->mb->pos; req->mb->pos = req->mb->end; err = mbuf_write_mem(req->mb, mbuf_buf(mb), len); if (err) goto out; req->mb->pos = pos; } else { req->mb = mem_ref(mb); } pos = req->mb->pos; err = http_msg_decode(&req->msg, req->mb, false); if (err) { if (err == ENODATA) { req->mb->pos = pos; return; } goto out; } if (req->datah) tmr_cancel(&conn->tmr); hdr = http_msg_hdr(req->msg, HTTP_HDR_CONNECTION); if (hdr && !pl_strcasecmp(&hdr->val, "close")) req->close = true; if (http_msg_hdr_has_value(req->msg, HTTP_HDR_TRANSFER_ENCODING, "chunked")) req->chunked = true; else req->rx_len = req->msg->clen; err = req_recv(req, req->mb, &last); if (err || last) goto out; return; out: req_close(req, err, req->msg); } static void close_handler(int err, void *arg) { struct conn *conn = arg; try_next(conn, err ? err : ECONNRESET); } static bool conn_cmp(struct le *le, void *arg) { const struct conn *conn = le->data; const struct http_req *req = arg; if (!sa_cmp(&req->srvv[req->srvc], &conn->addr, SA_ALL)) return false; if (req->secure != !!conn->sc) return false; return conn->req == NULL; } static int conn_connect(struct http_req *req) { const struct sa *addr = &req->srvv[req->srvc]; struct conn *conn; struct sa *laddr = NULL; int err; conn = list_ledata(hash_lookup(req->cli->ht_conn, sa_hash(addr, SA_ALL), conn_cmp, req)); if (conn) { err = tcp_send(conn->tc, req->mbreq); if (!err) { tmr_start(&conn->tmr, RECV_TIMEOUT, timeout_handler, conn); req->conn = conn; conn->req = req; ++conn->usec; return 0; } mem_deref(conn); } conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return ENOMEM; hash_append(req->cli->ht_conn, sa_hash(addr, SA_ALL), &conn->he, conn); conn->addr = *addr; conn->usec = 1; if (sa_af(&conn->addr) == AF_INET) laddr = &req->cli->laddr; #ifdef HAVE_INET6 else if (sa_af(&conn->addr) == AF_INET6) laddr = &req->cli->laddr6; #endif if (sa_isset(laddr, SA_ADDR)) err = tcp_connect_bind(&conn->tc, addr, estab_handler, recv_handler,close_handler, laddr, conn); else err = tcp_connect(&conn->tc, addr, estab_handler, recv_handler, close_handler, conn); if (err) goto out; #ifdef USE_TLS if (req->secure) { err = tls_start_tcp(&conn->sc, req->cli->tls, conn->tc, 0); if (err) goto out; if (req->cli->tls_hostname) err = tls_peer_set_verify_host(conn->sc, req->cli->tls_hostname); if (err) goto out; err = tls_set_servername(conn->sc, req->host); if (err) goto out; } #endif tmr_start(&conn->tmr, CONN_TIMEOUT, timeout_handler, conn); req->conn = conn; conn->req = req; out: if (err) mem_deref(conn); return err; } static int req_connect(struct http_req *req) { int err = EINVAL; while (req->srvc > 0) { --req->srvc; req->mb = mem_deref(req->mb); err = conn_connect(req); if (!err) break; } return err; } static bool rr_handler(struct dnsrr *rr, void *arg) { struct http_req *req = arg; if (req->srvc >= ARRAY_SIZE(req->srvv)) return true; switch (rr->type) { case DNS_TYPE_A: sa_set_in(&req->srvv[req->srvc++], rr->rdata.a.addr, req->port); break; case DNS_TYPE_AAAA: sa_set_in6(&req->srvv[req->srvc++], rr->rdata.aaaa.addr, req->port); break; } return false; } static void query_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct http_req *req = arg; (void)hdr; (void)authl; (void)addl; dns_rrlist_apply2(ansl, req->host, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN, true, rr_handler, req); if (req->srvc == 0) { err = err ? err : EDESTADDRREQ; goto fail; } err = req_connect(req); if (err) goto fail; return; fail: req_close(req, err, NULL); } /** * Send an HTTP request * * @param reqp Pointer to allocated HTTP request object * @param cli HTTP Client * @param met Request method * @param uri Request URI * @param resph Response handler * @param datah Content handler (optional) * @param arg Handler argument * @param fmt Formatted HTTP headers and body (optional) * * @return 0 if success, otherwise errorcode */ int http_request(struct http_req **reqp, struct http_cli *cli, const char *met, const char *uri, http_resp_h *resph, http_data_h *datah, void *arg, const char *fmt, ...) { struct pl scheme, host, port, path; struct http_req *req; uint16_t defport; bool secure; va_list ap; int err; if (!cli || !met || !uri) return EINVAL; if (re_regex(uri, strlen(uri), "[a-z]+://[^:/]+[:]*[0-9]*[^]+", &scheme, &host, NULL, &port, &path) || scheme.p != uri) return EINVAL; if (!pl_strcasecmp(&scheme, "http") || !pl_strcasecmp(&scheme, "ws")) { secure = false; defport = 80; } #ifdef USE_TLS else if (!pl_strcasecmp(&scheme, "https") || !pl_strcasecmp(&scheme, "wss")) { secure = true; defport = 443; } #endif else return ENOTSUP; req = mem_zalloc(sizeof(*req), req_destructor); if (!req) return ENOMEM; list_append(&cli->reql, &req->le, req); req->cli = cli; req->secure = secure; req->port = pl_isset(&port) ? pl_u32(&port) : defport; req->resph = resph; req->datah = datah; req->arg = arg; err = pl_strdup(&req->host, &host); if (err) goto out; req->mbreq = mbuf_alloc(1024); if (!req->mbreq) { err = ENOMEM; goto out; } err = mbuf_printf(req->mbreq, "%s %r HTTP/1.1\r\n" "Host: %r\r\n", met, &path, &host); if (fmt) { va_start(ap, fmt); err |= mbuf_vprintf(req->mbreq, fmt, ap); va_end(ap); } else { err |= mbuf_write_str(req->mbreq, "\r\n"); } if (err) goto out; req->mbreq->pos = 0; if (!sa_set_str(&req->srvv[0], req->host, req->port)) { req->srvc = 1; err = req_connect(req); if (err) goto out; } else { err = dnsc_query(&req->dq, cli->dnsc, req->host, DNS_TYPE_A, DNS_CLASS_IN, true, query_handler, req); if (err) goto out; } out: if (err) mem_deref(req); else if (reqp) { req->reqp = reqp; *reqp = req; } return err; } /** * Set HTTP request connection handler * * @param req HTTP request object * @param connh Connection handler */ void http_req_set_conn_handler(struct http_req *req, http_conn_h *connh) { if (!req) return; req->connh = connh; } /** * Allocate an HTTP client instance * * @param clip Pointer to allocated HTTP client * @param dnsc DNS Client * * @return 0 if success, otherwise errorcode */ int http_client_alloc(struct http_cli **clip, struct dnsc *dnsc) { struct http_cli *cli; int err; if (!clip || !dnsc) return EINVAL; cli = mem_zalloc(sizeof(*cli), cli_destructor); if (!cli) return ENOMEM; err = hash_alloc(&cli->ht_conn, CONN_BSIZE); if (err) goto out; #ifdef USE_TLS err = tls_alloc(&cli->tls, TLS_METHOD_SSLV23, NULL, NULL); if (err) goto out; err = tls_set_verify_purpose(cli->tls, "sslserver"); if (err) goto out; #else err = 0; #endif cli->dnsc = mem_ref(dnsc); out: if (err) mem_deref(cli); else *clip = cli; return err; } #ifdef USE_TLS /** * Add trusted CA certificates * * @param cli HTTP client * @param capath Path to CA certificates * * @return 0 if success, otherwise errorcode */ int http_client_add_ca(struct http_cli *cli, const char *tls_ca) { if (!cli || !tls_ca) return EINVAL; return tls_add_ca(cli->tls, tls_ca); } /** * Set verify host name * * @param cli HTTP client * @param hostname String for alternative name validation. * * @return 0 if success, otherwise errorcode */ int http_client_set_tls_hostname(struct http_cli *cli, const struct pl *hostname) { if (!cli || !hostname) return EINVAL; return tls_set_hostname(cli->tls_hostname, hostname); } #endif /** * Send an HTTP request * * @param cli HTTP Client * @param addr Bind to local v4 address * */ void http_client_set_laddr(struct http_cli *cli, struct sa *addr) { if (cli && addr) sa_cpy(&cli->laddr, addr); } /** * Send an HTTP request * * @param cli HTTP Client * @param addr Bind to local v6 address * */ void http_client_set_laddr6(struct http_cli *cli, struct sa *addr) { #ifdef HAVE_INET6 if (cli && addr) sa_cpy(&cli->laddr6, addr); #endif } re-1.1.0/src/http/http.h000066400000000000000000000004051373627245400150310ustar00rootroot00000000000000/** * @file http.h HTTP Private Interface * * Copyright (C) 2010 Creytiv.com */ struct http_chunk { size_t size; unsigned lf; bool trailer; bool digit; bool param; }; int http_chunk_decode(struct http_chunk *chunk, struct mbuf *mb, size_t *size); re-1.1.0/src/http/mod.mk000066400000000000000000000002311373627245400150060ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += http/auth.c SRCS += http/chunk.c SRCS += http/client.c SRCS += http/msg.c SRCS += http/server.c re-1.1.0/src/http/msg.c000066400000000000000000000225521373627245400146420ustar00rootroot00000000000000/** * @file http/msg.c HTTP Message decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include enum { STARTLINE_MAX = 8192, }; static void hdr_destructor(void *arg) { struct http_hdr *hdr = arg; list_unlink(&hdr->le); } static void destructor(void *arg) { struct http_msg *msg = arg; list_flush(&msg->hdrl); mem_deref(msg->_mb); mem_deref(msg->mb); } static enum http_hdrid hdr_hash(const struct pl *name) { if (!name->l) return HTTP_HDR_NONE; switch (name->p[0]) { case 'x': case 'X': if (name->l > 1 && name->p[1] == '-') return HTTP_HDR_NONE; break; } return (enum http_hdrid)(hash_joaat_ci(name->p, name->l) & 0xfff); } static inline bool hdr_comma_separated(enum http_hdrid id) { switch (id) { case HTTP_HDR_ACCEPT: case HTTP_HDR_ACCEPT_CHARSET: case HTTP_HDR_ACCEPT_ENCODING: case HTTP_HDR_ACCEPT_LANGUAGE: case HTTP_HDR_ACCEPT_RANGES: case HTTP_HDR_ALLOW: case HTTP_HDR_CACHE_CONTROL: case HTTP_HDR_CONNECTION: case HTTP_HDR_CONTENT_ENCODING: case HTTP_HDR_CONTENT_LANGUAGE: case HTTP_HDR_EXPECT: case HTTP_HDR_IF_MATCH: case HTTP_HDR_IF_NONE_MATCH: case HTTP_HDR_PRAGMA: case HTTP_HDR_SEC_WEBSOCKET_EXTENSIONS: case HTTP_HDR_SEC_WEBSOCKET_PROTOCOL: case HTTP_HDR_SEC_WEBSOCKET_VERSION: case HTTP_HDR_TE: case HTTP_HDR_TRAILER: case HTTP_HDR_TRANSFER_ENCODING: case HTTP_HDR_UPGRADE: case HTTP_HDR_VARY: case HTTP_HDR_VIA: case HTTP_HDR_WARNING: return true; default: return false; } } static inline int hdr_add(struct http_msg *msg, const struct pl *name, enum http_hdrid id, const char *p, ssize_t l) { struct http_hdr *hdr; int err = 0; hdr = mem_zalloc(sizeof(*hdr), hdr_destructor); if (!hdr) return ENOMEM; hdr->name = *name; hdr->val.p = p; hdr->val.l = MAX(l, 0); hdr->id = id; list_append(&msg->hdrl, &hdr->le, hdr); /* parse common headers */ switch (id) { case HTTP_HDR_CONTENT_TYPE: err = msg_ctype_decode(&msg->ctyp, &hdr->val); break; case HTTP_HDR_CONTENT_LENGTH: msg->clen = pl_u32(&hdr->val); break; default: break; } if (err) mem_deref(hdr); return err; } /** * Decode a HTTP message * * @param msgp Pointer to allocated HTTP Message * @param mb Buffer containing HTTP Message * @param req True for request, false for response * * @return 0 if success, otherwise errorcode */ int http_msg_decode(struct http_msg **msgp, struct mbuf *mb, bool req) { struct pl b, s, e, name, scode; const char *p, *cv; struct http_msg *msg; bool comsep, quote; enum http_hdrid id = HTTP_HDR_NONE; uint32_t ws, lf; size_t l; int err; if (!msgp || !mb) return EINVAL; p = (const char *)mbuf_buf(mb); l = mbuf_get_left(mb); if (re_regex(p, l, "[\r\n]*[^\r\n]+[\r]*[\n]1", &b, &s, NULL, &e)) return (l > STARTLINE_MAX) ? EBADMSG : ENODATA; msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) return ENOMEM; msg->_mb = mem_ref(mb); msg->mb = mbuf_alloc(8192); if (!msg->mb) { err = ENOMEM; goto out; } if (req) { if (re_regex(s.p, s.l, "[a-z]+ [^? ]+[^ ]* HTTP/[0-9.]+", &msg->met, &msg->path, &msg->prm, &msg->ver) || msg->met.p != s.p) { err = EBADMSG; goto out; } } else { if (re_regex(s.p, s.l, "HTTP/[0-9.]+ [0-9]+[ ]*[^]*", &msg->ver, &scode, NULL, &msg->reason) || msg->ver.p != s.p + 5) { err = EBADMSG; goto out; } msg->scode = pl_u32(&scode); } l -= e.p + e.l - p; p = e.p + e.l; name.p = cv = NULL; name.l = ws = lf = 0; comsep = false; quote = false; for (; l > 0; p++, l--) { switch (*p) { case ' ': case '\t': lf = 0; /* folding */ ++ws; break; case '\r': ++ws; break; case '\n': ++ws; if (!name.p) { ++p; --l; /* no headers */ err = 0; goto out; } if (!lf++) break; ++p; --l; /* eoh */ /*@fallthrough@*/ default: if (lf || (*p == ',' && comsep && !quote)) { if (!name.l) { err = EBADMSG; goto out; } err = hdr_add(msg, &name, id, cv ? cv : p, cv ? p - cv - ws : 0); if (err) goto out; if (!lf) { /* comma separated */ cv = NULL; break; } if (lf > 1) { /* eoh */ err = 0; goto out; } comsep = false; name.p = NULL; cv = NULL; lf = 0; } if (!name.p) { name.p = p; name.l = 0; ws = 0; } if (!name.l) { if (*p != ':') { ws = 0; break; } name.l = MAX((int)(p - name.p - ws), 0); if (!name.l) { err = EBADMSG; goto out; } id = hdr_hash(&name); comsep = hdr_comma_separated(id); break; } if (!cv) { quote = false; cv = p; } if (*p == '"') quote = !quote; ws = 0; break; } } err = ENODATA; out: if (err) mem_deref(msg); else { *msgp = msg; mb->pos = mb->end - l; } return err; } /** * Get a HTTP Header from a HTTP Message * * @param msg HTTP Message * @param id HTTP Header ID * * @return HTTP Header if found, NULL if not found */ const struct http_hdr *http_msg_hdr(const struct http_msg *msg, enum http_hdrid id) { return http_msg_hdr_apply(msg, true, id, NULL, NULL); } /** * Apply a function handler to certain HTTP Headers * * @param msg HTTP Message * @param fwd True to traverse forwards, false to traverse backwards * @param id HTTP Header ID * @param h Function handler * @param arg Handler argument * * @return HTTP Header if handler returns true, otherwise NULL */ const struct http_hdr *http_msg_hdr_apply(const struct http_msg *msg, bool fwd, enum http_hdrid id, http_hdr_h *h, void *arg) { struct le *le; if (!msg) return NULL; le = fwd ? msg->hdrl.head : msg->hdrl.tail; while (le) { const struct http_hdr *hdr = le->data; le = fwd ? le->next : le->prev; if (hdr->id != id) continue; if (!h || h(hdr, arg)) return hdr; } return NULL; } /** * Get an unknown HTTP Header from a HTTP Message * * @param msg HTTP Message * @param name Header name * * @return HTTP Header if found, NULL if not found */ const struct http_hdr *http_msg_xhdr(const struct http_msg *msg, const char *name) { return http_msg_xhdr_apply(msg, true, name, NULL, NULL); } /** * Apply a function handler to certain unknown HTTP Headers * * @param msg HTTP Message * @param fwd True to traverse forwards, false to traverse backwards * @param name HTTP Header name * @param h Function handler * @param arg Handler argument * * @return HTTP Header if handler returns true, otherwise NULL */ const struct http_hdr *http_msg_xhdr_apply(const struct http_msg *msg, bool fwd, const char *name, http_hdr_h *h, void *arg) { struct le *le; struct pl pl; if (!msg || !name) return NULL; pl_set_str(&pl, name); le = fwd ? msg->hdrl.head : msg->hdrl.tail; while (le) { const struct http_hdr *hdr = le->data; le = fwd ? le->next : le->prev; if (pl_casecmp(&hdr->name, &pl)) continue; if (!h || h(hdr, arg)) return hdr; } return NULL; } static bool count_handler(const struct http_hdr *hdr, void *arg) { uint32_t *n = arg; (void)hdr; ++(*n); return false; } /** * Count the number of HTTP Headers * * @param msg HTTP Message * @param id HTTP Header ID * * @return Number of HTTP Headers */ uint32_t http_msg_hdr_count(const struct http_msg *msg, enum http_hdrid id) { uint32_t n = 0; http_msg_hdr_apply(msg, true, id, count_handler, &n); return n; } /** * Count the number of unknown HTTP Headers * * @param msg HTTP Message * @param name HTTP Header name * * @return Number of HTTP Headers */ uint32_t http_msg_xhdr_count(const struct http_msg *msg, const char *name) { uint32_t n = 0; http_msg_xhdr_apply(msg, true, name, count_handler, &n); return n; } static bool value_handler(const struct http_hdr *hdr, void *arg) { return 0 == pl_strcasecmp(&hdr->val, (const char *)arg); } /** * Check if a HTTP Header matches a certain value * * @param msg HTTP Message * @param id HTTP Header ID * @param value Header value to check * * @return True if value matches, false if not */ bool http_msg_hdr_has_value(const struct http_msg *msg, enum http_hdrid id, const char *value) { return NULL != http_msg_hdr_apply(msg, true, id, value_handler, (void *)value); } /** * Check if an unknown HTTP Header matches a certain value * * @param msg HTTP Message * @param name HTTP Header name * @param value Header value to check * * @return True if value matches, false if not */ bool http_msg_xhdr_has_value(const struct http_msg *msg, const char *name, const char *value) { return NULL != http_msg_xhdr_apply(msg, true, name, value_handler, (void *)value); } /** * Print a HTTP Message * * @param pf Print function for output * @param msg HTTP Message * * @return 0 if success, otherwise errorcode */ int http_msg_print(struct re_printf *pf, const struct http_msg *msg) { struct le *le; int err; if (!msg) return 0; if (pl_isset(&msg->met)) err = re_hprintf(pf, "%r %r%r HTTP/%r\n", &msg->met, &msg->path, &msg->prm, &msg->ver); else err = re_hprintf(pf, "HTTP/%r %u %r\n", &msg->ver, msg->scode, &msg->reason); for (le=msg->hdrl.head; le; le=le->next) { const struct http_hdr *hdr = le->data; err |= re_hprintf(pf, "%r: %r (%i)\n", &hdr->name, &hdr->val, hdr->id); } return err; } re-1.1.0/src/http/server.c000066400000000000000000000211751373627245400153620ustar00rootroot00000000000000/** * @file http/server.c HTTP Server * * Copyright (C) 2011 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include enum { TIMEOUT_IDLE = 600000, TIMEOUT_INIT = 10000, BUFSIZE_MAX = 524288, }; struct http_sock { struct list connl; struct tcp_sock *ts; struct tls *tls; http_req_h *reqh; void *arg; }; struct http_conn { struct le le; struct tmr tmr; struct sa peer; struct http_sock *sock; struct tcp_conn *tc; struct tls_conn *sc; struct mbuf *mb; }; static void conn_close(struct http_conn *conn); static void sock_destructor(void *arg) { struct http_sock *sock = arg; struct le *le; for (le=sock->connl.head; le;) { struct http_conn *conn = le->data; le = le->next; conn_close(conn); mem_deref(conn); } mem_deref(sock->tls); mem_deref(sock->ts); } static void conn_destructor(void *arg) { struct http_conn *conn = arg; list_unlink(&conn->le); tmr_cancel(&conn->tmr); mem_deref(conn->sc); mem_deref(conn->tc); mem_deref(conn->mb); } static void conn_close(struct http_conn *conn) { list_unlink(&conn->le); tmr_cancel(&conn->tmr); conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); conn->sock = NULL; } static void timeout_handler(void *arg) { struct http_conn *conn = arg; conn_close(conn); mem_deref(conn); } static void recv_handler(struct mbuf *mb, void *arg) { struct http_conn *conn = arg; int err = 0; if (conn->mb) { const size_t len = mbuf_get_left(mb), pos = conn->mb->pos; if ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) { err = EOVERFLOW; goto out; } conn->mb->pos = conn->mb->end; err = mbuf_write_mem(conn->mb, mbuf_buf(mb), len); if (err) goto out; conn->mb->pos = pos; } else { conn->mb = mem_ref(mb); } while (conn->mb) { size_t end, pos = conn->mb->pos; struct http_msg *msg; err = http_msg_decode(&msg, conn->mb, true); if (err) { if (err == ENODATA) { conn->mb->pos = pos; err = 0; break; } goto out; } if (mbuf_get_left(conn->mb) < msg->clen) { conn->mb->pos = pos; mem_deref(msg); break; } mem_deref(msg->mb); msg->mb = mem_ref(msg->_mb); mb = conn->mb; end = mb->end; mb->end = mb->pos + msg->clen; if (end > mb->end) { struct mbuf *mbn = mbuf_alloc(end - mb->end); if (!mbn) { mem_deref(msg); err = ENOMEM; goto out; } (void)mbuf_write_mem(mbn, mb->buf + mb->end, end - mb->end); mbn->pos = 0; mem_deref(conn->mb); conn->mb = mbn; } else { conn->mb = mem_deref(conn->mb); } if (conn->sock) conn->sock->reqh(conn, msg, conn->sock->arg); mem_deref(msg); if (!conn->tc) { err = ENOTCONN; goto out; } tmr_start(&conn->tmr, TIMEOUT_IDLE, timeout_handler, conn); } out: if (err) { conn_close(conn); mem_deref(conn); } } static void close_handler(int err, void *arg) { struct http_conn *conn = arg; (void)err; conn_close(conn); mem_deref(conn); } static void connect_handler(const struct sa *peer, void *arg) { struct http_sock *sock = arg; struct http_conn *conn; int err; conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) { err = ENOMEM; goto out; } list_append(&sock->connl, &conn->le, conn); conn->peer = *peer; conn->sock = sock; err = tcp_accept(&conn->tc, sock->ts, NULL, recv_handler, close_handler, conn); if (err) goto out; #ifdef USE_TLS if (sock->tls) { err = tls_start_tcp(&conn->sc, sock->tls, conn->tc, 0); if (err) goto out; } #endif tmr_start(&conn->tmr, TIMEOUT_INIT, timeout_handler, conn); out: if (err) { mem_deref(conn); tcp_reject(sock->ts); } } /** * Create an HTTP socket * * @param sockp Pointer to returned HTTP Socket * @param laddr Network address to listen on * @param reqh Request handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int http_listen(struct http_sock **sockp, const struct sa *laddr, http_req_h *reqh, void *arg) { struct http_sock *sock; int err; if (!sockp || !laddr || !reqh) return EINVAL; sock = mem_zalloc(sizeof(*sock), sock_destructor); if (!sock) return ENOMEM; err = tcp_listen(&sock->ts, laddr, connect_handler, sock); if (err) goto out; sock->reqh = reqh; sock->arg = arg; out: if (err) mem_deref(sock); else *sockp = sock; return err; } /** * Create an HTTP secure socket * * @param sockp Pointer to returned HTTP Socket * @param laddr Network address to listen on * @param cert File path of TLS certificate * @param reqh Request handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int https_listen(struct http_sock **sockp, const struct sa *laddr, const char *cert, http_req_h *reqh, void *arg) { struct http_sock *sock; int err; if (!sockp || !laddr || !cert || !reqh) return EINVAL; err = http_listen(&sock, laddr, reqh, arg); if (err) return err; #ifdef USE_TLS err = tls_alloc(&sock->tls, TLS_METHOD_SSLV23, cert, NULL); #else err = EPROTONOSUPPORT; #endif if (err) goto out; out: if (err) mem_deref(sock); else *sockp = sock; return err; } /** * Get the TCP socket of an HTTP socket * * @param sock HTTP socket * * @return TCP socket */ struct tcp_sock *http_sock_tcp(struct http_sock *sock) { return sock ? sock->ts : NULL; } /** * Get the peer address of an HTTP connection * * @param conn HTTP connection * * @return Peer address */ const struct sa *http_conn_peer(const struct http_conn *conn) { return conn ? &conn->peer : NULL; } /** * Get the TCP connection of an HTTP connection * * @param conn HTTP connection * * @return TCP connection */ struct tcp_conn *http_conn_tcp(struct http_conn *conn) { return conn ? conn->tc : NULL; } /** * Get the TLS connection of an HTTP connection * * @param conn HTTP connection * * @return TLS connection */ struct tls_conn *http_conn_tls(struct http_conn *conn) { return conn ? conn->sc : NULL; } /** * Close the HTTP connection * * @param conn HTTP connection */ void http_conn_close(struct http_conn *conn) { if (!conn) return; conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); } static int http_vreply(struct http_conn *conn, uint16_t scode, const char *reason, const char *fmt, va_list ap) { struct mbuf *mb; int err; if (!conn || !scode || !reason) return EINVAL; if (!conn->tc) return ENOTCONN; mb = mbuf_alloc(8192); if (!mb) return ENOMEM; err = mbuf_printf(mb, "HTTP/1.1 %u %s\r\n", scode, reason); if (fmt) err |= mbuf_vprintf(mb, fmt, ap); else err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n"); if (err) goto out; mb->pos = 0; err = tcp_send(conn->tc, mb); if (err) goto out; out: mem_deref(mb); return err; } /** * Send an HTTP response * * @param conn HTTP connection * @param scode Response status code * @param reason Response reason phrase * @param fmt Formatted HTTP message * * @return 0 if success, otherwise errorcode */ int http_reply(struct http_conn *conn, uint16_t scode, const char *reason, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = http_vreply(conn, scode, reason, fmt, ap); va_end(ap); return err; } /** * Send an HTTP response with content formatting * * @param conn HTTP connection * @param scode Response status code * @param reason Response reason phrase * @param ctype Content type * @param fmt Formatted HTTP content * * @return 0 if success, otherwise errorcode */ int http_creply(struct http_conn *conn, uint16_t scode, const char *reason, const char *ctype, const char *fmt, ...) { struct mbuf *mb; va_list ap; int err; if (!ctype || !fmt) return EINVAL; mb = mbuf_alloc(8192); if (!mb) return ENOMEM; va_start(ap, fmt); err = mbuf_vprintf(mb, fmt, ap); va_end(ap); if (err) goto out; err = http_reply(conn, scode, reason, "Content-Type: %s\r\n" "Content-Length: %zu\r\n" "\r\n" "%b", ctype, mb->end, mb->buf, mb->end); if (err) goto out; out: mem_deref(mb); return err; } /** * Send an HTTP error response * * @param conn HTTP connection * @param scode Response status code * @param reason Response reason phrase * * @return 0 if success, otherwise errorcode */ int http_ereply(struct http_conn *conn, uint16_t scode, const char *reason) { return http_creply(conn, scode, reason, "text/html", "\n" "\n" "%u %s\n" "

%u %s

\n" "\n", scode, reason, scode, reason); } re-1.1.0/src/httpauth/000077500000000000000000000000001373627245400145645ustar00rootroot00000000000000re-1.1.0/src/httpauth/basic.c000066400000000000000000000003211373627245400160050ustar00rootroot00000000000000/** * @file basic.c HTTP Basic authentication * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include /* todo */ re-1.1.0/src/httpauth/digest.c000066400000000000000000000115641373627245400162160ustar00rootroot00000000000000/** * @file digest.c HTTP Digest authentication (RFC 2617) * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include typedef void (digest_decode_h)(const struct pl *name, const struct pl *val, void *arg); static const struct pl param_algorithm = PL("algorithm"); static const struct pl param_cnonce = PL("cnonce"); static const struct pl param_nc = PL("nc"); static const struct pl param_nonce = PL("nonce"); static const struct pl param_opaque = PL("opaque"); static const struct pl param_qop = PL("qop"); static const struct pl param_realm = PL("realm"); static const struct pl param_response = PL("response"); static const struct pl param_uri = PL("uri"); static const struct pl param_username = PL("username"); static const struct pl param_stale = PL("stale"); static void challenge_decode(const struct pl *name, const struct pl *val, void *arg) { struct httpauth_digest_chall *chall = arg; if (!pl_casecmp(name, ¶m_realm)) chall->realm = *val; else if (!pl_casecmp(name, ¶m_nonce)) chall->nonce = *val; else if (!pl_casecmp(name, ¶m_opaque)) chall->opaque= *val; else if (!pl_casecmp(name, ¶m_stale)) chall->stale = *val; else if (!pl_casecmp(name, ¶m_algorithm)) chall->algorithm = *val; else if (!pl_casecmp(name, ¶m_qop)) chall->qop = *val; } static void response_decode(const struct pl *name, const struct pl *val, void *arg) { struct httpauth_digest_resp *resp = arg; if (!pl_casecmp(name, ¶m_realm)) resp->realm = *val; else if (!pl_casecmp(name, ¶m_nonce)) resp->nonce = *val; else if (!pl_casecmp(name, ¶m_response)) resp->response = *val; else if (!pl_casecmp(name, ¶m_username)) resp->username = *val; else if (!pl_casecmp(name, ¶m_uri)) resp->uri = *val; else if (!pl_casecmp(name, ¶m_nc)) resp->nc = *val; else if (!pl_casecmp(name, ¶m_cnonce)) resp->cnonce = *val; else if (!pl_casecmp(name, ¶m_qop)) resp->qop = *val; } static int digest_decode(const struct pl *hval, digest_decode_h *dech, void *arg) { struct pl r = *hval, start, end, name, val; if (re_regex(r.p, r.l, "[ \t\r\n]*Digest[ \t\r\n]+", &start, &end) || start.p != r.p) return EBADMSG; pl_advance(&r, end.p - r.p); while (!re_regex(r.p, r.l, "[ \t\r\n,]+[a-z]+[ \t\r\n]*=[ \t\r\n]*[~ \t\r\n,]*", NULL, &name, NULL, NULL, &val)) { pl_advance(&r, val.p + val.l - r.p); dech(&name, &val, arg); } return 0; } /** * Decode a Digest challenge * * @param chall Digest challenge object to decode into * @param hval Header value to decode from * * @return 0 if successfully decoded, otherwise errorcode */ int httpauth_digest_challenge_decode(struct httpauth_digest_chall *chall, const struct pl *hval) { int err; if (!chall || !hval) return EINVAL; memset(chall, 0, sizeof(*chall)); err = digest_decode(hval, challenge_decode, chall); if (err) return err; if (!chall->realm.p || !chall->nonce.p) return EBADMSG; return 0; } /** * Decode a Digest response * * @param resp Digest response object to decode into * @param hval Header value to decode from * * @return 0 if successfully decoded, otherwise errorcode */ int httpauth_digest_response_decode(struct httpauth_digest_resp *resp, const struct pl *hval) { int err; if (!resp || !hval) return EINVAL; memset(resp, 0, sizeof(*resp)); err = digest_decode(hval, response_decode, resp); if (err) return err; if (!resp->realm.p || !resp->nonce.p || !resp->response.p || !resp->username.p || !resp->uri.p) return EBADMSG; return 0; } /** * Authenticate a digest response * * @param resp Digest response * @param method Request method * @param ha1 HA1 value from MD5(username:realm:password) * * @return 0 if successfully authenticated, otherwise errorcode */ int httpauth_digest_response_auth(const struct httpauth_digest_resp *resp, const struct pl *method, const uint8_t *ha1) { uint8_t ha2[MD5_SIZE], digest[MD5_SIZE], response[MD5_SIZE]; const char *p; uint32_t i; int err; if (!resp || !method || !ha1) return EINVAL; if (resp->response.l != 32) return EAUTH; err = md5_printf(ha2, "%r:%r", method, &resp->uri); if (err) return err; if (pl_isset(&resp->qop)) err = md5_printf(digest, "%w:%r:%r:%r:%r:%w", ha1, (size_t)MD5_SIZE, &resp->nonce, &resp->nc, &resp->cnonce, &resp->qop, ha2, sizeof(ha2)); else err = md5_printf(digest, "%w:%r:%w", ha1, (size_t)MD5_SIZE, &resp->nonce, ha2, sizeof(ha2)); if (err) return err; for (i=0, p=resp->response.p; i #include #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "icecand" #define DEBUG_LEVEL 5 #include static void cand_destructor(void *arg) { struct ice_cand *cand = arg; list_unlink(&cand->le); mem_deref(cand->foundation); mem_deref(cand->ifname); if (cand != cand->base) mem_deref(cand->base); } /** Foundation is a hash of IP address and candidate type */ static int compute_foundation(struct ice_cand *cand) { uint32_t v; v = sa_hash(&cand->addr, SA_ADDR); v ^= cand->type; return re_sdprintf(&cand->foundation, "%08x", v); } static int cand_alloc(struct ice_cand **candp, struct icem *icem, enum ice_cand_type type, unsigned compid, uint32_t prio, const char *ifname, enum ice_transp transp, const struct sa *addr) { struct ice_cand *cand; int err; if (!icem) return EINVAL; cand = mem_zalloc(sizeof(*cand), cand_destructor); if (!cand) return ENOMEM; list_append(&icem->lcandl, &cand->le, cand); cand->type = type; cand->compid = compid; cand->prio = prio; cand->transp = transp; sa_cpy(&cand->addr, addr); err = compute_foundation(cand); if (ifname) err |= str_dup(&cand->ifname, ifname); if (err) mem_deref(cand); else if (candp) *candp = cand; return err; } int icem_lcand_add_base(struct icem *icem, unsigned compid, uint16_t lprio, const char *ifname, enum ice_transp transp, const struct sa *addr) { struct icem_comp *comp; struct ice_cand *cand; int err; comp = icem_comp_find(icem, compid); if (!comp) return ENOENT; err = cand_alloc(&cand, icem, ICE_CAND_TYPE_HOST, compid, ice_cand_calc_prio(ICE_CAND_TYPE_HOST, lprio, compid), ifname, transp, addr); if (err) return err; /* the base is itself */ cand->base = cand; sa_set_port(&cand->addr, comp->lport); return 0; } int icem_lcand_add(struct icem *icem, struct ice_cand *base, enum ice_cand_type type, const struct sa *addr) { struct ice_cand *cand; int err; if (!base) return EINVAL; err = cand_alloc(&cand, icem, type, base->compid, ice_cand_calc_prio(type, 0, base->compid), base->ifname, base->transp, addr); if (err) return err; cand->base = mem_ref(base); sa_cpy(&cand->rel, &base->addr); return 0; } int icem_rcand_add(struct icem *icem, enum ice_cand_type type, unsigned compid, uint32_t prio, const struct sa *addr, const struct sa *rel_addr, const struct pl *foundation) { struct ice_cand *rcand; int err; if (!icem || !foundation) return EINVAL; rcand = mem_zalloc(sizeof(*rcand), cand_destructor); if (!rcand) return ENOMEM; list_append(&icem->rcandl, &rcand->le, rcand); rcand->type = type; rcand->compid = compid; rcand->prio = prio; sa_cpy(&rcand->addr, addr); sa_cpy(&rcand->rel, rel_addr); err = pl_strdup(&rcand->foundation, foundation); if (err) mem_deref(rcand); return err; } int icem_rcand_add_prflx(struct ice_cand **rcp, struct icem *icem, unsigned compid, uint32_t prio, const struct sa *addr) { struct ice_cand *rcand; int err; if (!icem || !addr) return EINVAL; rcand = mem_zalloc(sizeof(*rcand), cand_destructor); if (!rcand) return ENOMEM; list_append(&icem->rcandl, &rcand->le, rcand); rcand->type = ICE_CAND_TYPE_PRFLX; rcand->compid = compid; rcand->prio = prio; rcand->addr = *addr; err = re_sdprintf(&rcand->foundation, "%08x", rand_u32()); if (err) goto out; icecomp_printf(icem_comp_find(icem, compid), "added PeerReflexive remote candidate" " with priority %u (%J)\n", prio, addr); out: if (err) mem_deref(rcand); else if (rcp) *rcp = rcand; return err; } struct ice_cand *icem_cand_find(const struct list *lst, unsigned compid, const struct sa *addr) { struct le *le; for (le = list_head(lst); le; le = le->next) { struct ice_cand *cand = le->data; if (compid && cand->compid != compid) continue; if (addr && !sa_cmp(&cand->addr, addr, SA_ALL)) continue; return cand; } return NULL; } /** * Find the highest priority LCAND on the check-list of type HOST/RELAY * * @param icem ICE Media object * @param compid Component ID * * @return Local candidate if found, otherwise NULL */ struct ice_cand *icem_lcand_find_checklist(const struct icem *icem, unsigned compid) { struct le *le; for (le = icem->checkl.head; le; le = le->next) { struct ice_candpair *cp = le->data; if (cp->lcand->compid != compid) continue; switch (cp->lcand->type) { case ICE_CAND_TYPE_HOST: case ICE_CAND_TYPE_RELAY: return cp->lcand; default: break; } } return NULL; } struct ice_cand *icem_lcand_base(struct ice_cand *lcand) { return lcand ? lcand->base : NULL; } const struct sa *icem_lcand_addr(const struct ice_cand *cand) { return cand ? &cand->addr : NULL; } int icem_cands_debug(struct re_printf *pf, const struct list *lst) { struct le *le; int err; err = re_hprintf(pf, " (%u)\n", list_count(lst)); for (le = list_head(lst); le && !err; le = le->next) { const struct ice_cand *cand = le->data; err |= re_hprintf(pf, " {%u} fnd=%-2s prio=%08x %24H", cand->compid, cand->foundation, cand->prio, icem_cand_print, cand); if (sa_isset(&cand->rel, SA_ADDR)) err |= re_hprintf(pf, " (rel-addr=%J)", &cand->rel); err |= re_hprintf(pf, "\n"); } return err; } int icem_cand_print(struct re_printf *pf, const struct ice_cand *cand) { int err = 0; if (!cand) return 0; if (cand->ifname) err |= re_hprintf(pf, "%s:", cand->ifname); err |= re_hprintf(pf, "%s:%J", ice_cand_type2name(cand->type), &cand->addr); return err; } enum ice_cand_type icem_cand_type(const struct ice_cand *cand) { return cand ? cand->type : (enum ice_cand_type)-1; } re-1.1.0/src/ice/candpair.c000066400000000000000000000205441373627245400154150ustar00rootroot00000000000000/** * @file candpair.c ICE Candidate Pairs * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "cndpair" #define DEBUG_LEVEL 5 #include static void candpair_destructor(void *arg) { struct ice_candpair *cp = arg; list_unlink(&cp->le); mem_deref(cp->ct_conn); mem_deref(cp->lcand); mem_deref(cp->rcand); } static bool sort_handler(struct le *le1, struct le *le2, void *arg) { const struct ice_candpair *cp1 = le1->data, *cp2 = le2->data; (void)arg; return cp1->pprio >= cp2->pprio; } static void candpair_set_pprio(struct ice_candpair *cp) { uint32_t g, d; if (ICE_ROLE_CONTROLLING == cp->icem->lrole) { g = cp->lcand->prio; d = cp->rcand->prio; } else { g = cp->rcand->prio; d = cp->lcand->prio; } cp->pprio = ice_calc_pair_prio(g, d); } /** * Add candidate pair to list, sorted by pair priority (highest is first) */ static void list_add_sorted(struct list *list, struct ice_candpair *cp) { struct le *le; /* find our slot */ for (le = list_tail(list); le; le = le->prev) { struct ice_candpair *cp0 = le->data; if (cp->pprio < cp0->pprio) { list_insert_after(list, le, &cp->le, cp); return; } } list_prepend(list, &cp->le, cp); } int icem_candpair_alloc(struct ice_candpair **cpp, struct icem *icem, struct ice_cand *lcand, struct ice_cand *rcand) { struct ice_candpair *cp; struct icem_comp *comp; if (!icem || !lcand || !rcand) return EINVAL; comp = icem_comp_find(icem, lcand->compid); if (!comp) return ENOENT; cp = mem_zalloc(sizeof(*cp), candpair_destructor); if (!cp) return ENOMEM; cp->icem = icem; cp->comp = comp; cp->lcand = mem_ref(lcand); cp->rcand = mem_ref(rcand); cp->state = ICE_CANDPAIR_FROZEN; cp->def = comp->def_lcand == lcand && comp->def_rcand == rcand; candpair_set_pprio(cp); list_add_sorted(&icem->checkl, cp); if (cpp) *cpp = cp; return 0; } int icem_candpair_clone(struct ice_candpair **cpp, struct ice_candpair *cp0, struct ice_cand *lcand, struct ice_cand *rcand) { struct ice_candpair *cp; if (!cp0) return EINVAL; cp = mem_zalloc(sizeof(*cp), candpair_destructor); if (!cp) return ENOMEM; cp->icem = cp0->icem; cp->comp = cp0->comp; cp->lcand = mem_ref(lcand ? lcand : cp0->lcand); cp->rcand = mem_ref(rcand ? rcand : cp0->rcand); cp->def = cp0->def; cp->valid = cp0->valid; cp->nominated = cp0->nominated; cp->state = cp0->state; cp->pprio = cp0->pprio; cp->err = cp0->err; cp->scode = cp0->scode; list_add_sorted(&cp0->icem->checkl, cp); if (cpp) *cpp = cp; return 0; } /** * Computing Pair Priority and Ordering Pairs * * @param lst Checklist (struct ice_candpair) */ void icem_candpair_prio_order(struct list *lst) { struct le *le; for (le = list_head(lst); le; le = le->next) { struct ice_candpair *cp = le->data; candpair_set_pprio(cp); } list_sort(lst, sort_handler, NULL); } /* cancel transaction */ void icem_candpair_cancel(struct ice_candpair *cp) { if (!cp) return; cp->ct_conn = mem_deref(cp->ct_conn); } void icem_candpair_make_valid(struct ice_candpair *cp) { if (!cp) return; cp->err = 0; cp->scode = 0; cp->valid = true; icem_candpair_set_state(cp, ICE_CANDPAIR_SUCCEEDED); list_unlink(&cp->le); list_add_sorted(&cp->icem->validl, cp); } void icem_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode) { if (!cp) return; cp->err = err; cp->scode = scode; cp->valid = false; icem_candpair_set_state(cp, ICE_CANDPAIR_FAILED); } void icem_candpair_set_state(struct ice_candpair *cp, enum ice_candpair_state state) { if (!cp) return; if (cp->state == state || icem_candpair_iscompleted(cp)) return; icecomp_printf(cp->comp, "%5s <---> %5s FSM: %10s ===> %-10s\n", ice_cand_type2name(cp->lcand->type), ice_cand_type2name(cp->rcand->type), ice_candpair_state2name(cp->state), ice_candpair_state2name(state)); cp->state = state; } /** * Delete all Candidate-Pairs where the Local candidate is of a given type * * @param lst Checklist or Validlist * @param type Candidate type * @param compid Component ID */ void icem_candpairs_flush(struct list *lst, enum ice_cand_type type, unsigned compid) { struct le *le = list_head(lst); while (le) { struct ice_candpair *cp = le->data; le = le->next; if (cp->lcand->compid != compid) continue; if (cp->lcand->type != type) continue; mem_deref(cp); } } bool icem_candpair_iscompleted(const struct ice_candpair *cp) { if (!cp) return false; return cp->state == ICE_CANDPAIR_FAILED || cp->state == ICE_CANDPAIR_SUCCEEDED; } /** * Compare local and remote candidates of two candidate pairs * * @param cp1 First Candidate pair * @param cp2 Second Candidate pair * * @return true if match */ bool icem_candpair_cmp(const struct ice_candpair *cp1, const struct ice_candpair *cp2) { if (!sa_cmp(&cp1->lcand->addr, &cp2->lcand->addr, SA_ALL)) return false; return sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL); } /** * Find the highest-priority candidate-pair in a given list, with * optional match parameters * * @param lst List of candidate pairs * @param lcand Local candidate (optional) * @param rcand Remote candidate (optional) * * @return Matching candidate pair if found, otherwise NULL * * note: assume list is sorted by priority */ struct ice_candpair *icem_candpair_find(const struct list *lst, const struct ice_cand *lcand, const struct ice_cand *rcand) { struct le *le; for (le = list_head(lst); le; le = le->next) { struct ice_candpair *cp = le->data; if (!cp->lcand || !cp->rcand) { DEBUG_WARNING("corrupt candpair %p\n", cp); continue; } if (lcand && cp->lcand != lcand) continue; if (rcand && cp->rcand != rcand) continue; return cp; } return NULL; } struct ice_candpair *icem_candpair_find_st(const struct list *lst, unsigned compid, enum ice_candpair_state state) { struct le *le; for (le = list_head(lst); le; le = le->next) { struct ice_candpair *cp = le->data; if (compid && cp->lcand->compid != compid) continue; if (cp->state != state) continue; return cp; } return NULL; } struct ice_candpair *icem_candpair_find_compid(const struct list *lst, unsigned compid) { struct le *le; for (le = list_head(lst); le; le = le->next) { struct ice_candpair *cp = le->data; if (cp->lcand->compid != compid) continue; return cp; } return NULL; } /** * Find a remote candidate in the checklist or validlist * * @param icem ICE Media object * @param rcand Remote candidate * * @return Candidate pair if found, otherwise NULL */ struct ice_candpair *icem_candpair_find_rcand(struct icem *icem, const struct ice_cand *rcand) { struct ice_candpair *cp; cp = icem_candpair_find(&icem->checkl, NULL, rcand); if (cp) return cp; cp = icem_candpair_find(&icem->validl, NULL, rcand); if (cp) return cp; return NULL; } bool icem_candpair_cmp_fnd(const struct ice_candpair *cp1, const struct ice_candpair *cp2) { if (!cp1 || !cp2) return false; return 0 == strcmp(cp1->lcand->foundation, cp2->lcand->foundation) && 0 == strcmp(cp1->rcand->foundation, cp2->rcand->foundation); } int icem_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp) { int err; if (!cp) return 0; err = re_hprintf(pf, "{comp=%u} %10s {%c%c%c} %28H <---> %28H", cp->lcand->compid, ice_candpair_state2name(cp->state), cp->def ? 'D' : ' ', cp->valid ? 'V' : ' ', cp->nominated ? 'N' : ' ', icem_cand_print, cp->lcand, icem_cand_print, cp->rcand); if (cp->err) err |= re_hprintf(pf, " (%m)", cp->err); if (cp->scode) err |= re_hprintf(pf, " [%u]", cp->scode); return err; } int icem_candpairs_debug(struct re_printf *pf, const struct list *list) { struct le *le; int err; if (!list) return 0; err = re_hprintf(pf, " (%u)\n", list_count(list)); for (le = list->head; le && !err; le = le->next) { const struct ice_candpair *cp = le->data; bool is_selected = (cp == cp->comp->cp_sel); err = re_hprintf(pf, " %c %H\n", is_selected ? '*' : ' ', icem_candpair_debug, cp); } return err; } re-1.1.0/src/ice/chklist.c000066400000000000000000000157551373627245400153050ustar00rootroot00000000000000/** * @file chklist.c ICE Checklist * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "chklist" #define DEBUG_LEVEL 5 #include /** * Forming Candidate Pairs */ static int candpairs_form(struct icem *icem) { struct le *le; int err = 0; if (list_isempty(&icem->lcandl)) return ENOENT; if (list_isempty(&icem->rcandl)) { DEBUG_WARNING("%s: no remote candidates\n", icem->name); return ENOENT; } for (le = icem->lcandl.head; le; le = le->next) { struct ice_cand *lcand = le->data; struct le *rle; for (rle = icem->rcandl.head; rle; rle = rle->next) { struct ice_cand *rcand = rle->data; if (lcand->compid != rcand->compid) continue; if (sa_af(&lcand->addr) != sa_af(&rcand->addr)) continue; err = icem_candpair_alloc(NULL, icem, lcand, rcand); if (err) return err; } } return err; } /* Replace server reflexive candidates by its base */ static const struct sa *cand_srflx_addr(const struct ice_cand *c) { return (ICE_CAND_TYPE_SRFLX == c->type) ? &c->base->addr : &c->addr; } /* return: NULL to keep, pointer to remove object */ static void *unique_handler(struct le *le1, struct le *le2) { struct ice_candpair *cp1 = le1->data, *cp2 = le2->data; if (cp1->comp->id != cp2->comp->id) return NULL; if (!sa_cmp(cand_srflx_addr(cp1->lcand), cand_srflx_addr(cp2->lcand), SA_ALL) || !sa_cmp(&cp1->rcand->addr, &cp2->rcand->addr, SA_ALL)) return NULL; return cp1->pprio < cp2->pprio ? cp1 : cp2; } /** * Pruning the Pairs */ static void candpair_prune(struct icem *icem) { /* The agent MUST prune the list. This is done by removing a pair if its local and remote candidates are identical to the local and remote candidates of a pair higher up on the priority list. NOTE: This logic assumes the list is sorted by priority */ uint32_t n = ice_list_unique(&icem->checkl, unique_handler); if (n > 0) { DEBUG_NOTICE("%s: pruned candidate pairs: %u\n", icem->name, n); } } /** * Computing States * * @param icem ICE Media object */ void ice_candpair_set_states(struct icem *icem) { struct le *le, *le2; /* For all pairs with the same foundation, it sets the state of the pair with the lowest component ID to Waiting. If there is more than one such pair, the one with the highest priority is used. */ for (le = icem->checkl.head; le; le = le->next) { struct ice_candpair *cp = le->data; for (le2 = icem->checkl.head; le2; le2 = le2->next) { struct ice_candpair *cp2 = le2->data; if (!icem_candpair_cmp_fnd(cp, cp2)) continue; if (cp2->lcand->compid < cp->lcand->compid && cp2->pprio > cp->pprio) cp = cp2; } icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING); } } /** * Forming the Check Lists * * To form the check list for a media stream, * the agent forms candidate pairs, computes a candidate pair priority, * orders the pairs by priority, prunes them, and sets their states. * These steps are described in this section. * * @param icem ICE Media object * * @return 0 if success, otherwise errorcode */ int icem_checklist_form(struct icem *icem) { int err; if (!icem) return EINVAL; if (!list_isempty(&icem->checkl)) return EALREADY; /* 1. form candidate pairs */ err = candpairs_form(icem); if (err) return err; /* 2. compute a candidate pair priority */ /* 3. order the pairs by priority */ icem_candpair_prio_order(&icem->checkl); /* 4. prune the pairs */ candpair_prune(icem); return err; } /* If all of the pairs in the check list are now either in the Failed or Succeeded state: */ static bool iscompleted(const struct icem *icem) { struct le *le; for (le = icem->checkl.head; le; le = le->next) { const struct ice_candpair *cp = le->data; if (!icem_candpair_iscompleted(cp)) return false; } return true; } /* 8. Concluding ICE Processing */ static void concluding_ice(struct icem_comp *comp) { struct ice_candpair *cp; bool use_cand; if (!comp || comp->concluded) return; /* pick the best candidate pair, highest priority */ cp = icem_candpair_find_st(&comp->icem->validl, comp->id, ICE_CANDPAIR_SUCCEEDED); if (!cp) { DEBUG_WARNING("{%s.%u} conclude: no valid candpair found" " (validlist=%u)\n", comp->icem->name, comp->id, list_count(&comp->icem->validl)); return; } icem_comp_set_selected(comp, cp); /* Regular nomination */ use_cand = comp->icem->lrole == ICE_ROLE_CONTROLLING; /* send STUN request with USE_CAND flag via triggered qeueue */ (void)icem_conncheck_send(cp, use_cand, true); icem_conncheck_schedule_check(comp->icem); comp->concluded = true; } /** * Check List and Timer State Updates * * @param icem ICE Media object */ void icem_checklist_update(struct icem *icem) { struct le *le; bool compl; int err = 0; compl = iscompleted(icem); if (!compl) return; /* * If there is not a pair in the valid list for each component of the * media stream, the state of the check list is set to Failed. */ for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; if (!icem_candpair_find_compid(&icem->validl, comp->id)) { DEBUG_WARNING("{%s.%u} no valid candidate pair" " (validlist=%u)\n", icem->name, comp->id, list_count(&icem->validl)); err = ENOENT; break; } concluding_ice(comp); if (!comp->cp_sel) continue; icem_comp_keepalive(comp, true); } icem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED; if (icem->chkh) { icem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING, icem->arg); } } /** * Get the Local address of the Selected Candidate pair, if available * * @param icem ICE Media object * @param compid Component ID * * @return Local address if available, otherwise NULL */ const struct sa *icem_selected_laddr(const struct icem *icem, unsigned compid) { const struct ice_cand *cand = icem_selected_lcand(icem, compid); return icem_lcand_addr(cand); } /** * Get the Local candidate of the Selected Candidate pair, if available * * @param icem ICE Media object * @param compid Component ID * * @return Local candidate if available, otherwise NULL */ const struct ice_cand *icem_selected_lcand(const struct icem *icem, unsigned compid) { const struct icem_comp *comp = icem_comp_find(icem, compid); if (!comp || !comp->cp_sel) return NULL; return comp->cp_sel->lcand; } /** * Get the Remote candidate of the Selected Candidate pair, if available * * @param icem ICE Media object * @param compid Component ID * * @return Remote candidate if available, otherwise NULL */ const struct ice_cand *icem_selected_rcand(const struct icem *icem, unsigned compid) { const struct icem_comp *comp = icem_comp_find(icem, compid); if (!comp || !comp->cp_sel) return NULL; return comp->cp_sel->rcand; } re-1.1.0/src/ice/comp.c000066400000000000000000000127401373627245400145710ustar00rootroot00000000000000/** * @file comp.c ICE Media component * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "icecomp" #define DEBUG_LEVEL 5 #include enum {COMPID_MIN = 1, COMPID_MAX = 255}; static bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct icem_comp *comp = arg; struct icem *icem = comp->icem; struct stun_msg *msg = NULL; struct stun_unknown_attr ua; const size_t start = mb->pos; #if 0 re_printf("{%d} UDP recv_helper: %u bytes from %J\n", comp->id, mbuf_get_left(mb), src); #endif if (stun_msg_decode(&msg, mb, &ua)) return false; if (STUN_METHOD_BINDING == stun_msg_method(msg)) { switch (stun_msg_class(msg)) { case STUN_CLASS_REQUEST: (void)icem_stund_recv(comp, src, msg, start); break; default: (void)stun_ctrans_recv(icem->stun, msg, &ua); break; } } mem_deref(msg); return true; /* handled */ } static void destructor(void *arg) { struct icem_comp *comp = arg; tmr_cancel(&comp->tmr_ka); mem_deref(comp->turnc); mem_deref(comp->cp_sel); mem_deref(comp->def_lcand); mem_deref(comp->def_rcand); mem_deref(comp->uh); mem_deref(comp->sock); } static struct ice_cand *cand_default(const struct list *lcandl, unsigned compid) { struct ice_cand *def = NULL; struct le *le; /* NOTE: list must be sorted by priority */ for (le = list_head(lcandl); le; le = le->next) { struct ice_cand *cand = le->data; if (cand->compid != compid) continue; switch (cand->type) { case ICE_CAND_TYPE_RELAY: return cand; case ICE_CAND_TYPE_SRFLX: if (!def || ICE_CAND_TYPE_SRFLX != def->type) def = cand; break; case ICE_CAND_TYPE_HOST: if (!def) def = cand; break; default: break; } } return def; } int icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id, void *sock) { struct icem_comp *comp; struct sa local; int err; if (!cp || !icem || id<1 || id>255 || !sock) return EINVAL; comp = mem_zalloc(sizeof(*comp), destructor); if (!comp) return ENOMEM; comp->id = id; comp->sock = mem_ref(sock); comp->icem = icem; err = udp_register_helper(&comp->uh, sock, icem->layer, NULL, helper_recv_handler, comp); if (err) goto out; err = udp_local_get(comp->sock, &local); if (err) goto out; comp->lport = sa_port(&local); out: if (err) mem_deref(comp); else *cp = comp; return err; } int icem_comp_set_default_cand(struct icem_comp *comp) { struct ice_cand *cand; if (!comp) return EINVAL; cand = cand_default(&comp->icem->lcandl, comp->id); if (!cand) return ENOENT; mem_deref(comp->def_lcand); comp->def_lcand = mem_ref(cand); return 0; } void icem_comp_set_default_rcand(struct icem_comp *comp, struct ice_cand *rcand) { if (!comp) return; icecomp_printf(comp, "Set default remote candidate: %s:%J\n", ice_cand_type2name(rcand->type), &rcand->addr); mem_deref(comp->def_rcand); comp->def_rcand = mem_ref(rcand); if (comp->turnc) { icecomp_printf(comp, "Add TURN Channel to peer %J\n", &rcand->addr); (void)turnc_add_chan(comp->turnc, &rcand->addr, NULL, NULL); } } void icem_comp_set_selected(struct icem_comp *comp, struct ice_candpair *cp) { if (!comp || !cp) return; if (cp->state != ICE_CANDPAIR_SUCCEEDED) { DEBUG_WARNING("{%s.%u} set_selected: invalid state '%s'" " [%H]\n", comp->icem->name, comp->id, ice_candpair_state2name(cp->state), icem_candpair_debug, cp); } mem_deref(comp->cp_sel); comp->cp_sel = mem_ref(cp); } struct icem_comp *icem_comp_find(const struct icem *icem, unsigned compid) { struct le *le; if (!icem) return NULL; for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; if (comp->id == compid) return comp; } return NULL; } static void timeout(void *arg) { struct icem_comp *comp = arg; struct ice_candpair *cp; tmr_start(&comp->tmr_ka, ICE_DEFAULT_Tr * 1000 + rand_u16() % 1000, timeout, comp); /* find selected candidate-pair */ cp = comp->cp_sel; if (!cp) return; (void)stun_indication(comp->icem->proto, comp->sock, &cp->rcand->addr, (cp->lcand->type == ICE_CAND_TYPE_RELAY) ? 4 : 0, STUN_METHOD_BINDING, NULL, 0, true, 0); } void icem_comp_keepalive(struct icem_comp *comp, bool enable) { if (!comp) return; if (enable) { tmr_start(&comp->tmr_ka, ICE_DEFAULT_Tr * 1000, timeout, comp); } else { tmr_cancel(&comp->tmr_ka); } } void icecomp_printf(struct icem_comp *comp, const char *fmt, ...) { va_list ap; if (!comp || !comp->icem->conf.debug) return; va_start(ap, fmt); (void)re_printf("{%11s.%u} %v", comp->icem->name, comp->id, fmt, &ap); va_end(ap); } int icecomp_debug(struct re_printf *pf, const struct icem_comp *comp) { if (!comp) return 0; return re_hprintf(pf, "id=%u ldef=%J rdef=%J concluded=%d", comp->id, comp->def_lcand ? &comp->def_lcand->addr : NULL, comp->def_rcand ? &comp->def_rcand->addr : NULL, comp->concluded); } int icem_set_turn_client(struct icem *icem, unsigned compid, struct turnc *turnc) { struct icem_comp *comp; comp = icem_comp_find(icem, compid); if (!comp) return ENOENT; comp->turnc = mem_deref(comp->turnc); if (turnc) comp->turnc = mem_ref(turnc); return 0; } re-1.1.0/src/ice/connchk.c000066400000000000000000000222501373627245400152530ustar00rootroot00000000000000/** * @file connchk.c ICE Connectivity Checks * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "connchk" #define DEBUG_LEVEL 5 #include static void pace_next(struct icem *icem) { if (icem->state != ICE_CHECKLIST_RUNNING) return; icem_conncheck_schedule_check(icem); if (icem->state == ICE_CHECKLIST_FAILED) return; icem_checklist_update(icem); } /** * Constructing a Valid Pair * * @return The valid pair */ static struct ice_candpair *construct_valid_pair(struct icem *icem, struct ice_candpair *cp, const struct sa *mapped, const struct sa *dest) { struct ice_cand *lcand, *rcand; struct ice_candpair *cp2; int err; lcand = icem_cand_find(&icem->lcandl, cp->lcand->compid, mapped); rcand = icem_cand_find(&icem->rcandl, cp->rcand->compid, dest); if (!lcand) { DEBUG_WARNING("no such local candidate: %J\n", mapped); return NULL; } if (!rcand) { DEBUG_WARNING("no such remote candidate: %J\n", dest); return NULL; } /* New candidate? -- implicit success */ if (lcand != cp->lcand || rcand != cp->rcand) { if (lcand != cp->lcand) { icecomp_printf(cp->comp, "New local candidate for mapped %J\n", mapped); } if (rcand != cp->rcand) { icecomp_printf(cp->comp, "New remote candidate for dest %J\n", dest); } /* The original candidate pair is set to 'Failed' because * the implicitly discovered pair is 'better'. * This happens for UAs behind NAT where the original * pair is of type 'host' and the implicit pair is 'srflx' */ icem_candpair_make_valid(cp); cp2 = icem_candpair_find(&icem->validl, lcand, rcand); if (cp2) return cp2; err = icem_candpair_clone(&cp2, cp, lcand, rcand); if (err) return NULL; icem_candpair_make_valid(cp2); /*icem_candpair_failed(cp, EINTR, 0);*/ return cp2; } else { /* Add to VALID LIST, the pair that generated the check */ icem_candpair_make_valid(cp); return cp; } } static void handle_success(struct icem *icem, struct ice_candpair *cp, const struct sa *laddr) { if (!icem_cand_find(&icem->lcandl, cp->lcand->compid, laddr)) { int err; icecomp_printf(cp->comp, "adding local PRFLX Candidate: %J\n", laddr); err = icem_lcand_add(icem, cp->lcand, ICE_CAND_TYPE_PRFLX, laddr); if (err) { DEBUG_WARNING("failed to add PRFLX: %m\n", err); } } cp = construct_valid_pair(icem, cp, laddr, &cp->rcand->addr); if (!cp) { DEBUG_WARNING("{%s} no valid candidate pair for %J\n", icem->name, laddr); return; } icem_candpair_make_valid(cp); icem_comp_set_selected(cp->comp, cp); cp->nominated = true; #if 0 /* stop conncheck now -- conclude */ icem_conncheck_stop(icem, 0); #endif } #if ICE_TRACE static int print_err(struct re_printf *pf, const int *err) { if (err && *err) return re_hprintf(pf, " (%m)", *err); return 0; } #endif static void stunc_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct ice_candpair *cp = arg; struct icem *icem = cp->icem; struct stun_attr *attr; (void)reason; #if ICE_TRACE icecomp_printf(cp->comp, "Rx %H <--- %H '%u %s'%H\n", icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, scode, reason, print_err, &err); #endif if (err) { icem_candpair_failed(cp, err, scode); goto out; } switch (scode) { case 0: /* Success case */ attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { DEBUG_WARNING("no XOR-MAPPED-ADDR in response\n"); icem_candpair_failed(cp, EBADMSG, 0); break; } handle_success(icem, cp, &attr->v.sa); break; case 487: /* Role Conflict */ ice_switch_local_role(icem); (void)icem_conncheck_send(cp, false, true); break; default: DEBUG_WARNING("{%s.%u} STUN Response: %u %s\n", icem->name, cp->comp->id, scode, reason); icem_candpair_failed(cp, err, scode); break; } out: pace_next(icem); } int icem_conncheck_send(struct ice_candpair *cp, bool use_cand, bool trigged) { struct ice_cand *lcand = cp->lcand; struct icem *icem = cp->icem; char username_buf[64]; size_t presz = 0; uint32_t prio_prflx; uint16_t ctrl_attr; int err = 0; icem_candpair_set_state(cp, ICE_CANDPAIR_INPROGRESS); (void)re_snprintf(username_buf, sizeof(username_buf), "%s:%s", icem->rufrag, icem->lufrag); /* PRIORITY and USE-CANDIDATE */ prio_prflx = ice_cand_calc_prio(ICE_CAND_TYPE_PRFLX, 0, lcand->compid); switch (icem->lrole) { case ICE_ROLE_CONTROLLING: ctrl_attr = STUN_ATTR_CONTROLLING; break; case ICE_ROLE_CONTROLLED: ctrl_attr = STUN_ATTR_CONTROLLED; if (use_cand) { DEBUG_WARNING("send: use_cand=true, but" " role is controlled (trigged=%d)" " [%H]\n", trigged, icem_candpair_debug, cp); return EINVAL; } break; default: return EINVAL; } #if ICE_TRACE icecomp_printf(cp->comp, "Tx %H ---> %H (%s) %s %s\n", icem_cand_print, cp->lcand, icem_cand_print, cp->rcand, ice_candpair_state2name(cp->state), use_cand ? "[USE]" : "", trigged ? "[Trigged]" : ""); #else (void)trigged; #endif /* A connectivity check MUST utilize the STUN short term credential mechanism. */ /* The password is equal to the password provided by the peer */ if (!icem->rpwd) { DEBUG_WARNING("no remote password!\n"); } if (cp->ct_conn) { DEBUG_WARNING("send_req: CONNCHECK already Pending!\n"); return EBUSY; } switch (lcand->type) { case ICE_CAND_TYPE_RELAY: /* Creating Permissions for Relayed Candidates */ err = turnc_add_chan(cp->comp->turnc, &cp->rcand->addr, NULL, NULL); if (err) { DEBUG_WARNING("add channel: %m\n", err); break; } presz = 4; /*@fallthrough@*/ case ICE_CAND_TYPE_HOST: case ICE_CAND_TYPE_SRFLX: case ICE_CAND_TYPE_PRFLX: cp->ct_conn = mem_deref(cp->ct_conn); err = stun_request(&cp->ct_conn, icem->stun, icem->proto, cp->comp->sock, &cp->rcand->addr, presz, STUN_METHOD_BINDING, (uint8_t *)icem->rpwd, str_len(icem->rpwd), true, stunc_resp_handler, cp, 4, STUN_ATTR_USERNAME, username_buf, STUN_ATTR_PRIORITY, &prio_prflx, ctrl_attr, &icem->tiebrk, STUN_ATTR_USE_CAND, use_cand ? &use_cand : 0); break; default: DEBUG_WARNING("unknown candidate type %d\n", lcand->type); err = EINVAL; break; } return err; } static void abort_ice(struct icem *icem, int err) { icem->state = ICE_CHECKLIST_FAILED; tmr_cancel(&icem->tmr_pace); if (icem->chkh) { icem->chkh(err, icem->lrole == ICE_ROLE_CONTROLLING, icem->arg); } icem->chkh = NULL; } static void do_check(struct ice_candpair *cp) { int err; err = icem_conncheck_send(cp, false, false); if (err) { icem_candpair_failed(cp, err, 0); if (err == ENOMEM) { abort_ice(cp->icem, err); } else { pace_next(cp->icem); } } } /** * Scheduling Checks * * @param icem ICE Media object */ void icem_conncheck_schedule_check(struct icem *icem) { struct ice_candpair *cp; /* Find the highest priority pair in that check list that is in the Waiting state. */ cp = icem_candpair_find_st(&icem->checkl, 0, ICE_CANDPAIR_WAITING); if (cp) { do_check(cp); return; } /* If there is no such pair: */ /* Find the highest priority pair in that check list that is in the Frozen state. */ cp = icem_candpair_find_st(&icem->checkl, 0, ICE_CANDPAIR_FROZEN); if (cp) { /* If there is such a pair: */ /* Unfreeze the pair. Perform a check for that pair, causing its state to transition to In-Progress. */ do_check(cp); return; } /* If there is no such pair: */ /* Terminate the timer for that check list. */ #if 0 icem->state = ICE_CHECKLIST_COMPLETED; #endif } static void pace_timeout(void *arg) { struct icem *icem = arg; pace_next(icem); } /** * Scheduling Checks * * @param icem ICE Media object * * @return 0 if success, otherwise errorcode */ int icem_conncheck_start(struct icem *icem) { int err; if (!icem) return EINVAL; err = icem_checklist_form(icem); if (err) return err; icem->state = ICE_CHECKLIST_RUNNING; icem_printf(icem, "starting connectivity checks" " with %u candidate pairs\n", list_count(&icem->checkl)); /* add some delay, to wait for call to be 'established' */ tmr_start(&icem->tmr_pace, 10, pace_timeout, icem); return 0; } void icem_conncheck_continue(struct icem *icem) { if (!tmr_isrunning(&icem->tmr_pace)) tmr_start(&icem->tmr_pace, 1, pace_timeout, icem); } /** * Stop checklist, cancel all connectivity checks * * @param icem ICE Media object * @param err Error code */ void icem_conncheck_stop(struct icem *icem, int err) { struct le *le; icem->state = err ? ICE_CHECKLIST_FAILED : ICE_CHECKLIST_COMPLETED; tmr_cancel(&icem->tmr_pace); for (le = icem->checkl.head; le; le = le->next) { struct ice_candpair *cp = le->data; if (!icem_candpair_iscompleted(cp)) { icem_candpair_cancel(cp); icem_candpair_failed(cp, EINTR, 0); } } icem_checklist_update(icem); } re-1.1.0/src/ice/ice.c000066400000000000000000000000011373627245400143560ustar00rootroot00000000000000 re-1.1.0/src/ice/ice.h000066400000000000000000000216141373627245400144000ustar00rootroot00000000000000/** * @file ice.h Internal Interface to ICE * * Copyright (C) 2010 Creytiv.com */ #ifndef RELEASE #define ICE_TRACE 1 /**< Trace connectivity checks */ #endif enum ice_checkl_state { ICE_CHECKLIST_NULL = -1, ICE_CHECKLIST_RUNNING, ICE_CHECKLIST_COMPLETED, ICE_CHECKLIST_FAILED }; enum ice_transp { ICE_TRANSP_NONE = -1, ICE_TRANSP_UDP = IPPROTO_UDP }; /** ICE protocol values */ enum { ICE_DEFAULT_Tr = 15, /**< Keepalive interval [s] */ ICE_DEFAULT_Ta_RTP = 20, /**< Pacing interval RTP [ms] */ ICE_DEFAULT_Ta_NON_RTP = 500, /**< Pacing interval [ms] */ ICE_DEFAULT_RTO_RTP = 100, /**< Retransmission TimeOut RTP [ms] */ ICE_DEFAULT_RTO_NONRTP = 500, /**< Retransmission TimeOut [ms] */ ICE_DEFAULT_RC = 7 /**< Retransmission count */ }; /** Defines a media-stream component */ struct icem_comp { struct le le; /**< Linked-list element */ struct icem *icem; /**< Parent ICE media */ struct ice_cand *def_lcand; /**< Default local candidate */ struct ice_cand *def_rcand; /**< Default remote candidate */ struct ice_candpair *cp_sel; /**< Selected candidate-pair */ struct udp_helper *uh; /**< UDP helper */ void *sock; /**< Transport socket */ uint16_t lport; /**< Local port number */ unsigned id; /**< Component ID */ bool concluded; /**< Concluded flag */ struct turnc *turnc; /**< TURN Client */ struct tmr tmr_ka; /**< Keep-alive timer */ }; /** Defines an ICE media-stream */ struct icem { struct ice_conf conf; /**< ICE Configuration */ struct stun *stun; /**< STUN Transport */ struct sa stun_srv; /**< STUN Server IP address and port */ struct list lcandl; /**< List of local candidates */ struct list rcandl; /**< List of remote candidates */ struct list checkl; /**< Check List of cand pairs (sorted) */ struct list validl; /**< Valid List of cand pairs (sorted) */ uint64_t tiebrk; /**< Tie-break value for roleconflict */ bool mismatch; /**< ICE mismatch flag */ enum ice_mode rmode; /**< Remote mode */ enum ice_role lrole; /**< Local role */ struct tmr tmr_pace; /**< Timer for pacing STUN requests */ int proto; /**< Transport protocol */ int layer; /**< Protocol layer */ enum ice_checkl_state state; /**< State of the checklist */ struct list compl; /**< ICE media components */ char *lufrag; /**< Local Username fragment */ char *lpwd; /**< Local Password */ char *rufrag; /**< Remote Username fragment */ char *rpwd; /**< Remote Password */ ice_connchk_h *chkh; /**< Connectivity check handler */ void *arg; /**< Handler argument */ char name[32]; /**< Name of the media stream */ }; /** Defines a candidate */ struct ice_cand { struct le le; /**< List element */ enum ice_cand_type type; /**< Candidate type */ uint32_t prio; /**< Priority of this candidate */ char *foundation; /**< Foundation */ unsigned compid; /**< Component ID (1-256) */ struct sa rel; /**< Related IP address and port number */ struct sa addr; /**< Transport address */ enum ice_transp transp; /**< Transport protocol */ /* extra for local */ struct ice_cand *base; /**< Links to base candidate, if any */ char *ifname; /**< Network interface, for diagnostics */ }; /** Defines a candidate pair */ struct ice_candpair { struct le le; /**< List element */ struct icem *icem; /**< Pointer to parent ICE media */ struct icem_comp *comp; /**< Pointer to media-stream component */ struct ice_cand *lcand; /**< Local candidate */ struct ice_cand *rcand; /**< Remote candidate */ bool def; /**< Default flag */ bool valid; /**< Valid flag */ bool nominated; /**< Nominated flag */ enum ice_candpair_state state;/**< Candidate pair state */ uint64_t pprio; /**< Pair priority */ struct stun_ctrans *ct_conn; /**< STUN Transaction for conncheck */ int err; /**< Saved error code, if failed */ uint16_t scode; /**< Saved STUN code, if failed */ }; /* cand */ int icem_lcand_add_base(struct icem *icem, unsigned compid, uint16_t lprio, const char *ifname, enum ice_transp transp, const struct sa *addr); int icem_rcand_add(struct icem *icem, enum ice_cand_type type, unsigned compid, uint32_t prio, const struct sa *addr, const struct sa *rel_addr, const struct pl *foundation); int icem_rcand_add_prflx(struct ice_cand **rcp, struct icem *icem, unsigned compid, uint32_t prio, const struct sa *addr); struct ice_cand *icem_lcand_find_checklist(const struct icem *icem, unsigned compid); int icem_cands_debug(struct re_printf *pf, const struct list *lst); int icem_cand_print(struct re_printf *pf, const struct ice_cand *cand); /* candpair */ int icem_candpair_alloc(struct ice_candpair **cpp, struct icem *icem, struct ice_cand *lcand, struct ice_cand *rcand); int icem_candpair_clone(struct ice_candpair **cpp, struct ice_candpair *cp0, struct ice_cand *lcand, struct ice_cand *rcand); void icem_candpair_prio_order(struct list *lst); void icem_candpair_cancel(struct ice_candpair *cp); void icem_candpair_make_valid(struct ice_candpair *cp); void icem_candpair_failed(struct ice_candpair *cp, int err, uint16_t scode); void icem_candpair_set_state(struct ice_candpair *cp, enum ice_candpair_state state); void icem_candpairs_flush(struct list *lst, enum ice_cand_type type, unsigned compid); bool icem_candpair_iscompleted(const struct ice_candpair *cp); bool icem_candpair_cmp(const struct ice_candpair *cp1, const struct ice_candpair *cp2); bool icem_candpair_cmp_fnd(const struct ice_candpair *cp1, const struct ice_candpair *cp2); struct ice_candpair *icem_candpair_find(const struct list *lst, const struct ice_cand *lcand, const struct ice_cand *rcand); struct ice_candpair *icem_candpair_find_st(const struct list *lst, unsigned compid, enum ice_candpair_state state); struct ice_candpair *icem_candpair_find_compid(const struct list *lst, unsigned compid); struct ice_candpair *icem_candpair_find_rcand(struct icem *icem, const struct ice_cand *rcand); int icem_candpair_debug(struct re_printf *pf, const struct ice_candpair *cp); int icem_candpairs_debug(struct re_printf *pf, const struct list *list); /* stun server */ int icem_stund_recv(struct icem_comp *comp, const struct sa *src, struct stun_msg *req, size_t presz); /* ICE media */ void icem_printf(struct icem *icem, const char *fmt, ...); /* Checklist */ int icem_checklist_form(struct icem *icem); void icem_checklist_update(struct icem *icem); /* component */ int icem_comp_alloc(struct icem_comp **cp, struct icem *icem, int id, void *sock); int icem_comp_set_default_cand(struct icem_comp *comp); void icem_comp_set_default_rcand(struct icem_comp *comp, struct ice_cand *rcand); void icem_comp_set_selected(struct icem_comp *comp, struct ice_candpair *cp); struct icem_comp *icem_comp_find(const struct icem *icem, unsigned compid); void icem_comp_keepalive(struct icem_comp *comp, bool enable); void icecomp_printf(struct icem_comp *comp, const char *fmt, ...); int icecomp_debug(struct re_printf *pf, const struct icem_comp *comp); /* conncheck */ void icem_conncheck_schedule_check(struct icem *icem); void icem_conncheck_continue(struct icem *icem); int icem_conncheck_send(struct ice_candpair *cp, bool use_cand, bool trigged); /* icestr */ const char *ice_mode2name(enum ice_mode mode); const char *ice_checkl_state2name(enum ice_checkl_state cst); /* util */ typedef void * (list_unique_h)(struct le *le1, struct le *le2); uint64_t ice_calc_pair_prio(uint32_t g, uint32_t d); void ice_switch_local_role(struct icem *icem); uint32_t ice_list_unique(struct list *list, list_unique_h *uh); re-1.1.0/src/ice/icem.c000066400000000000000000000260531373627245400145520ustar00rootroot00000000000000/** * @file icem.c ICE Media stream * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "icem" #define DEBUG_LEVEL 5 #include /* * ICE Implementation as of RFC 5245 */ static const struct ice_conf conf_default = { ICE_DEFAULT_RTO_RTP, ICE_DEFAULT_RC, false }; /** Determining Role */ static void ice_determine_role(struct icem *icem, enum ice_role role) { if (!icem) return; if (icem->rmode == ICE_MODE_LITE) icem->lrole = ICE_ROLE_CONTROLLING; else icem->lrole = role; } static void icem_destructor(void *data) { struct icem *icem = data; tmr_cancel(&icem->tmr_pace); list_flush(&icem->compl); list_flush(&icem->validl); list_flush(&icem->checkl); list_flush(&icem->lcandl); list_flush(&icem->rcandl); mem_deref(icem->lufrag); mem_deref(icem->lpwd); mem_deref(icem->rufrag); mem_deref(icem->rpwd); mem_deref(icem->stun); } /** * Add a new ICE Media object to the ICE Session * * @param icemp Pointer to allocated ICE Media object * @param mode ICE mode * @param role Local ICE role * @param proto Transport protocol * @param layer Protocol stack layer * @param tiebrk Tie-breaker value, must be same for all media streams * @param lufrag Local username fragment * @param lpwd Local password * @param chkh Connectivity check handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int icem_alloc(struct icem **icemp, enum ice_mode mode, enum ice_role role, int proto, int layer, uint64_t tiebrk, const char *lufrag, const char *lpwd, ice_connchk_h *chkh, void *arg) { struct icem *icem; int err = 0; if (!icemp || !tiebrk || !lufrag || !lpwd) return EINVAL; if (mode != ICE_MODE_FULL) return EINVAL; if (str_len(lufrag) < 4 || str_len(lpwd) < 22) { DEBUG_WARNING("alloc: lufrag/lpwd is too short\n"); return EINVAL; } if (proto != IPPROTO_UDP) return EPROTONOSUPPORT; icem = mem_zalloc(sizeof(*icem), icem_destructor); if (!icem) return ENOMEM; icem->conf = conf_default; tmr_init(&icem->tmr_pace); list_init(&icem->lcandl); list_init(&icem->rcandl); list_init(&icem->checkl); list_init(&icem->validl); icem->layer = layer; icem->proto = proto; icem->state = ICE_CHECKLIST_NULL; icem->chkh = chkh; icem->arg = arg; if (err) goto out; icem->tiebrk = tiebrk; err |= str_dup(&icem->lufrag, lufrag); err |= str_dup(&icem->lpwd, lpwd); if (err) goto out; ice_determine_role(icem, role); err = stun_alloc(&icem->stun, NULL, NULL, NULL); if (err) goto out; /* Update STUN Transport */ stun_conf(icem->stun)->rto = icem->conf.rto; stun_conf(icem->stun)->rc = icem->conf.rc; out: if (err) mem_deref(icem); else if (icemp) *icemp = icem; return err; } /** * Get the ICE Configuration * * @param icem ICE Media object * * @return ICE Configuration */ struct ice_conf *icem_conf(struct icem *icem) { return icem ? &icem->conf : NULL; } enum ice_role icem_local_role(const struct icem *icem) { return icem ? icem->lrole : ICE_ROLE_UNKNOWN; } void icem_set_conf(struct icem *icem, const struct ice_conf *conf) { if (!icem || !conf) return; icem->conf = *conf; if (icem->stun) { /* Update STUN Transport */ stun_conf(icem->stun)->rto = icem->conf.rto; stun_conf(icem->stun)->rc = icem->conf.rc; } } /** * Set the local role on the ICE Session * * @param icem ICE Media object * @param role Local ICE role */ void icem_set_role(struct icem *icem, enum ice_role role) { if (!icem) return; ice_determine_role(icem, role); } /** * Set the name of the ICE Media object, used for debugging * * @param icem ICE Media object * @param name Media name */ void icem_set_name(struct icem *icem, const char *name) { if (!icem) return; str_ncpy(icem->name, name, sizeof(icem->name)); } /** * Add a new component to the ICE Media object * * @param icem ICE Media object * @param compid Component ID * @param sock Application protocol socket * * @return 0 if success, otherwise errorcode */ int icem_comp_add(struct icem *icem, unsigned compid, void *sock) { struct icem_comp *comp; int err; if (!icem) return EINVAL; if (icem_comp_find(icem, compid)) return EALREADY; err = icem_comp_alloc(&comp, icem, compid, sock); if (err) return err; list_append(&icem->compl, &comp->le, comp); return 0; } /** * Add a new candidate to the ICE Media object * * @param icem ICE Media object * @param compid Component ID * @param lprio Local priority * @param ifname Name of the network interface * @param addr Local network address * * @return 0 if success, otherwise errorcode */ int icem_cand_add(struct icem *icem, unsigned compid, uint16_t lprio, const char *ifname, const struct sa *addr) { if (!icem_comp_find(icem, compid)) return ENOENT; return icem_lcand_add_base(icem, compid, lprio, ifname, ICE_TRANSP_UDP, addr); } static void *unique_handler(struct le *le1, struct le *le2) { struct ice_cand *c1 = le1->data, *c2 = le2->data; if (c1->base != c2->base || !sa_cmp(&c1->addr, &c2->addr, SA_ALL)) return NULL; /* remove candidate with lower priority */ return c1->prio < c2->prio ? c1 : c2; } /** * Eliminating Redundant Candidates * * @param icem ICE Media object */ void icem_cand_redund_elim(struct icem *icem) { uint32_t n; n = ice_list_unique(&icem->lcandl, unique_handler); if (n > 0) { icem_printf(icem, "redundant candidates eliminated: %u\n", n); } } /** * Get the Default Local Candidate * * @param icem ICE Media object * @param compid Component ID * * @return Default Local Candidate address if set, otherwise NULL */ const struct sa *icem_cand_default(struct icem *icem, unsigned compid) { const struct icem_comp *comp = icem_comp_find(icem, compid); if (!comp || !comp->def_lcand) return NULL; return &comp->def_lcand->addr; } /** * Verifying ICE Support and set default remote candidate * * @param icem ICE Media * @param compid Component ID * @param raddr Address of default remote candidate * * @return True if ICE is supported, otherwise false */ bool icem_verify_support(struct icem *icem, unsigned compid, const struct sa *raddr) { struct ice_cand *rcand; bool match; if (!icem) return false; rcand = icem_cand_find(&icem->rcandl, compid, raddr); match = rcand != NULL; if (!match) icem->mismatch = true; if (rcand) { icem_comp_set_default_rcand(icem_comp_find(icem, compid), rcand); } return match; } /** * Add a TURN Channel for the selected remote address * * @param icem ICE Media object * @param compid Component ID * @param raddr Remote network address * * @return 0 if success, otherwise errorcode */ int icem_add_chan(struct icem *icem, unsigned compid, const struct sa *raddr) { struct icem_comp *comp; if (!icem) return EINVAL; comp = icem_comp_find(icem, compid); if (!comp) return ENOENT; if (comp->turnc) { DEBUG_NOTICE("{%s.%u} Add TURN Channel to peer %J\n", comp->icem->name, comp->id, raddr); return turnc_add_chan(comp->turnc, raddr, NULL, NULL); } return 0; } static void purge_relayed(struct icem *icem, struct icem_comp *comp) { if (comp->turnc) { DEBUG_NOTICE("{%s.%u} purge local RELAY candidates\n", icem->name, comp->id); } /* * Purge all Candidate-Pairs where the Local candidate * is of type "Relay" */ icem_candpairs_flush(&icem->checkl, ICE_CAND_TYPE_RELAY, comp->id); icem_candpairs_flush(&icem->validl, ICE_CAND_TYPE_RELAY, comp->id); comp->turnc = mem_deref(comp->turnc); } /** * Update the ICE Media object * * @param icem ICE Media object */ void icem_update(struct icem *icem) { struct le *le; if (!icem) return; for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; /* remove TURN client if not used by local "Selected" */ if (comp->cp_sel) { if (comp->cp_sel->lcand->type != ICE_CAND_TYPE_RELAY) purge_relayed(icem, comp); } } } /** * Get the ICE Mismatch flag of the ICE Media object * * @param icem ICE Media object * * @return True if ICE mismatch, otherwise false */ bool icem_mismatch(const struct icem *icem) { return icem ? icem->mismatch : true; } /** * Print debug information for the ICE Media * * @param pf Print function for debug output * @param icem ICE Media object * * @return 0 if success, otherwise errorcode */ int icem_debug(struct re_printf *pf, const struct icem *icem) { struct le *le; int err = 0; if (!icem) return 0; err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name); err |= re_hprintf(pf, " local_mode=Full, remote_mode=%s", ice_mode2name(icem->rmode)); err |= re_hprintf(pf, ", local_role=%s\n", ice_role2name(icem->lrole)); err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n", icem->lufrag, icem->lpwd); err |= re_hprintf(pf, " Components: (%u)\n", list_count(&icem->compl)); for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; err |= re_hprintf(pf, " %H\n", icecomp_debug, comp); } err |= re_hprintf(pf, " Local Candidates: %H", icem_cands_debug, &icem->lcandl); err |= re_hprintf(pf, " Remote Candidates: %H", icem_cands_debug, &icem->rcandl); err |= re_hprintf(pf, " Check list: [state=%s]%H", ice_checkl_state2name(icem->state), icem_candpairs_debug, &icem->checkl); err |= re_hprintf(pf, " Valid list: %H", icem_candpairs_debug, &icem->validl); err |= stun_debug(pf, icem->stun); return err; } /** * Get the list of Local Candidates (struct cand) * * @param icem ICE Media object * * @return List of Local Candidates */ struct list *icem_lcandl(const struct icem *icem) { return icem ? (struct list *)&icem->lcandl : NULL; } /** * Get the list of Remote Candidates (struct cand) * * @param icem ICE Media object * * @return List of Remote Candidates */ struct list *icem_rcandl(const struct icem *icem) { return icem ? (struct list *)&icem->rcandl : NULL; } /** * Get the checklist of Candidate Pairs * * @param icem ICE Media object * * @return Checklist (struct ice_candpair) */ struct list *icem_checkl(const struct icem *icem) { return icem ? (struct list *)&icem->checkl : NULL; } /** * Get the list of valid Candidate Pairs * * @param icem ICE Media object * * @return Validlist (struct ice_candpair) */ struct list *icem_validl(const struct icem *icem) { return icem ? (struct list *)&icem->validl : NULL; } int icem_comps_set_default_cand(struct icem *icem) { struct le *le; int err = 0; if (!icem) return EINVAL; for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; err |= icem_comp_set_default_cand(comp); } return err; } struct stun *icem_stun(struct icem *icem) { return icem ? icem->stun : NULL; } void icem_printf(struct icem *icem, const char *fmt, ...) { va_list ap; if (!icem || !icem->conf.debug) return; va_start(ap, fmt); (void)re_printf("{%11s. } %v", icem->name, fmt, &ap); va_end(ap); } re-1.1.0/src/ice/icesdp.c000066400000000000000000000214511373627245400151010ustar00rootroot00000000000000/** * @file icesdp.c SDP Attributes for ICE * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "icesdp" #define DEBUG_LEVEL 5 #include const char ice_attr_cand[] = "candidate"; const char ice_attr_remote_cand[] = "remote-candidates"; const char ice_attr_lite[] = "ice-lite"; const char ice_attr_ufrag[] = "ice-ufrag"; const char ice_attr_pwd[] = "ice-pwd"; const char ice_attr_mismatch[] = "ice-mismatch"; static const char rel_addr_str[] = "raddr"; static const char rel_port_str[] = "rport"; /* Encode SDP Attributes */ static const char *transp_name(enum ice_transp transp) { switch (transp) { case ICE_TRANSP_UDP: return "UDP"; default: return "???"; } } static enum ice_transp transp_resolve(const struct pl *transp) { if (!pl_strcasecmp(transp, "UDP")) return ICE_TRANSP_UDP; return ICE_TRANSP_NONE; } /** * Encode SDP candidate attribute * * @param pf Print function * @param cand Candidate to encode * * @return 0 if success, otherwise errorcode */ int ice_cand_encode(struct re_printf *pf, const struct ice_cand *cand) { int err; err = re_hprintf(pf, "%s %u %s %u %j %u typ %s", cand->foundation, cand->compid, transp_name(cand->transp), cand->prio, &cand->addr, sa_port(&cand->addr), ice_cand_type2name(cand->type)); if (sa_isset(&cand->rel, SA_ADDR)) err |= re_hprintf(pf, " raddr %j", &cand->rel); if (sa_isset(&cand->rel, SA_PORT)) err |= re_hprintf(pf, " rport %u", sa_port(&cand->rel)); return err; } /** * Check if remote candidates are available * * @param icem ICE Media object * * @return True if available, otherwise false */ bool ice_remotecands_avail(const struct icem *icem) { if (!icem) return false; return icem->lrole == ICE_ROLE_CONTROLLING && icem->state == ICE_CHECKLIST_COMPLETED; } /** * Encode the SDP "remote-candidates" Attribute * * @param pf Print function * @param icem ICE Media object * * @return 0 if success, otherwise errorcode */ int ice_remotecands_encode(struct re_printf *pf, const struct icem *icem) { struct le *le; int err = 0; if (!icem) return EINVAL; for (le = icem->rcandl.head; le && !err; le = le->next) { const struct ice_cand *rcand = le->data; err = re_hprintf(pf, "%s%d %j %u", icem->rcandl.head==le ? "" : " ", rcand->compid, &rcand->addr, sa_port(&rcand->addr)); } return err; } /* Decode SDP Attributes */ static int ufrag_decode(struct icem *icem, const char *value) { char *ufrag = NULL; int err; err = str_dup(&ufrag, value); if (err) return err; mem_deref(icem->rufrag); icem->rufrag = mem_ref(ufrag); mem_deref(ufrag); return 0; } static int pwd_decode(struct icem *icem, const char *value) { char *pwd = NULL; int err; err = str_dup(&pwd, value); if (err) return err; mem_deref(icem->rpwd); icem->rpwd = mem_ref(pwd); mem_deref(pwd); return 0; } static int media_ufrag_decode(struct icem *icem, const char *value) { icem->rufrag = mem_deref(icem->rufrag); return str_dup(&icem->rufrag, value); } static int media_pwd_decode(struct icem *icem, const char *value) { icem->rpwd = mem_deref(icem->rpwd); return str_dup(&icem->rpwd, value); } static int cand_decode(struct icem *icem, const char *val) { struct pl foundation, compid, transp, prio, addr, port, cand_type; struct pl extra = pl_null; struct sa caddr, rel_addr; char type[8]; uint8_t cid; int err; sa_init(&rel_addr, AF_INET); err = re_regex(val, strlen(val), "[^ ]+ [0-9]+ [^ ]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+[^]*", &foundation, &compid, &transp, &prio, &addr, &port, &cand_type, &extra); if (err) return err; if (ICE_TRANSP_NONE == transp_resolve(&transp)) { DEBUG_NOTICE("<%s> ignoring candidate with" " unknown transport=%r (%r:%r)\n", icem->name, &transp, &cand_type, &addr); return 0; } if (pl_isset(&extra)) { struct pl name, value; /* Loop through " SP attr SP value" pairs */ while (!re_regex(extra.p, extra.l, " [^ ]+ [^ ]+", &name, &value)) { pl_advance(&extra, value.p + value.l - extra.p); if (0 == pl_strcasecmp(&name, rel_addr_str)) { err = sa_set(&rel_addr, &value, sa_port(&rel_addr)); if (err) break; } else if (0 == pl_strcasecmp(&name, rel_port_str)) { sa_set_port(&rel_addr, pl_u32(&value)); } } } err = sa_set(&caddr, &addr, pl_u32(&port)); if (err) return err; cid = pl_u32(&compid); /* add only if not exist */ if (icem_cand_find(&icem->rcandl, cid, &caddr)) return 0; (void)pl_strcpy(&cand_type, type, sizeof(type)); return icem_rcand_add(icem, ice_cand_name2type(type), cid, pl_u32(&prio), &caddr, &rel_addr, &foundation); } /** * Decode SDP session attributes * * @param icem ICE Media object * @param name Name of the SDP attribute * @param value Value of the SDP attribute (optional) * * @return 0 if success, otherwise errorcode */ int ice_sdp_decode(struct icem *icem, const char *name, const char *value) { if (!icem) return EINVAL; if (0 == str_casecmp(name, ice_attr_lite)) { icem->rmode = ICE_MODE_LITE; icem->lrole = ICE_ROLE_CONTROLLING; } else if (0 == str_casecmp(name, ice_attr_ufrag)) return ufrag_decode(icem, value); else if (0 == str_casecmp(name, ice_attr_pwd)) return pwd_decode(icem, value); return 0; } /** * Decode SDP media attributes * * @param icem ICE Media object * @param name Name of the SDP attribute * @param value Value of the SDP attribute (optional) * * @return 0 if success, otherwise errorcode */ int icem_sdp_decode(struct icem *icem, const char *name, const char *value) { if (!icem) return EINVAL; if (0 == str_casecmp(name, ice_attr_cand)) return cand_decode(icem, value); else if (0 == str_casecmp(name, ice_attr_mismatch)) icem->mismatch = true; else if (0 == str_casecmp(name, ice_attr_ufrag)) return media_ufrag_decode(icem, value); else if (0 == str_casecmp(name, ice_attr_pwd)) return media_pwd_decode(icem, value); return 0; } static const char *ice_tcptype_name(enum ice_tcptype tcptype) { switch (tcptype) { case ICE_TCP_ACTIVE: return "active"; case ICE_TCP_PASSIVE: return "passive"; case ICE_TCP_SO: return "so"; default: return "???"; } } static enum ice_tcptype ice_tcptype_resolve(const struct pl *pl) { if (0 == pl_strcasecmp(pl, "active")) return ICE_TCP_ACTIVE; if (0 == pl_strcasecmp(pl, "passive")) return ICE_TCP_PASSIVE; if (0 == pl_strcasecmp(pl, "so")) return ICE_TCP_SO; return (enum ice_tcptype)-1; } int ice_cand_attr_encode(struct re_printf *pf, const struct ice_cand_attr *cand) { int err = 0; if (!cand) return 0; err |= re_hprintf(pf, "%s %u %s %u %j %u typ %s", cand->foundation, cand->compid, net_proto2name(cand->proto), cand->prio, &cand->addr, sa_port(&cand->addr), ice_cand_type2name(cand->type)); if (sa_isset(&cand->rel_addr, SA_ADDR)) err |= re_hprintf(pf, " raddr %j", &cand->rel_addr); if (sa_isset(&cand->rel_addr, SA_PORT)) err |= re_hprintf(pf, " rport %u", sa_port(&cand->rel_addr)); if (cand->proto == IPPROTO_TCP) { err |= re_hprintf(pf, " tcptype %s", ice_tcptype_name(cand->tcptype)); } return err; } int ice_cand_attr_decode(struct ice_cand_attr *cand, const char *val) { struct pl pl_fnd, pl_compid, pl_transp, pl_prio, pl_addr, pl_port; struct pl pl_type, pl_raddr, pl_rport, pl_opt = PL_INIT; size_t len; char type[8]; int err; if (!cand || !val) return EINVAL; memset(cand, 0, sizeof(*cand)); len = str_len(val); err = re_regex(val, len, "[^ ]+ [0-9]+ [a-z]+ [0-9]+ [^ ]+ [0-9]+ typ [a-z]+" "[^]*", &pl_fnd, &pl_compid, &pl_transp, &pl_prio, &pl_addr, &pl_port, &pl_type, &pl_opt); if (err) return err; (void)pl_strcpy(&pl_fnd, cand->foundation, sizeof(cand->foundation)); if (0 == pl_strcasecmp(&pl_transp, "UDP")) cand->proto = IPPROTO_UDP; else if (0 == pl_strcasecmp(&pl_transp, "TCP")) cand->proto = IPPROTO_TCP; else cand->proto = 0; err = sa_set(&cand->addr, &pl_addr, pl_u32(&pl_port)); if (err) return err; cand->compid = pl_u32(&pl_compid); cand->prio = pl_u32(&pl_prio); (void)pl_strcpy(&pl_type, type, sizeof(type)); cand->type = ice_cand_name2type(type); /* optional */ if (0 == re_regex(pl_opt.p, pl_opt.l, "raddr [^ ]+ rport [0-9]+", &pl_raddr, &pl_rport)) { err = sa_set(&cand->rel_addr, &pl_raddr, pl_u32(&pl_rport)); if (err) return err; } if (cand->proto == IPPROTO_TCP) { struct pl tcptype; err = re_regex(pl_opt.p, pl_opt.l, "tcptype [^ ]+", &tcptype); if (err) return err; cand->tcptype = ice_tcptype_resolve(&tcptype); } return 0; } re-1.1.0/src/ice/icestr.c000066400000000000000000000040101373627245400151130ustar00rootroot00000000000000/** * @file icestr.c ICE Strings * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "ice.h" const char *ice_cand_type2name(enum ice_cand_type type) { switch (type) { case ICE_CAND_TYPE_HOST: return "host"; case ICE_CAND_TYPE_SRFLX: return "srflx"; case ICE_CAND_TYPE_PRFLX: return "prflx"; case ICE_CAND_TYPE_RELAY: return "relay"; default: return "???"; } } enum ice_cand_type ice_cand_name2type(const char *name) { if (0 == str_casecmp(name, "host")) return ICE_CAND_TYPE_HOST; if (0 == str_casecmp(name, "srflx")) return ICE_CAND_TYPE_SRFLX; if (0 == str_casecmp(name, "prflx")) return ICE_CAND_TYPE_PRFLX; if (0 == str_casecmp(name, "relay")) return ICE_CAND_TYPE_RELAY; return (enum ice_cand_type)-1; } const char *ice_mode2name(enum ice_mode mode) { switch (mode) { case ICE_MODE_FULL: return "Full"; case ICE_MODE_LITE: return "Lite"; default: return "???"; } } const char *ice_role2name(enum ice_role role) { switch (role) { case ICE_ROLE_UNKNOWN: return "Unknown"; case ICE_ROLE_CONTROLLING: return "Controlling"; case ICE_ROLE_CONTROLLED: return "Controlled"; default: return "???"; } } const char *ice_candpair_state2name(enum ice_candpair_state st) { switch (st) { case ICE_CANDPAIR_FROZEN: return "Frozen"; case ICE_CANDPAIR_WAITING: return "Waiting"; case ICE_CANDPAIR_INPROGRESS: return "InProgress"; case ICE_CANDPAIR_SUCCEEDED: return "Succeeded"; case ICE_CANDPAIR_FAILED: return "Failed"; default: return "???"; } } const char *ice_checkl_state2name(enum ice_checkl_state cst) { switch (cst) { case ICE_CHECKLIST_NULL: return "(NULL)"; case ICE_CHECKLIST_RUNNING: return "Running"; case ICE_CHECKLIST_COMPLETED: return "Completed"; case ICE_CHECKLIST_FAILED: return "Failed"; default: return "???"; } } re-1.1.0/src/ice/mod.mk000066400000000000000000000004001373627245400145650ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += ice/cand.c SRCS += ice/candpair.c SRCS += ice/chklist.c SRCS += ice/comp.c SRCS += ice/connchk.c SRCS += ice/icem.c SRCS += ice/icesdp.c SRCS += ice/icestr.c SRCS += ice/stunsrv.c SRCS += ice/util.c re-1.1.0/src/ice/stunsrv.c000066400000000000000000000137451373627245400153650ustar00rootroot00000000000000/** * @file stunsrv.c Basic STUN Server for Connectivity checks * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "stunsrv" #define DEBUG_LEVEL 5 #include static const char *sw = "ice stunsrv v" VERSION " (" ARCH "/" OS ")"; static void triggered_check(struct icem *icem, struct ice_cand *lcand, struct ice_cand *rcand) { struct ice_candpair *cp = NULL; int err; if (lcand && rcand) cp = icem_candpair_find(&icem->checkl, lcand, rcand); if (cp) { switch (cp->state) { #if 0 /* TODO: I am not sure why we should cancel the * pending Connectivity check here. this * can lead to a deadlock situation where * both agents are stuck on sending * triggered checks on the same candidate pair */ case ICE_CANDPAIR_INPROGRESS: icem_candpair_cancel(cp); /*@fallthrough@*/ #endif case ICE_CANDPAIR_FAILED: icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING); /*@fallthrough@*/ case ICE_CANDPAIR_FROZEN: case ICE_CANDPAIR_WAITING: err = icem_conncheck_send(cp, false, true); if (err) { DEBUG_WARNING("triggered check failed\n"); } break; case ICE_CANDPAIR_SUCCEEDED: default: break; } } else { #if 0 err = icem_candpair_alloc(&cp, icem, lcand, rcand); if (err) { DEBUG_WARNING("failed to allocate candpair:" " lcand=%p rcand=%p (%m)\n", lcand, rcand, err); return; } icem_candpair_prio_order(&icem->checkl); icem_candpair_set_state(cp, ICE_CANDPAIR_WAITING); (void)icem_conncheck_send(cp, false, true); #endif } } /* * 7.2.1. Additional Procedures for Full Implementations */ static int handle_stun_full(struct icem *icem, struct icem_comp *comp, const struct sa *src, uint32_t prio, bool use_cand, bool tunnel) { struct ice_cand *lcand = NULL, *rcand; struct ice_candpair *cp = NULL; int err; rcand = icem_cand_find(&icem->rcandl, comp->id, src); if (!rcand) { err = icem_rcand_add_prflx(&rcand, icem, comp->id, prio, src); if (err) return err; } cp = icem_candpair_find_rcand(icem, rcand); if (cp) lcand = cp->lcand; else lcand = icem_lcand_find_checklist(icem, comp->id); if (!lcand) { DEBUG_WARNING("{%s.%u} local candidate not found" " (checklist=%u) (src=%J)\n", icem->name, comp->id, list_count(&icem->checkl), src); return 0; } triggered_check(icem, lcand, rcand); if (!cp) { cp = icem_candpair_find_rcand(icem, rcand); if (!cp) { DEBUG_WARNING("{%s.%u} candidate pair not found:" " source=%J\n", icem->name, comp->id, src); return 0; } } #if ICE_TRACE icecomp_printf(comp, "Rx Binding Request from %J via %s" " (candpair=%s) %s\n", src, tunnel ? "Tunnel" : "Socket", cp ? ice_candpair_state2name(cp->state) : "n/a", use_cand ? "[USE]" : ""); #else (void)tunnel; #endif /* 7.2.1.5. Updating the Nominated Flag */ if (use_cand) { if (icem->lrole == ICE_ROLE_CONTROLLED && cp->state == ICE_CANDPAIR_SUCCEEDED) { if (!cp->nominated) { icecomp_printf(comp, "setting NOMINATED" " flag on candpair [%H]\n", icem_candpair_debug, cp); } cp->nominated = true; } } return 0; } static int stunsrv_ereply(struct icem_comp *comp, const struct sa *src, size_t presz, const struct stun_msg *req, uint16_t scode, const char *reason) { struct icem *icem = comp->icem; return stun_ereply(icem->proto, comp->sock, src, presz, req, scode, reason, (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 1, STUN_ATTR_SOFTWARE, sw); } int icem_stund_recv(struct icem_comp *comp, const struct sa *src, struct stun_msg *req, size_t presz) { struct icem *icem = comp->icem; struct stun_attr *attr; struct pl lu, ru; enum ice_role rrole = ICE_ROLE_UNKNOWN; uint64_t tiebrk = 0; uint32_t prio_prflx; bool use_cand = false; int err; /* RFC 5389: Fingerprint errors are silently discarded */ err = stun_msg_chk_fingerprint(req); if (err) return err; err = stun_msg_chk_mi(req, (uint8_t *)icem->lpwd, strlen(icem->lpwd)); if (err) { if (err == EBADMSG) goto unauth; else goto badmsg; } attr = stun_msg_attr(req, STUN_ATTR_USERNAME); if (!attr) goto badmsg; err = re_regex(attr->v.username, strlen(attr->v.username), "[^:]+:[^]+", &lu, &ru); if (err) { DEBUG_WARNING("could not parse USERNAME attribute (%s)\n", attr->v.username); goto unauth; } if (pl_strcmp(&lu, icem->lufrag)) goto unauth; if (str_isset(icem->rufrag) && pl_strcmp(&ru, icem->rufrag)) goto unauth; attr = stun_msg_attr(req, STUN_ATTR_CONTROLLED); if (attr) { rrole = ICE_ROLE_CONTROLLED; tiebrk = attr->v.uint64; } attr = stun_msg_attr(req, STUN_ATTR_CONTROLLING); if (attr) { rrole = ICE_ROLE_CONTROLLING; tiebrk = attr->v.uint64; } if (rrole == icem->lrole) { if (icem->tiebrk >= tiebrk) ice_switch_local_role(icem); else goto conflict; } attr = stun_msg_attr(req, STUN_ATTR_PRIORITY); if (attr) prio_prflx = attr->v.uint32; else goto badmsg; attr = stun_msg_attr(req, STUN_ATTR_USE_CAND); if (attr) { use_cand = true; } if (rrole == ICE_ROLE_CONTROLLED && use_cand) { DEBUG_NOTICE("remote peer is Controlled and" " should not send USE-CANDIDATE\n"); } err = handle_stun_full(icem, comp, src, prio_prflx, use_cand, presz > 0); if (err) goto badmsg; return stun_reply(icem->proto, comp->sock, src, presz, req, (uint8_t *)icem->lpwd, strlen(icem->lpwd), true, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, sw); badmsg: return stunsrv_ereply(comp, src, presz, req, 400, "Bad Request"); unauth: return stunsrv_ereply(comp, src, presz, req, 401, "Unauthorized"); conflict: return stunsrv_ereply(comp, src, presz, req, 487, "Role Conflict"); } re-1.1.0/src/ice/util.c000066400000000000000000000047721373627245400146160ustar00rootroot00000000000000/** * @file ice/util.c ICE Utilities * * Copyright (C) 2010 Creytiv.com */ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include #include "ice.h" #define DEBUG_MODULE "iceutil" #define DEBUG_LEVEL 5 #include enum { CAND_PRIO_RELAY = 0, CAND_PRIO_SRFLX = 100, CAND_PRIO_PRFLX = 110, CAND_PRIO_HOST = 126 }; static uint32_t type_prio(enum ice_cand_type type) { switch (type) { case ICE_CAND_TYPE_HOST: return CAND_PRIO_HOST; case ICE_CAND_TYPE_SRFLX: return CAND_PRIO_SRFLX; case ICE_CAND_TYPE_PRFLX: return CAND_PRIO_PRFLX; case ICE_CAND_TYPE_RELAY: return CAND_PRIO_RELAY; default: return 0; } } uint32_t ice_cand_calc_prio(enum ice_cand_type type, uint16_t local, unsigned compid) { return type_prio(type)<<24 | (uint32_t)local<<8 | (256 - compid); } /* * g = controlling agent * d = controlled agent pair priority = 2^32*MIN(G,D) + 2*MAX(G,D) + (G>D?1:0) */ uint64_t ice_calc_pair_prio(uint32_t g, uint32_t d) { const uint64_t m = min(g, d); const uint64_t x = max(g, d); return (m<<32) + 2*x + (g>d?1:0); } void ice_switch_local_role(struct icem *icem) { enum ice_role new_role; if (ICE_ROLE_CONTROLLING == icem->lrole) new_role = ICE_ROLE_CONTROLLED; else new_role = ICE_ROLE_CONTROLLING; DEBUG_NOTICE("Switch local role from %s to %s\n", ice_role2name(icem->lrole), ice_role2name(new_role)); icem->lrole = new_role; #if 0 /* recompute pair priorities for all media streams */ for (le = icem->le.list->head; le; le = le->next) { icem = le->data; icem_candpair_prio_order(&icem->checkl); } #endif } /** * Remove duplicate elements from list, preserving order * * @param list Linked list * @param uh Unique handler (return object to remove) * * @return Number of elements removed * * @note: O (n ^ 2) */ uint32_t ice_list_unique(struct list *list, list_unique_h *uh) { struct le *le1 = list_head(list); uint32_t n = 0; while (le1 && le1 != list->tail) { struct le *le2 = le1->next; void *data = NULL; while (le2) { data = uh(le1, le2); le2 = le2->next; if (!data) continue; if (le1->data == data) break; else { data = mem_deref(data); ++n; } } le1 = le1->next; if (data) { mem_deref(data); ++n; } } return n; } re-1.1.0/src/jbuf/000077500000000000000000000000001373627245400136515ustar00rootroot00000000000000re-1.1.0/src/jbuf/jbuf.c000066400000000000000000000221131373627245400147420ustar00rootroot00000000000000/** * @file jbuf.c Jitter Buffer implementation * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #define DEBUG_MODULE "jbuf" #define DEBUG_LEVEL 5 #include #ifndef RELEASE #define JBUF_STAT 1 /**< Jitter buffer statistics */ #endif #if JBUF_STAT #define STAT_ADD(var, value) (jb->stat.var) += (value) /**< Stats add */ #define STAT_INC(var) ++(jb->stat.var) /**< Stats inc */ #else #define STAT_ADD(var, value) #define STAT_INC(var) #endif /** Defines a packet frame */ struct frame { struct le le; /**< Linked list element */ struct rtp_header hdr; /**< RTP Header */ void *mem; /**< Reference counted pointer */ }; /** * Defines a jitter buffer * * The jitter buffer is for incoming RTP packets, which are sorted by * sequence number. */ struct jbuf { struct list pooll; /**< List of free frames in pool */ struct list framel; /**< List of buffered frames */ uint32_t n; /**< [# frames] Current # of frames in buffer */ uint32_t min; /**< [# frames] Minimum # of frames to buffer */ uint32_t max; /**< [# frames] Maximum # of frames to buffer */ uint16_t seq_put; /**< Sequence number for last jbuf_put() */ bool running; /**< Jitter buffer is running */ #if JBUF_STAT uint16_t seq_get; /**< Timestamp of last played frame */ struct jbuf_stat stat; /**< Jitter buffer Statistics */ #endif }; /** Is x less than y? */ static inline bool seq_less(uint16_t x, uint16_t y) { return ((int16_t)(x - y)) < 0; } /** * Get a frame from the pool */ static void frame_alloc(struct jbuf *jb, struct frame **f) { struct le *le; le = jb->pooll.head; if (le) { list_unlink(le); ++jb->n; } else { struct frame *f0; /* Steal an old frame */ le = jb->framel.head; f0 = le->data; STAT_INC(n_overflow); DEBUG_INFO("drop 1 old frame seq=%u (total dropped %u)\n", f0->hdr.seq, jb->stat.n_overflow); f0->mem = mem_deref(f0->mem); list_unlink(le); } *f = le->data; } /** * Release a frame, put it back in the pool */ static void frame_deref(struct jbuf *jb, struct frame *f) { f->mem = mem_deref(f->mem); list_unlink(&f->le); list_append(&jb->pooll, &f->le, f); --jb->n; } static void jbuf_destructor(void *data) { struct jbuf *jb = data; jbuf_flush(jb); /* Free all frames in the pool list */ list_flush(&jb->pooll); } /** * Allocate a new jitter buffer * * @param jbp Pointer to returned jitter buffer * @param min Minimum delay in [frames] * @param max Maximum delay in [frames] * * @return 0 if success, otherwise errorcode */ int jbuf_alloc(struct jbuf **jbp, uint32_t min, uint32_t max) { struct jbuf *jb; uint32_t i; int err = 0; if (!jbp || ( min > max)) return EINVAL; DEBUG_INFO("alloc: delay=%u-%u frames\n", min, max); /* self-test: x < y (also handle wrap around) */ if (!seq_less(10, 20) || seq_less(20, 10) || !seq_less(65535, 0)) { DEBUG_WARNING("seq_less() is broken\n"); return ENOSYS; } jb = mem_zalloc(sizeof(*jb), jbuf_destructor); if (!jb) return ENOMEM; list_init(&jb->pooll); list_init(&jb->framel); jb->min = min; jb->max = max; /* Allocate all frames now */ for (i=0; imax; i++) { struct frame *f = mem_zalloc(sizeof(*f), NULL); if (!f) { err = ENOMEM; break; } list_append(&jb->pooll, &f->le, f); DEBUG_INFO("alloc: adding to pool list %u\n", i); } if (err) mem_deref(jb); else *jbp = jb; return err; } /** * Put one frame into the jitter buffer * * @param jb Jitter buffer * @param hdr RTP Header * @param mem Memory pointer - will be referenced * * @return 0 if success, otherwise errorcode */ int jbuf_put(struct jbuf *jb, const struct rtp_header *hdr, void *mem) { struct frame *f; struct le *le, *tail; uint16_t seq; int err = 0; if (!jb || !hdr) return EINVAL; seq = hdr->seq; STAT_INC(n_put); if (jb->running) { /* Packet arrived too late to be put into buffer */ if (seq_less((seq + jb->n), jb->seq_put)) { STAT_INC(n_late); DEBUG_INFO("packet too late: seq=%u (seq_put=%u)\n", seq, jb->seq_put); return ETIMEDOUT; } } frame_alloc(jb, &f); tail = jb->framel.tail; /* If buffer is empty -> append to tail Frame is later than tail -> append to tail */ if (!tail || seq_less(((struct frame *)tail->data)->hdr.seq, seq)) { list_append(&jb->framel, &f->le, f); goto out; } /* Out-of-sequence, find right position */ for (le = tail; le; le = le->prev) { const uint16_t seq_le = ((struct frame *)le->data)->hdr.seq; if (seq_less(seq_le, seq)) { /* most likely */ DEBUG_INFO("put: out-of-sequence" " - inserting after seq=%u (seq=%u)\n", seq_le, seq); list_insert_after(&jb->framel, le, &f->le, f); break; } else if (seq == seq_le) { /* less likely */ /* Detect duplicates */ DEBUG_INFO("duplicate: seq=%u\n", seq); STAT_INC(n_dups); list_insert_after(&jb->framel, le, &f->le, f); frame_deref(jb, f); return EALREADY; } /* sequence number less than current seq, continue */ } /* no earlier timestamps found, put in head */ if (!le) { DEBUG_INFO("put: out-of-sequence" " - put in head (seq=%u)\n", seq); list_prepend(&jb->framel, &f->le, f); } STAT_INC(n_oos); out: /* Update last timestamp */ jb->running = true; jb->seq_put = seq; /* Success */ f->hdr = *hdr; f->mem = mem_ref(mem); return err; } /** * Get one frame from the jitter buffer * * @param jb Jitter buffer * @param hdr Returned RTP Header * @param mem Pointer to memory object storage - referenced on success * * @return 0 if success, otherwise errorcode */ int jbuf_get(struct jbuf *jb, struct rtp_header *hdr, void **mem) { struct frame *f; if (!jb || !hdr || !mem) return EINVAL; STAT_INC(n_get); if (jb->n <= jb->min || !jb->framel.head) { DEBUG_INFO("not enough buffer frames - wait.. (n=%u min=%u)\n", jb->n, jb->min); STAT_INC(n_underflow); return ENOENT; } /* When we get one frame F[i], check that the next frame F[i+1] is present and have a seq no. of seq[i] + 1 ! if not, we should consider that packet lost */ f = jb->framel.head->data; #if JBUF_STAT /* Check timestamp of previously played frame */ if (jb->seq_get) { const int16_t seq_diff = f->hdr.seq - jb->seq_get; if (seq_less(f->hdr.seq, jb->seq_get)) { DEBUG_WARNING("get: seq=%u too late\n", f->hdr.seq); } else if (seq_diff > 1) { STAT_ADD(n_lost, 1); DEBUG_INFO("get: n_lost: diff=%d,seq=%u,seq_get=%u\n", seq_diff, f->hdr.seq, jb->seq_get); } } /* Update sequence number for 'get' */ jb->seq_get = f->hdr.seq; #endif *hdr = f->hdr; *mem = mem_ref(f->mem); frame_deref(jb, f); return 0; } /** * Flush all frames in the jitter buffer * * @param jb Jitter buffer */ void jbuf_flush(struct jbuf *jb) { struct le *le; #if JBUF_STAT uint32_t n_flush; #endif if (!jb) return; if (jb->framel.head) { DEBUG_INFO("flush: %u frames\n", jb->n); } /* put all buffered frames back in free list */ for (le = jb->framel.head; le; le = jb->framel.head) { DEBUG_INFO(" flush frame: seq=%u\n", ((struct frame *)(le->data))->hdr.seq); frame_deref(jb, le->data); } jb->n = 0; jb->running = false; #if JBUF_STAT n_flush = STAT_INC(n_flush); jb->seq_get = 0; memset(&jb->stat, 0, sizeof(jb->stat)); jb->stat.n_flush = n_flush; #endif } /** * Get jitter buffer statistics * * @param jb Jitter buffer * @param jstat Pointer to statistics storage * * @return 0 if success, otherwise errorcode */ int jbuf_stats(const struct jbuf *jb, struct jbuf_stat *jstat) { if (!jb || !jstat) return EINVAL; #if JBUF_STAT *jstat = jb->stat; return 0; #else return ENOSYS; #endif } /** * Debug the jitter buffer * * @param pf Print handler * @param jb Jitter buffer * * @return 0 if success, otherwise errorcode */ int jbuf_debug(struct re_printf *pf, const struct jbuf *jb) { int err = 0; if (!jb) return 0; err |= re_hprintf(pf, "--- jitter buffer debug---\n"); err |= re_hprintf(pf, " running=%d", jb->running); err |= re_hprintf(pf, " min=%u cur=%u max=%u [frames]\n", jb->min, jb->n, jb->max); err |= re_hprintf(pf, " seq_put=%u\n", jb->seq_put); #if JBUF_STAT err |= re_hprintf(pf, " Stat: put=%u", jb->stat.n_put); err |= re_hprintf(pf, " get=%u", jb->stat.n_get); err |= re_hprintf(pf, " oos=%u", jb->stat.n_oos); err |= re_hprintf(pf, " dup=%u", jb->stat.n_dups); err |= re_hprintf(pf, " late=%u", jb->stat.n_late); err |= re_hprintf(pf, " or=%u", jb->stat.n_overflow); err |= re_hprintf(pf, " ur=%u", jb->stat.n_underflow); err |= re_hprintf(pf, " flush=%u", jb->stat.n_flush); err |= re_hprintf(pf, " put/get_ratio=%u%%", jb->stat.n_get ? 100*jb->stat.n_put/jb->stat.n_get : 0); err |= re_hprintf(pf, " lost=%u (%u.%02u%%)\n", jb->stat.n_lost, jb->stat.n_put ? 100*jb->stat.n_lost/jb->stat.n_put : 0, jb->stat.n_put ? 10000*jb->stat.n_lost/jb->stat.n_put%100 : 0); #endif return err; } re-1.1.0/src/jbuf/mod.mk000066400000000000000000000001051373627245400147550ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += jbuf/jbuf.c re-1.1.0/src/json/000077500000000000000000000000001373627245400136745ustar00rootroot00000000000000re-1.1.0/src/json/decode.c000066400000000000000000000156141373627245400152720ustar00rootroot00000000000000/** * @file json/decode.c JSON decoder * * Copyright (C) 2010 - 2015 Creytiv.com */ #include #include #include #include #include #include #include static inline long double mypower10(uint64_t e) { long double p = 10, n = 1; while (e > 0) { if (e & 1) n *= p; p *= p; e >>= 1; } return n; } static bool is_string(struct pl *c, const struct pl *pl) { if (pl->l < 2) return false; if (pl->p[0] != '"'|| pl->p[pl->l-1] != '"') return false; c->p = pl->p + 1; c->l = pl->l - 2; return true; } static bool is_number(long double *d, bool *isfloat, const struct pl *pl) { bool neg = false, pos = false, frac = false, exp = false; long double v = 0, mul = 1; const char *p; int64_t e = 0; if (!pl->l) return false; p = &pl->p[pl->l]; while (p > pl->p) { const char ch = *--p; if (ch == 'e' || ch == 'E') { if (exp || frac) return false; exp = true; e = neg ? -v : v; v = 0; mul = 1; neg = false; pos = false; } else if (pos || neg) { return false; } else if (ch == '.') { if (frac) return false; frac = true; v /= mul; mul = 1; } else if ('0' <= ch && ch <= '9') { v += mul * (ch - '0'); mul *= 10; } else if (ch == '-') { neg = true; } else if (ch == '+') { pos = true; } else { return false; } } *isfloat = (frac || exp); if (exp) { if (e < 0) v /= mypower10(-e); else v *= mypower10(e); } if (neg) v = -v; *d = v; return true; } static int decode_name(char **str, const struct pl *pl) { struct pl pls; if (!pl->p) return EBADMSG; if (!is_string(&pls, pl)) return EBADMSG; return re_sdprintf(str, "%H", utf8_decode, &pls); } static int decode_value(struct json_value *val, const struct pl *pl) { long double dbl; struct pl pls; bool isfloat; int err = 0; if (!pl->p) return EBADMSG; if (is_string(&pls, pl)) { err = re_sdprintf(&val->v.str, "%H", utf8_decode, &pls); val->type = JSON_STRING; } else if (is_number(&dbl, &isfloat, pl)) { if (isfloat) { val->type = JSON_DOUBLE; val->v.dbl = dbl; } else { val->type = JSON_INT; val->v.integer = dbl; } } else if (!pl_strcasecmp(pl, "false")) { val->v.boolean = false; val->type = JSON_BOOL; } else if (!pl_strcasecmp(pl, "true")) { val->v.boolean = true; val->type = JSON_BOOL; } else if (!pl_strcasecmp(pl, "null")) { val->type = JSON_NULL; } else { re_printf("json: value of unknown type: <%r>\n", pl); err = EBADMSG; } return err; } static int object_entry(const struct pl *pl_name, const struct pl *pl_val, json_object_entry_h *oeh, void *arg) { struct json_value val; char *name; int err; err = decode_name(&name, pl_name); if (err) return err; err = decode_value(&val, pl_val); if (err) goto out; if (oeh) err = oeh(name, &val, arg); if (val.type == JSON_STRING) mem_deref(val.v.str); out: mem_deref(name); return err; } static int array_entry(unsigned idx, const struct pl *pl_val, json_array_entry_h *aeh, void *arg) { struct json_value val; int err; err = decode_value(&val, pl_val); if (err) return err; if (aeh) err = aeh(idx, &val, arg); if (val.type == JSON_STRING) mem_deref(val.v.str); return err; } static int object_start(const struct pl *pl_name, unsigned idx, struct json_handlers *h) { char *name = NULL; int err = 0; if (pl_name->p) { err = decode_name(&name, pl_name); if (err) return err; } if (h->oh) err = h->oh(name, idx, h); mem_deref(name); return err; } static int array_start(const struct pl *pl_name, unsigned idx, struct json_handlers *h) { char *name = NULL; int err = 0; if (pl_name->p) { err = decode_name(&name, pl_name); if (err) return err; } if (h->ah) err = h->ah(name, idx, h); mem_deref(name); return err; } static inline int chkval(struct pl *val, const char *p) { if (!val->p || pp) return EINVAL; val->l = p - val->p; return 0; } static int _json_decode(const char **str, size_t *len, unsigned depth, unsigned maxdepth, json_object_h *oh, json_array_h *ah, json_object_entry_h *oeh, json_array_entry_h *aeh, void *arg) { bool esc = false, inquot = false, inobj = false, inarray = false; struct pl name = PL_INIT, val = PL_INIT; size_t ws = 0; unsigned idx = 0; int err; for (; *len>0; ++(*str), --(*len)) { if (inquot) { if (esc) esc = false; else if (**str == '\"') inquot = false; else if (**str == '\\') esc = true; continue; } switch (**str) { case ':': if (!inobj || name.p || chkval(&val, *str - ws)) return EBADMSG; name = val; val = pl_null; break; case ',': if (chkval(&val, *str - ws)) break; if (inobj) { if (!name.p) return EBADMSG; err = object_entry(&name, &val, oeh, arg); if (err) return err; } else if (inarray) { err = array_entry(idx, &val, aeh, arg); if (err) return err; ++idx; } else return EBADMSG; name = pl_null; val = pl_null; break; case '{': if (inobj || inarray) { struct json_handlers h = {oh,ah,oeh,aeh,arg}; if (depth >= maxdepth) return EOVERFLOW; if (inobj && !name.p) return EBADMSG; err = object_start(&name, idx, &h); if (err) return err; name = pl_null; err = _json_decode(str, len, depth + 1, maxdepth, h.oh, h.ah, h.oeh, h.aeh, h.arg); if (err) return err; if (inarray) ++idx; } else { inobj = true; } break; case '[': if (inobj || inarray) { struct json_handlers h = {oh,ah,oeh,aeh,arg}; if (depth >= maxdepth) return EOVERFLOW; if (inobj && !name.p) return EBADMSG; err = array_start(&name, idx, &h); if (err) return err; name = pl_null; err = _json_decode(str, len, depth + 1, maxdepth, h.oh, h.ah, h.oeh, h.aeh, h.arg); if (err) return err; if (inarray) ++idx; } else { inarray = true; idx = 0; } break; case '}': if (!inobj) return EBADMSG; if (chkval(&val, *str - ws)) return 0; if (!name.p) return EBADMSG; return object_entry(&name, &val, oeh, arg); case ']': if (!inarray) return EBADMSG; if (chkval(&val, *str - ws)) return 0; return array_entry(idx, &val, aeh, arg); case ' ': case '\t': case '\r': case '\n': ++ws; break; default: if (val.p) break; if (**str == '\"') inquot = true; val.p = *str; val.l = 0; ws = 0; break; } } if (inobj || inarray) return EBADMSG; return 0; } int json_decode(const char *str, size_t len, unsigned maxdepth, json_object_h *oh, json_array_h *ah, json_object_entry_h *oeh, json_array_entry_h *aeh, void *arg) { if (!str) return EINVAL; return _json_decode(&str, &len, 0, maxdepth, oh, ah, oeh, aeh, arg); } re-1.1.0/src/json/decode_odict.c000066400000000000000000000044561373627245400164560ustar00rootroot00000000000000/** * @file json/decode_odict.c JSON odict decode * * Copyright (C) 2010 - 2015 Creytiv.com */ #include #include #include #include #include #include #include static int container_add(const char *name, unsigned idx, enum odict_type type, struct json_handlers *h) { struct odict *o = h->arg, *oc; char index[64]; int err; if (!name) { if (re_snprintf(index, sizeof(index), "%u", idx) < 0) return ENOMEM; name = index; } err = odict_alloc(&oc, hash_bsize(o->ht)); if (err) return err; err = odict_entry_add(o, name, type, oc); mem_deref(oc); h->arg = oc; return err; } static int object_handler(const char *name, unsigned idx, struct json_handlers *h) { return container_add(name, idx, ODICT_OBJECT, h); } static int array_handler(const char *name, unsigned idx, struct json_handlers *h) { return container_add(name, idx, ODICT_ARRAY, h); } static int entry_add(struct odict *o, const char *name, const struct json_value *val) { switch (val->type) { case JSON_STRING: return odict_entry_add(o, name, ODICT_STRING, val->v.str); case JSON_INT: return odict_entry_add(o, name, ODICT_INT, val->v.integer); case JSON_DOUBLE: return odict_entry_add(o, name, ODICT_DOUBLE, val->v.dbl); case JSON_BOOL: return odict_entry_add(o, name, ODICT_BOOL, val->v.boolean); case JSON_NULL: return odict_entry_add(o, name, ODICT_NULL); default: return ENOSYS; } } static int object_entry_handler(const char *name, const struct json_value *val, void *arg) { struct odict *o = arg; return entry_add(o, name, val); } static int array_entry_handler(unsigned idx, const struct json_value *val, void *arg) { struct odict *o = arg; char index[64]; if (re_snprintf(index, sizeof(index), "%u", idx) < 0) return ENOMEM; return entry_add(o, index, val); } int json_decode_odict(struct odict **op, uint32_t hash_size, const char *str, size_t len, unsigned maxdepth) { struct odict *o; int err; if (!op || !str) return EINVAL; err = odict_alloc(&o, hash_size); if (err) return err; err = json_decode(str, len, maxdepth, object_handler, array_handler, object_entry_handler, array_entry_handler, o); if (err) mem_deref(o); else *op = o; return err; } re-1.1.0/src/json/encode.c000066400000000000000000000031621373627245400152770ustar00rootroot00000000000000/** * @file json/encode.c JSON encoder * * Copyright (C) 2010 - 2015 Creytiv.com */ #include #include #include #include #include static int encode_entry(struct re_printf *pf, const struct odict_entry *e) { struct odict *array; struct le *le; int err; if (!e) return 0; switch (e->type) { case ODICT_OBJECT: err = json_encode_odict(pf, e->u.odict); break; case ODICT_ARRAY: array = e->u.odict; if (!array) return 0; err = re_hprintf(pf, "["); for (le=array->lst.head; le; le=le->next) { const struct odict_entry *ae = le->data; err |= re_hprintf(pf, "%H%s", encode_entry, ae, le->next ? "," : ""); } err |= re_hprintf(pf, "]"); break; case ODICT_INT: err = re_hprintf(pf, "%lld", e->u.integer); break; case ODICT_DOUBLE: err = re_hprintf(pf, "%f", e->u.dbl); break; case ODICT_STRING: err = re_hprintf(pf, "\"%H\"", utf8_encode, e->u.str); break; case ODICT_BOOL: err = re_hprintf(pf, "%s", e->u.boolean ? "true" : "false"); break; case ODICT_NULL: err = re_hprintf(pf, "null"); break; default: re_fprintf(stderr, "json: unsupported type %d\n", e->type); err = EINVAL; } return err; } int json_encode_odict(struct re_printf *pf, const struct odict *o) { struct le *le; int err; if (!o) return 0; err = re_hprintf(pf, "{"); for (le=o->lst.head; le; le=le->next) { const struct odict_entry *e = le->data; err |= re_hprintf(pf, "\"%H\":%H%s", utf8_encode, e->key, encode_entry, e, le->next ? "," : ""); } err |= re_hprintf(pf, "}"); return err; } re-1.1.0/src/json/mod.mk000066400000000000000000000002001373627245400147740ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 - 2015 Creytiv.com # SRCS += json/decode.c SRCS += json/decode_odict.c SRCS += json/encode.c re-1.1.0/src/list/000077500000000000000000000000001373627245400136765ustar00rootroot00000000000000re-1.1.0/src/list/list.c000066400000000000000000000130331373627245400150150ustar00rootroot00000000000000/** * @file list.c Linked List implementation * * Copyright (C) 2010 Creytiv.com */ #include #include #include #define DEBUG_MODULE "list" #define DEBUG_LEVEL 5 #include /** * Initialise a linked list * * @param list Linked list */ void list_init(struct list *list) { if (!list) return; list->head = NULL; list->tail = NULL; } /** * Flush a linked list and free all elements * * @param list Linked list */ void list_flush(struct list *list) { struct le *le; if (!list) return; le = list->head; while (le) { struct le *next = le->next; void *data = le->data; le->list = NULL; le->prev = le->next = NULL; le->data = NULL; le = next; mem_deref(data); } list_init(list); } /** * Clear a linked list without dereferencing the elements * * @param list Linked list */ void list_clear(struct list *list) { struct le *le; if (!list) return; le = list->head; while (le) { struct le *next = le->next; le->list = NULL; le->prev = le->next = NULL; le->data = NULL; le = next; } list_init(list); } /** * Append a list element to a linked list * * @param list Linked list * @param le List element * @param data Element data */ void list_append(struct list *list, struct le *le, void *data) { if (!list || !le) return; if (le->list) { DEBUG_WARNING("append: le linked to %p\n", le->list); return; } le->prev = list->tail; le->next = NULL; le->list = list; le->data = data; if (!list->head) list->head = le; if (list->tail) list->tail->next = le; list->tail = le; } /** * Prepend a list element to a linked list * * @param list Linked list * @param le List element * @param data Element data */ void list_prepend(struct list *list, struct le *le, void *data) { if (!list || !le) return; if (le->list) { DEBUG_WARNING("prepend: le linked to %p\n", le->list); return; } le->prev = NULL; le->next = list->head; le->list = list; le->data = data; if (list->head) list->head->prev = le; if (!list->tail) list->tail = le; list->head = le; } /** * Insert a list element before a given list element * * @param list Linked list * @param le Given list element * @param ile List element to insert * @param data Element data */ void list_insert_before(struct list *list, struct le *le, struct le *ile, void *data) { if (!list || !le || !ile) return; if (ile->list) { DEBUG_WARNING("insert_before: le linked to %p\n", le->list); return; } if (le->prev) le->prev->next = ile; else if (list->head == le) list->head = ile; ile->prev = le->prev; ile->next = le; ile->list = list; ile->data = data; le->prev = ile; } /** * Insert a list element after a given list element * * @param list Linked list * @param le Given list element * @param ile List element to insert * @param data Element data */ void list_insert_after(struct list *list, struct le *le, struct le *ile, void *data) { if (!list || !le || !ile) return; if (ile->list) { DEBUG_WARNING("insert_after: le linked to %p\n", le->list); return; } if (le->next) le->next->prev = ile; else if (list->tail == le) list->tail = ile; ile->prev = le; ile->next = le->next; ile->list = list; ile->data = data; le->next = ile; } /** * Remove a list element from a linked list * * @param le List element to remove */ void list_unlink(struct le *le) { struct list *list; if (!le || !le->list) return; list = le->list; if (le->prev) le->prev->next = le->next; else list->head = le->next; if (le->next) le->next->prev = le->prev; else list->tail = le->prev; le->next = NULL; le->prev = NULL; le->list = NULL; } /** * Sort a linked list in an order defined by the sort handler * * @param list Linked list * @param sh Sort handler * @param arg Handler argument */ void list_sort(struct list *list, list_sort_h *sh, void *arg) { struct le *le; bool sort; if (!list || !sh) return; retry: le = list->head; sort = false; while (le && le->next) { if (sh(le, le->next, arg)) { le = le->next; } else { struct le *tle = le->next; list_unlink(le); list_insert_after(list, tle, le, le->data); sort = true; } } if (sort) { goto retry; } } /** * Call the apply handler for each element in a linked list * * @param list Linked list * @param fwd true to traverse from head to tail, false for reverse * @param ah Apply handler * @param arg Handler argument * * @return Current list element if handler returned true */ struct le *list_apply(const struct list *list, bool fwd, list_apply_h *ah, void *arg) { struct le *le; if (!list || !ah) return NULL; le = fwd ? list->head : list->tail; while (le) { struct le *cur = le; le = fwd ? le->next : le->prev; if (ah(cur, arg)) return cur; } return NULL; } /** * Get the first element in a linked list * * @param list Linked list * * @return First list element (NULL if empty) */ struct le *list_head(const struct list *list) { return list ? list->head : NULL; } /** * Get the last element in a linked list * * @param list Linked list * * @return Last list element (NULL if empty) */ struct le *list_tail(const struct list *list) { return list ? list->tail : NULL; } /** * Get the number of elements in a linked list * * @param list Linked list * * @return Number of list elements */ uint32_t list_count(const struct list *list) { uint32_t n = 0; struct le *le; if (!list) return 0; for (le = list->head; le; le = le->next) ++n; return n; } re-1.1.0/src/list/mod.mk000066400000000000000000000001051373627245400150020ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += list/list.c re-1.1.0/src/lock/000077500000000000000000000000001373627245400136535ustar00rootroot00000000000000re-1.1.0/src/lock/lock.c000066400000000000000000000042441373627245400147530ustar00rootroot00000000000000/** * @file lock/lock.c Pthread mutex locking * * Copyright (C) 2010 Creytiv.com */ #define _DEFAULT_SOURCE 1 #define __USE_UNIX98 1 #include #include #include #include #define DEBUG_MODULE "lock" #define DEBUG_LEVEL 5 #include #ifndef RELEASE #define LOCK_DEBUG 0 #endif /** Defines a lock */ struct lock { pthread_mutex_t m; }; static void lock_destructor(void *data) { struct lock *l = data; int err = pthread_mutex_destroy(&l->m); if (err) { DEBUG_WARNING("pthread_mutex_destroy: %m\n", err); } } /** * Allocate a new lock * * @param lp Pointer to allocated lock object * * @return 0 if success, otherwise errorcode */ int lock_alloc(struct lock **lp) { pthread_mutexattr_t attr; struct lock *l; if (!lp) return EINVAL; l = mem_zalloc(sizeof(*l), lock_destructor); if (!l) return ENOMEM; (void)pthread_mutex_init(&l->m, NULL); pthread_mutexattr_init(&attr); #if LOCK_DEBUG pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); DEBUG_NOTICE("init debug lock\n"); #else pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL); #endif pthread_mutex_init(&l->m, &attr); *lp = l; return 0; } /** * Get the lock for reading * * @param l Lock object */ void lock_read_get(struct lock *l) { const int err = pthread_mutex_lock(&l->m); if (err) { DEBUG_WARNING("lock_read_get: %m\n", err); } } /** * Get the lock for writing * * @param l Lock object */ void lock_write_get(struct lock *l) { const int err = pthread_mutex_lock(&l->m); if (err) { DEBUG_WARNING("lock_write_get: %m\n", err); } } /** * Attempt to get a lock for reading * * @param l Lock object * * @return 0 if success, otherwise errorcode */ int lock_read_try(struct lock *l) { return pthread_mutex_trylock(&l->m); } /** * Attempt to get a lock for writing * * @param l Lock object * * @return 0 if success, otherwise errorcode */ int lock_write_try(struct lock *l) { return pthread_mutex_trylock(&l->m); } /** * Release a lock * * @param l Lock object */ void lock_rel(struct lock *l) { const int err = pthread_mutex_unlock(&l->m); if (err) { DEBUG_WARNING("lock_rel: %m\n", err); } } re-1.1.0/src/lock/mod.mk000066400000000000000000000003151373627245400147620ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # ifdef HAVE_PTHREAD_RWLOCK SRCS += lock/rwlock.c else ifdef HAVE_PTHREAD SRCS += lock/lock.c endif endif ifeq ($(OS),win32) SRCS += lock/win32/lock.c endif re-1.1.0/src/lock/rwlock.c000066400000000000000000000027571373627245400153330ustar00rootroot00000000000000/** * @file rwlock.c Pthread read/write locking * * Copyright (C) 2010 Creytiv.com */ #define _GNU_SOURCE 1 #include #include #include #include #define DEBUG_MODULE "rwlock" #define DEBUG_LEVEL 5 #include struct lock { pthread_rwlock_t lock; }; static void lock_destructor(void *data) { struct lock *l = data; int err = pthread_rwlock_destroy(&l->lock); if (err) { DEBUG_WARNING("pthread_rwlock_destroy: %m\n", err); } } int lock_alloc(struct lock **lp) { struct lock *l; int err; if (!lp) return EINVAL; l = mem_zalloc(sizeof(*l), lock_destructor); if (!l) return ENOMEM; err = pthread_rwlock_init(&l->lock, NULL); if (err) goto out; *lp = l; out: if (err) mem_deref(l); return err; } void lock_read_get(struct lock *l) { int err; if (!l) return; err = pthread_rwlock_rdlock(&l->lock); if (err) { DEBUG_WARNING("lock_read_get: %m\n", err); } } void lock_write_get(struct lock *l) { int err; if (!l) return; err = pthread_rwlock_wrlock(&l->lock); if (err) { DEBUG_WARNING("lock_write_get: %m\n", err); } } int lock_read_try(struct lock *l) { if (!l) return EINVAL; return pthread_rwlock_tryrdlock(&l->lock); } int lock_write_try(struct lock *l) { if (!l) return EINVAL; return pthread_rwlock_trywrlock(&l->lock); } void lock_rel(struct lock *l) { int err; if (!l) return; err = pthread_rwlock_unlock(&l->lock); if (err) { DEBUG_WARNING("lock_rel: %m\n", err); } } re-1.1.0/src/lock/win32/000077500000000000000000000000001373627245400146155ustar00rootroot00000000000000re-1.1.0/src/lock/win32/lock.c000066400000000000000000000017151373627245400157150ustar00rootroot00000000000000/** * @file win32/lock.c Locking for Windows * * Copyright (C) 2010 Creytiv.com */ #undef _WIN32_WINNT #define _WIN32_WINNT 0x0400 #include #include #include #include struct lock { CRITICAL_SECTION c; }; static void lock_destructor(void *data) { struct lock *l = data; DeleteCriticalSection(&l->c); } int lock_alloc(struct lock **lp) { struct lock *l; if (!lp) return EINVAL; l = mem_alloc(sizeof(*l), lock_destructor); if (!l) return ENOMEM; InitializeCriticalSection(&l->c); *lp = l; return 0; } void lock_read_get(struct lock *l) { EnterCriticalSection(&l->c); } void lock_write_get(struct lock *l) { EnterCriticalSection(&l->c); } int lock_read_try(struct lock *l) { return TryEnterCriticalSection(&l->c) ? 0 : ENODEV; } int lock_write_try(struct lock *l) { return TryEnterCriticalSection(&l->c) ? 0 : ENODEV; } void lock_rel(struct lock *l) { LeaveCriticalSection(&l->c); } re-1.1.0/src/main/000077500000000000000000000000001373627245400136475ustar00rootroot00000000000000re-1.1.0/src/main/epoll.c000066400000000000000000000016531373627245400151330ustar00rootroot00000000000000/** * @file epoll.c epoll specific routines * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include "main.h" #define DEBUG_MODULE "epoll" #define DEBUG_LEVEL 5 #include /** * Check for working epoll() kernel support * * @return true if support, false if not */ bool epoll_check(void) { uint32_t osrel; int err, epfd; err = sys_rel_get(&osrel, NULL, NULL, NULL); if (err) return false; if (osrel < 0x020542) { DEBUG_INFO("epoll not supported in osrel=0x%08x\n", osrel); return false; } #ifdef OPENWRT /* epoll is working again with 2.6.25.7 */ if (osrel < 0x020619) { DEBUG_NOTICE("epoll is broken in osrel=0x%08x\n", osrel); return false; } #endif epfd = epoll_create(64); if (-1 == epfd) { DEBUG_NOTICE("epoll_create: %m\n", errno); return false; } (void)close(epfd); return true; } re-1.1.0/src/main/init.c000066400000000000000000000013611373627245400147570ustar00rootroot00000000000000/** * @file init.c Main initialisation routine * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include "main.h" /** * Initialise main library * * @return 0 if success, errorcode if failure */ int libre_init(void) { int err; rand_init(); #ifdef USE_OPENSSL err = openssl_init(); if (err) goto out; #endif err = net_sock_init(); if (err) goto out; out: if (err) { net_sock_close(); #ifdef USE_OPENSSL openssl_close(); #endif } return err; } /** * Close library and free up all resources */ void libre_close(void) { (void)fd_setsize(0); net_sock_close(); #ifdef USE_OPENSSL openssl_close(); #endif } re-1.1.0/src/main/main.c000066400000000000000000000535101373627245400147430ustar00rootroot00000000000000/** * @file main.c Main polling routine * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_SYS_TIME_H #include #endif #include #undef _STRICT_ANSI #include #ifdef HAVE_UNISTD_H #include #endif #ifdef WIN32 #include #endif #ifdef HAVE_SIGNAL #include #endif #ifdef HAVE_SELECT_H #include #endif #ifdef HAVE_POLL #include #endif #ifdef HAVE_EPOLL #include #endif #ifdef HAVE_KQUEUE #include #include #include #undef LIST_INIT #undef LIST_FOREACH #endif #include #include #include #include #include #include #include #include "main.h" #ifdef HAVE_PTHREAD #define __USE_GNU 1 #include #include #endif #define DEBUG_MODULE "main" #define DEBUG_LEVEL 5 #include /* epoll() has been tested successfully on the following kernels: - Linux 2.6.16.29-xen (Debian 4.0 etch) - Linux 2.6.18-4-amd64 (Debian 4.0 etch) TODO clean this up - The polling method is selectable both in compile-time and run-time - The polling method can be changed in run time. this is cool! - Maximum number of fds can be set from application, but only once! - Look at howto optimise main loop */ #if !defined (RELEASE) && !defined (MAIN_DEBUG) #define MAIN_DEBUG 1 /**< Enable main loop debugging */ #endif /** Main loop values */ enum { MAX_BLOCKING = 100, /**< Maximum time spent in handler in [ms] */ #if defined (FD_SETSIZE) DEFAULT_MAXFDS = FD_SETSIZE #else DEFAULT_MAXFDS = 128 #endif }; /** File descriptor handler struct */ struct fhs { int fd; /**< File Descriptor */ int flags; /**< Polling flags (Read, Write, etc.) */ fd_h* fh; /**< Event handler */ void* arg; /**< Handler argument */ }; /** Polling loop data */ struct re { struct fhs *fhs; /** File descriptor handler set */ int maxfds; /**< Maximum number of polling fds */ int nfds; /**< Number of active file descriptors */ enum poll_method method; /**< The current polling method */ bool update; /**< File descriptor set need updating */ bool polling; /**< Is polling flag */ int sig; /**< Last caught signal */ struct list tmrl; /**< List of timers */ #ifdef HAVE_POLL struct pollfd *fds; /**< Event set for poll() */ #endif #ifdef HAVE_EPOLL struct epoll_event *events; /**< Event set for epoll() */ int epfd; /**< epoll control file descriptor */ #endif #ifdef HAVE_KQUEUE struct kevent *evlist; int kqfd; #endif #ifdef HAVE_PTHREAD pthread_mutex_t mutex; /**< Mutex for thread synchronization */ pthread_mutex_t *mutexp; /**< Pointer to active mutex */ #endif }; static struct re global_re = { NULL, 0, 0, METHOD_NULL, false, false, 0, LIST_INIT, #ifdef HAVE_POLL NULL, #endif #ifdef HAVE_EPOLL NULL, -1, #endif #ifdef HAVE_KQUEUE NULL, -1, #endif #ifdef HAVE_PTHREAD #if MAIN_DEBUG && defined (PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP) PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP, #else PTHREAD_MUTEX_INITIALIZER, #endif &global_re.mutex, #endif }; #ifdef HAVE_PTHREAD static void poll_close(struct re *re); static pthread_once_t pt_once = PTHREAD_ONCE_INIT; static pthread_key_t pt_key; static void thread_destructor(void *arg) { poll_close(arg); free(arg); } static void re_once(void) { pthread_key_create(&pt_key, thread_destructor); } static struct re *re_get(void) { struct re *re; pthread_once(&pt_once, re_once); re = pthread_getspecific(pt_key); if (!re) { re = &global_re; } return re; } static inline void re_lock(struct re *re) { int err; err = pthread_mutex_lock(re->mutexp); if (err) { DEBUG_WARNING("re_lock: %m\n", err); } } static inline void re_unlock(struct re *re) { int err; err = pthread_mutex_unlock(re->mutexp); if (err) { DEBUG_WARNING("re_unlock: %m\n", err); } } #else static struct re *re_get(void) { return &global_re; } #define re_lock(x) /**< Stub */ #define re_unlock(x) /**< Stub */ #endif #ifdef WIN32 /** * This code emulates POSIX numbering. There is no locking, * so zero thread-safety. * * @param re Poll state * @param fd File descriptor * * @return fhs index if success, otherwise -1 */ static int lookup_fd_index(struct re* re, int fd) { int i; for (i = 0; i < re->nfds; i++) { if (!re->fhs[i].fh) continue; if (re->fhs[i].fd == fd) return i; } /* if nothing is found a linear search for the first * zeroed handler */ for (i = 0; i < re->maxfds; i++) { if (!re->fhs[i].fh) return i; } return -1; } #endif #if MAIN_DEBUG /** * Call the application event handler * * @param re Poll state * @param i File descriptor handler index * @param flags Event flags */ static void fd_handler(struct re *re, int i, int flags) { const uint64_t tick = tmr_jiffies(); uint32_t diff; DEBUG_INFO("event on fd=%d index=%d (flags=0x%02x)...\n", re->fhs[i].fd, i, flags); re->fhs[i].fh(flags, re->fhs[i].arg); diff = (uint32_t)(tmr_jiffies() - tick); if (diff > MAX_BLOCKING) { DEBUG_WARNING("long async blocking: %u>%u ms (h=%p arg=%p)\n", diff, MAX_BLOCKING, re->fhs[i].fh, re->fhs[i].arg); } } #endif #ifdef HAVE_POLL static int set_poll_fds(struct re *re, int fd, int flags) { if (!re->fds) return 0; if (flags) re->fds[fd].fd = fd; else re->fds[fd].fd = -1; re->fds[fd].events = 0; if (flags & FD_READ) re->fds[fd].events |= POLLIN; if (flags & FD_WRITE) re->fds[fd].events |= POLLOUT; if (flags & FD_EXCEPT) re->fds[fd].events |= POLLERR; return 0; } #endif #ifdef HAVE_EPOLL static int set_epoll_fds(struct re *re, int fd, int flags) { struct epoll_event event; int err = 0; if (re->epfd < 0) return EBADFD; memset(&event, 0, sizeof(event)); DEBUG_INFO("set_epoll_fds: fd=%d flags=0x%02x\n", fd, flags); if (flags) { event.data.fd = fd; if (flags & FD_READ) event.events |= EPOLLIN; if (flags & FD_WRITE) event.events |= EPOLLOUT; if (flags & FD_EXCEPT) event.events |= EPOLLERR; /* Try to add it first */ if (-1 == epoll_ctl(re->epfd, EPOLL_CTL_ADD, fd, &event)) { /* If already exist then modify it */ if (EEXIST == errno) { if (-1 == epoll_ctl(re->epfd, EPOLL_CTL_MOD, fd, &event)) { err = errno; DEBUG_WARNING("epoll_ctl:" " EPOLL_CTL_MOD:" " fd=%d (%m)\n", fd, err); } } else { err = errno; DEBUG_WARNING("epoll_ctl: EPOLL_CTL_ADD:" " fd=%d (%m)\n", fd, err); } } } else { if (-1 == epoll_ctl(re->epfd, EPOLL_CTL_DEL, fd, &event)) { err = errno; DEBUG_INFO("epoll_ctl: EPOLL_CTL_DEL: fd=%d (%m)\n", fd, err); } } return err; } #endif #ifdef HAVE_KQUEUE static int set_kqueue_fds(struct re *re, int fd, int flags) { struct kevent kev[2]; int r, n = 0; memset(kev, 0, sizeof(kev)); /* always delete the events */ EV_SET(&kev[0], fd, EVFILT_READ, EV_DELETE, 0, 0, 0); EV_SET(&kev[1], fd, EVFILT_WRITE, EV_DELETE, 0, 0, 0); kevent(re->kqfd, kev, 2, NULL, 0, NULL); memset(kev, 0, sizeof(kev)); if (flags & FD_WRITE) { EV_SET(&kev[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, 0); ++n; } if (flags & FD_READ) { EV_SET(&kev[n], fd, EVFILT_READ, EV_ADD, 0, 0, 0); ++n; } if (n) { r = kevent(re->kqfd, kev, n, NULL, 0, NULL); if (r < 0) { int err = errno; DEBUG_WARNING("set: [fd=%d, flags=%x] kevent: %m\n", fd, flags, err); return err; } } return 0; } #endif /** * Rebuild the file descriptor mapping table. This must be done whenever * the polling method is changed. */ static int rebuild_fds(struct re *re) { int i, err = 0; DEBUG_INFO("rebuilding fds (nfds=%d)\n", re->nfds); /* Update fd sets */ for (i=0; infds; i++) { if (!re->fhs[i].fh) continue; switch (re->method) { #ifdef HAVE_POLL case METHOD_POLL: err = set_poll_fds(re, i, re->fhs[i].flags); break; #endif #ifdef HAVE_EPOLL case METHOD_EPOLL: err = set_epoll_fds(re, i, re->fhs[i].flags); break; #endif #ifdef HAVE_KQUEUE case METHOD_KQUEUE: err = set_kqueue_fds(re, i, re->fhs[i].flags); break; #endif default: break; } if (err) break; } return err; } static int poll_init(struct re *re) { DEBUG_INFO("poll init (maxfds=%d)\n", re->maxfds); if (!re->maxfds) { DEBUG_WARNING("poll init: maxfds is 0\n"); return EINVAL; } switch (re->method) { #ifdef HAVE_POLL case METHOD_POLL: if (!re->fds) { re->fds = mem_zalloc(re->maxfds * sizeof(*re->fds), NULL); if (!re->fds) return ENOMEM; } break; #endif #ifdef HAVE_EPOLL case METHOD_EPOLL: if (!re->events) { DEBUG_INFO("allocate %u bytes for epoll set\n", re->maxfds * sizeof(*re->events)); re->events = mem_zalloc(re->maxfds*sizeof(*re->events), NULL); if (!re->events) return ENOMEM; } if (re->epfd < 0 && -1 == (re->epfd = epoll_create(re->maxfds))) { int err = errno; DEBUG_WARNING("epoll_create: %m (maxfds=%d)\n", err, re->maxfds); return err; } DEBUG_INFO("init: epoll_create() epfd=%d\n", re->epfd); break; #endif #ifdef HAVE_KQUEUE case METHOD_KQUEUE: if (!re->evlist) { size_t sz = re->maxfds * sizeof(*re->evlist); re->evlist = mem_zalloc(sz, NULL); if (!re->evlist) return ENOMEM; } if (re->kqfd < 0) { re->kqfd = kqueue(); if (re->kqfd < 0) return errno; DEBUG_INFO("kqueue: fd=%d\n", re->kqfd); } break; #endif default: break; } return 0; } /** Free all resources */ static void poll_close(struct re *re) { DEBUG_INFO("poll close\n"); re->fhs = mem_deref(re->fhs); re->maxfds = 0; #ifdef HAVE_POLL re->fds = mem_deref(re->fds); #endif #ifdef HAVE_EPOLL DEBUG_INFO("poll_close: epfd=%d\n", re->epfd); if (re->epfd >= 0) { (void)close(re->epfd); re->epfd = -1; } re->events = mem_deref(re->events); #endif #ifdef HAVE_KQUEUE if (re->kqfd >= 0) { close(re->kqfd); re->kqfd = -1; } re->evlist = mem_deref(re->evlist); #endif } static int poll_setup(struct re *re) { int err; err = fd_setsize(DEFAULT_MAXFDS); if (err) goto out; if (METHOD_NULL == re->method) { err = poll_method_set(poll_method_best()); if (err) goto out; DEBUG_INFO("poll setup: poll method not set - set to `%s'\n", poll_method_name(re->method)); } err = poll_init(re); out: if (err) poll_close(re); return err; } /** * Listen for events on a file descriptor * * @param fd File descriptor * @param flags Wanted event flags * @param fh Event handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int fd_listen(int fd, int flags, fd_h *fh, void *arg) { struct re *re = re_get(); int err = 0; int i = fd; DEBUG_INFO("fd_listen: fd=%d flags=0x%02x\n", fd, flags); if (fd < 0) { DEBUG_WARNING("fd_listen: corrupt fd %d\n", fd); return EBADF; } if (flags || fh) { err = poll_setup(re); if (err) return err; } #ifdef WIN32 /* Windows file descriptors do not follow POSIX standard ranges. */ i = lookup_fd_index(re, fd); if (i < 0) { DEBUG_WARNING("fd_listen: fd=%d - no free fd_index\n", fd); return EMFILE; } #endif if (i >= re->maxfds) { if (flags) { DEBUG_WARNING("fd_listen: fd=%d flags=0x%02x" " - Max %d fds\n", fd, flags, re->maxfds); } return EMFILE; } /* Update fh set */ if (re->fhs) { re->fhs[i].fd = fd; re->fhs[i].flags = flags; re->fhs[i].fh = fh; re->fhs[i].arg = arg; } re->nfds = max(re->nfds, i+1); switch (re->method) { #ifdef HAVE_POLL case METHOD_POLL: err = set_poll_fds(re, fd, flags); break; #endif #ifdef HAVE_EPOLL case METHOD_EPOLL: if (re->epfd < 0) return EBADFD; err = set_epoll_fds(re, fd, flags); break; #endif #ifdef HAVE_KQUEUE case METHOD_KQUEUE: err = set_kqueue_fds(re, fd, flags); break; #endif default: break; } if (err) { if (flags && fh) { fd_close(fd); DEBUG_WARNING("fd_listen: fd=%d flags=0x%02x (%m)\n", fd, flags, err); } } return err; } /** * Stop listening for events on a file descriptor * * @param fd File descriptor */ void fd_close(int fd) { (void)fd_listen(fd, 0, NULL, NULL); } /** * Polling loop * * @param re Poll state. * * @return 0 if success, otherwise errorcode */ static int fd_poll(struct re *re) { const uint64_t to = tmr_next_timeout(&re->tmrl); int i, n, index; #ifdef HAVE_SELECT fd_set rfds, wfds, efds; #endif DEBUG_INFO("next timer: %llu ms\n", to); /* Wait for I/O */ switch (re->method) { #ifdef HAVE_POLL case METHOD_POLL: re_unlock(re); n = poll(re->fds, re->nfds, to ? (int)to : -1); re_lock(re); break; #endif #ifdef HAVE_SELECT case METHOD_SELECT: { struct timeval tv; /* Clear and update fd sets */ FD_ZERO(&rfds); FD_ZERO(&wfds); FD_ZERO(&efds); for (i=0; infds; i++) { int fd = re->fhs[i].fd; if (!re->fhs[i].fh) continue; if (re->fhs[i].flags & FD_READ) FD_SET(fd, &rfds); if (re->fhs[i].flags & FD_WRITE) FD_SET(fd, &wfds); if (re->fhs[i].flags & FD_EXCEPT) FD_SET(fd, &efds); } #ifdef WIN32 tv.tv_sec = (long) to / 1000; #else tv.tv_sec = (time_t) to / 1000; #endif tv.tv_usec = (uint32_t) (to % 1000) * 1000; re_unlock(re); n = select(re->nfds, &rfds, &wfds, &efds, to ? &tv : NULL); re_lock(re); } break; #endif #ifdef HAVE_EPOLL case METHOD_EPOLL: re_unlock(re); n = epoll_wait(re->epfd, re->events, re->maxfds, to ? (int)to : -1); re_lock(re); break; #endif #ifdef HAVE_KQUEUE case METHOD_KQUEUE: { struct timespec timeout; timeout.tv_sec = (time_t) (to / 1000); timeout.tv_nsec = (to % 1000) * 1000000; re_unlock(re); n = kevent(re->kqfd, NULL, 0, re->evlist, re->maxfds, to ? &timeout : NULL); re_lock(re); } break; #endif default: (void)to; DEBUG_WARNING("no polling method set\n"); return EINVAL; } if (n < 0) return errno; /* Check for events */ for (i=0; (n > 0) && (i < re->nfds); i++) { int fd, flags = 0; switch (re->method) { #ifdef HAVE_POLL case METHOD_POLL: fd = i; if (re->fds[fd].revents & POLLIN) flags |= FD_READ; if (re->fds[fd].revents & POLLOUT) flags |= FD_WRITE; if (re->fds[fd].revents & (POLLERR|POLLHUP|POLLNVAL)) flags |= FD_EXCEPT; if (re->fds[fd].revents & POLLNVAL) { DEBUG_WARNING("event: fd=%d POLLNVAL" " (fds.fd=%d," " fds.events=0x%02x)\n", fd, re->fds[fd].fd, re->fds[fd].events); } /* Clear events */ re->fds[fd].revents = 0; break; #endif #ifdef HAVE_SELECT case METHOD_SELECT: fd = re->fhs[i].fd; if (FD_ISSET(fd, &rfds)) flags |= FD_READ; if (FD_ISSET(fd, &wfds)) flags |= FD_WRITE; if (FD_ISSET(fd, &efds)) flags |= FD_EXCEPT; break; #endif #ifdef HAVE_EPOLL case METHOD_EPOLL: fd = re->events[i].data.fd; if (re->events[i].events & EPOLLIN) flags |= FD_READ; if (re->events[i].events & EPOLLOUT) flags |= FD_WRITE; if (re->events[i].events & (EPOLLERR|EPOLLHUP)) flags |= FD_EXCEPT; if (!flags) { DEBUG_WARNING("epoll: no flags fd=%d\n", fd); } break; #endif #ifdef HAVE_KQUEUE case METHOD_KQUEUE: { struct kevent *kev = &re->evlist[i]; fd = (int)kev->ident; if (fd >= re->maxfds) { DEBUG_WARNING("large fd=%d\n", fd); break; } if (kev->filter == EVFILT_READ) flags |= FD_READ; else if (kev->filter == EVFILT_WRITE) flags |= FD_WRITE; else { DEBUG_WARNING("kqueue: unhandled " "filter %x\n", kev->filter); } if (kev->flags & EV_EOF) { flags |= FD_EXCEPT; } if (kev->flags & EV_ERROR) { DEBUG_WARNING("kqueue: EV_ERROR on fd %d\n", fd); } if (!flags) { DEBUG_WARNING("kqueue: no flags fd=%d\n", fd); } } break; #endif default: return EINVAL; } if (!flags) continue; #ifdef WIN32 index = i; #else index = fd; #endif if (re->fhs[index].fh) { #if MAIN_DEBUG fd_handler(re, index, flags); #else re->fhs[index].fh(flags, re->fhs[index].arg); #endif } /* Check if polling method was changed */ if (re->update) { re->update = false; return 0; } --n; } return 0; } /** * Set the maximum number of file descriptors * * @param maxfds Max FDs. 0 to free. * * @return 0 if success, otherwise errorcode */ int fd_setsize(int maxfds) { struct re *re = re_get(); if (!maxfds) { fd_debug(); poll_close(re); return 0; } if (!re->maxfds) re->maxfds = maxfds; if (!re->fhs) { DEBUG_INFO("fd_setsize: maxfds=%d, allocating %u bytes\n", re->maxfds, re->maxfds * sizeof(*re->fhs)); re->fhs = mem_zalloc(re->maxfds * sizeof(*re->fhs), NULL); if (!re->fhs) return ENOMEM; } return 0; } /** * Print all file descriptors in-use */ void fd_debug(void) { const struct re *re = re_get(); int i; if (!re->fhs) return; for (i=0; infds; i++) { if (!re->fhs[i].flags) continue; (void)re_fprintf(stderr, "fd %d in use: flags=%x fh=%p arg=%p\n", i, re->fhs[i].flags, re->fhs[i].fh, re->fhs[i].arg); } } #ifdef HAVE_SIGNAL /* Thread-safe signal handling */ static void signal_handler(int sig) { (void)signal(sig, signal_handler); re_get()->sig = sig; } #endif /** * Main polling loop for async I/O events. This function will only return when * re_cancel() is called or an error occured. * * @param signalh Optional Signal handler * * @return 0 if success, otherwise errorcode */ int re_main(re_signal_h *signalh) { struct re *re = re_get(); int err; #ifdef HAVE_SIGNAL if (signalh) { (void)signal(SIGINT, signal_handler); (void)signal(SIGALRM, signal_handler); (void)signal(SIGTERM, signal_handler); } #endif if (re->polling) { DEBUG_WARNING("main loop already polling\n"); return EALREADY; } err = poll_setup(re); if (err) goto out; DEBUG_INFO("Using async I/O polling method: `%s'\n", poll_method_name(re->method)); re->polling = true; re_lock(re); for (;;) { if (re->sig) { if (signalh) signalh(re->sig); re->sig = 0; } if (!re->polling) { err = 0; break; } err = fd_poll(re); if (err) { if (EINTR == err) continue; #ifdef DARWIN /* NOTE: workaround for Darwin */ if (EBADF == err) continue; #endif break; } tmr_poll(&re->tmrl); } re_unlock(re); out: re->polling = false; return err; } /** * Cancel the main polling loop */ void re_cancel(void) { struct re *re = re_get(); re->polling = false; } /** * Debug the main polling loop * * @param pf Print handler where debug output is printed to * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int re_debug(struct re_printf *pf, void *unused) { struct re *re = re_get(); int err = 0; (void)unused; err |= re_hprintf(pf, "re main loop:\n"); err |= re_hprintf(pf, " maxfds: %d\n", re->maxfds); err |= re_hprintf(pf, " nfds: %d\n", re->nfds); err |= re_hprintf(pf, " method: %d (%s)\n", re->method, poll_method_name(re->method)); return err; } /** * Set async I/O polling method. This function can also be called while the * program is running. * * @param method New polling method * * @return 0 if success, otherwise errorcode */ int poll_method_set(enum poll_method method) { struct re *re = re_get(); int err; err = fd_setsize(DEFAULT_MAXFDS); if (err) return err; switch (method) { #ifdef HAVE_POLL case METHOD_POLL: break; #endif #ifdef HAVE_SELECT case METHOD_SELECT: if (re->maxfds > (int)FD_SETSIZE) { DEBUG_WARNING("SELECT: maxfds > FD_SETSIZE\n"); return EMFILE; } break; #endif #ifdef HAVE_EPOLL case METHOD_EPOLL: if (!epoll_check()) return EINVAL; break; #endif #ifdef HAVE_KQUEUE case METHOD_KQUEUE: break; #endif default: DEBUG_WARNING("poll method not supported: '%s'\n", poll_method_name(method)); return EINVAL; } re->method = method; re->update = true; DEBUG_INFO("Setting async I/O polling method to `%s'\n", poll_method_name(re->method)); err = poll_init(re); if (err) return err; return rebuild_fds(re); } /** * Add a worker thread for this thread * * @return 0 if success, otherwise errorcode */ int re_thread_init(void) { #ifdef HAVE_PTHREAD struct re *re; pthread_once(&pt_once, re_once); re = pthread_getspecific(pt_key); if (re) { DEBUG_WARNING("thread_init: already added for thread %d\n", pthread_self()); return EALREADY; } re = malloc(sizeof(*re)); if (!re) return ENOMEM; memset(re, 0, sizeof(*re)); pthread_mutex_init(&re->mutex, NULL); re->mutexp = &re->mutex; #ifdef HAVE_EPOLL re->epfd = -1; #endif #ifdef HAVE_KQUEUE re->kqfd = -1; #endif pthread_setspecific(pt_key, re); return 0; #else return ENOSYS; #endif } /** * Remove the worker thread for this thread */ void re_thread_close(void) { #ifdef HAVE_PTHREAD struct re *re; pthread_once(&pt_once, re_once); re = pthread_getspecific(pt_key); if (re) { poll_close(re); free(re); pthread_setspecific(pt_key, NULL); } #endif } /** * Enter an 're' thread * * @note Must only be called from a non-re thread */ void re_thread_enter(void) { re_lock(re_get()); } /** * Leave an 're' thread * * @note Must only be called from a non-re thread */ void re_thread_leave(void) { re_unlock(re_get()); } /** * Set an external mutex for this thread * * @param mutexp Pointer to external mutex, NULL to use internal */ void re_set_mutex(void *mutexp) { #ifdef HAVE_PTHREAD struct re *re = re_get(); re->mutexp = mutexp ? mutexp : &re->mutex; #else (void)mutexp; #endif } /** * Get the timer-list for this thread * * @return Timer list * * @note only used by tmr module */ struct list *tmrl_get(void); struct list *tmrl_get(void) { return &re_get()->tmrl; } re-1.1.0/src/main/main.h000066400000000000000000000004551373627245400147500ustar00rootroot00000000000000/** * @file main.h Internal interface to main polling loop * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_EPOLL bool epoll_check(void); #endif #ifdef __cplusplus extern "C" { #endif #ifdef USE_OPENSSL int openssl_init(void); void openssl_close(void); #endif #ifdef __cplusplus } #endif re-1.1.0/src/main/method.c000066400000000000000000000037451373627245400153040ustar00rootroot00000000000000/** * @file method.c Polling methods * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include "main.h" static const char str_poll[] = "poll"; /**< POSIX.1-2001 poll */ static const char str_select[] = "select"; /**< POSIX.1-2001 select */ static const char str_epoll[] = "epoll"; /**< Linux epoll */ static const char str_kqueue[] = "kqueue"; /** * Choose the best async I/O polling method * * @return Polling method */ enum poll_method poll_method_best(void) { enum poll_method m = METHOD_NULL; #ifdef HAVE_EPOLL /* Supported from Linux 2.5.66 */ if (METHOD_NULL == m) { if (epoll_check()) m = METHOD_EPOLL; } #endif #ifdef HAVE_KQUEUE if (METHOD_NULL == m) { m = METHOD_KQUEUE; } #endif #ifdef HAVE_POLL if (METHOD_NULL == m) { m = METHOD_POLL; } #endif #ifdef HAVE_SELECT if (METHOD_NULL == m) { m = METHOD_SELECT; } #endif return m; } /** * Get the name of the polling method * * @param method Polling method * * @return Polling name string */ const char *poll_method_name(enum poll_method method) { switch (method) { case METHOD_POLL: return str_poll; case METHOD_SELECT: return str_select; case METHOD_EPOLL: return str_epoll; case METHOD_KQUEUE: return str_kqueue; default: return "???"; } } /** * Get the polling method type from a string * * @param method Returned polling method * @param name Polling method name string * * @return 0 if success, otherwise errorcode */ int poll_method_type(enum poll_method *method, const struct pl *name) { if (!method || !name) return EINVAL; if (0 == pl_strcasecmp(name, str_poll)) *method = METHOD_POLL; else if (0 == pl_strcasecmp(name, str_select)) *method = METHOD_SELECT; else if (0 == pl_strcasecmp(name, str_epoll)) *method = METHOD_EPOLL; else if (0 == pl_strcasecmp(name, str_kqueue)) *method = METHOD_KQUEUE; else return ENOENT; return 0; } re-1.1.0/src/main/mod.mk000066400000000000000000000003331373627245400147560ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += main/init.c SRCS += main/main.c SRCS += main/method.c ifneq ($(HAVE_EPOLL),) SRCS += main/epoll.c endif ifneq ($(USE_OPENSSL),) SRCS += main/openssl.c endif re-1.1.0/src/main/openssl.c000066400000000000000000000063021373627245400154770ustar00rootroot00000000000000/** * @file openssl.c OpenSSL initialisation and multi-threading routines * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_SIGNAL #include #endif #ifdef HAVE_PTHREAD #include #endif #include #include #include #include #include #include #include "main.h" #if defined (HAVE_PTHREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L) static pthread_mutex_t *lockv; static inline unsigned long threadid(void) { #if defined (DARWIN) || defined (FREEBSD) || defined (OPENBSD) || \ defined (NETBSD) || defined (DRAGONFLY) return (unsigned long)(void *)pthread_self(); #else return (unsigned long)pthread_self(); #endif } #if OPENSSL_VERSION_NUMBER >= 0x10000000 static void threadid_handler(CRYPTO_THREADID *id) { CRYPTO_THREADID_set_numeric(id, threadid()); } #else static unsigned long threadid_handler(void) { return threadid(); } #endif static void locking_handler(int mode, int type, const char *file, int line) { (void)file; (void)line; if (mode & CRYPTO_LOCK) (void)pthread_mutex_lock(&lockv[type]); else (void)pthread_mutex_unlock(&lockv[type]); } #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L static struct CRYPTO_dynlock_value *dynlock_create_handler(const char *file, int line) { struct lock *lock; (void)file; (void)line; if (lock_alloc(&lock)) return NULL; return (struct CRYPTO_dynlock_value *)lock; } static void dynlock_lock_handler(int mode, struct CRYPTO_dynlock_value *l, const char *file, int line) { struct lock *lock = (struct lock *)l; (void)file; (void)line; if (mode & CRYPTO_LOCK) lock_write_get(lock); else lock_rel(lock); } static void dynlock_destroy_handler(struct CRYPTO_dynlock_value *l, const char *file, int line) { (void)file; (void)line; mem_deref(l); } #endif #ifdef SIGPIPE static void sigpipe_handler(int x) { (void)x; (void)signal(SIGPIPE, sigpipe_handler); } #endif int openssl_init(void) { #if defined (HAVE_PTHREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L) int err, i; lockv = mem_zalloc(sizeof(pthread_mutex_t) * CRYPTO_num_locks(), NULL); if (!lockv) return ENOMEM; for (i=0; i= 0x10000000 CRYPTO_THREADID_set_callback(threadid_handler); #else CRYPTO_set_id_callback(threadid_handler); #endif CRYPTO_set_locking_callback(locking_handler); #endif #if OPENSSL_VERSION_NUMBER < 0x10100000L CRYPTO_set_dynlock_create_callback(dynlock_create_handler); CRYPTO_set_dynlock_lock_callback(dynlock_lock_handler); CRYPTO_set_dynlock_destroy_callback(dynlock_destroy_handler); #endif #ifdef SIGPIPE (void)signal(SIGPIPE, sigpipe_handler); #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L int err; err = OPENSSL_init_ssl(OPENSSL_INIT_SSL_DEFAULT, NULL); if (!err) return !err; #else SSL_library_init(); SSL_load_error_strings(); #endif return 0; } void openssl_close(void) { #if OPENSSL_VERSION_NUMBER < 0x10100000L ERR_free_strings(); #endif #if defined (HAVE_PTHREAD) && (OPENSSL_VERSION_NUMBER < 0x10100000L) lockv = mem_deref(lockv); #endif } re-1.1.0/src/mbuf/000077500000000000000000000000001373627245400136545ustar00rootroot00000000000000re-1.1.0/src/mbuf/mbuf.c000066400000000000000000000240741373627245400147600ustar00rootroot00000000000000/** * @file mbuf.c Memory buffers * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #define DEBUG_MODULE "mbuf" #define DEBUG_LEVEL 4 #include enum {DEFAULT_SIZE=512}; static void mbuf_destructor(void *data) { struct mbuf *mb = data; mem_deref(mb->buf); } /** * Allocate a new memory buffer * * @param size Initial buffer size * * @return New memory buffer, NULL if no memory */ struct mbuf *mbuf_alloc(size_t size) { struct mbuf *mb; mb = mem_zalloc(sizeof(*mb), mbuf_destructor); if (!mb) return NULL; if (mbuf_resize(mb, size ? size : DEFAULT_SIZE)) return mem_deref(mb); return mb; } /** * Allocate a new memory buffer with a reference to another mbuf * * @param mbr Memory buffer to reference * * @return New memory buffer, NULL if no memory */ struct mbuf *mbuf_alloc_ref(struct mbuf *mbr) { struct mbuf *mb; if (!mbr) return NULL; mb = mem_zalloc(sizeof(*mb), mbuf_destructor); if (!mb) return NULL; mb->buf = mem_ref(mbr->buf); mb->size = mbr->size; mb->pos = mbr->pos; mb->end = mbr->end; return mb; } /** * Initialize a memory buffer * * @param mb Memory buffer to initialize */ void mbuf_init(struct mbuf *mb) { if (!mb) return; mb->buf = NULL; mb->size = 0; mb->pos = 0; mb->end = 0; } /** * Reset a memory buffer * * @param mb Memory buffer to reset */ void mbuf_reset(struct mbuf *mb) { if (!mb) return; mb->buf = mem_deref(mb->buf); mbuf_init(mb); } /** * Resize a memory buffer * * @param mb Memory buffer to resize * @param size New buffer size * * @return 0 if success, otherwise errorcode */ int mbuf_resize(struct mbuf *mb, size_t size) { uint8_t *buf; if (!mb) return EINVAL; buf = mb->buf ? mem_realloc(mb->buf, size) : mem_alloc(size, NULL); if (!buf) return ENOMEM; mb->buf = buf; mb->size = size; return 0; } /** * Trim unused trailing bytes in a memory buffer, resize if necessary * * @param mb Memory buffer to trim */ void mbuf_trim(struct mbuf *mb) { int err; if (!mb || !mb->end || mb->end == mb->size) return; /* We shrink - this cannot fail */ err = mbuf_resize(mb, mb->end); if (err) { DEBUG_WARNING("trim: resize failed (%m)\n", err); } } /** * Shift mbuf content position * * @param mb Memory buffer to shift * @param shift Shift offset count * * @return 0 if success, otherwise errorcode */ int mbuf_shift(struct mbuf *mb, ssize_t shift) { size_t rsize; uint8_t *p; if (!mb) return EINVAL; if (((ssize_t)mb->pos + shift) < 0 || ((ssize_t)mb->end + shift) < 0) return ERANGE; rsize = mb->end + shift; if (rsize > mb->size) { int err; err = mbuf_resize(mb, rsize); if (err) return err; } p = mbuf_buf(mb); memmove(p + shift, p, mbuf_get_left(mb)); mb->pos += shift; mb->end += shift; return 0; } /** * Write a block of memory to a memory buffer * * @param mb Memory buffer * @param buf Memory block to write * @param size Number of bytes to write * * @return 0 if success, otherwise errorcode */ int mbuf_write_mem(struct mbuf *mb, const uint8_t *buf, size_t size) { size_t rsize; if (!mb || !buf) return EINVAL; rsize = mb->pos + size; if (rsize > mb->size) { const size_t dsize = mb->size ? (mb->size * 2) : DEFAULT_SIZE; int err; err = mbuf_resize(mb, MAX(rsize, dsize)); if (err) return err; } memcpy(mb->buf + mb->pos, buf, size); mb->pos += size; mb->end = MAX(mb->end, mb->pos); return 0; } /** * Write an 8-bit value to a memory buffer * * @param mb Memory buffer * @param v 8-bit value to write * * @return 0 if success, otherwise errorcode */ int mbuf_write_u8(struct mbuf *mb, uint8_t v) { return mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v)); } /** * Write a 16-bit value to a memory buffer * * @param mb Memory buffer * @param v 16-bit value to write * * @return 0 if success, otherwise errorcode */ int mbuf_write_u16(struct mbuf *mb, uint16_t v) { return mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v)); } /** * Write a 32-bit value to a memory buffer * * @param mb Memory buffer * @param v 32-bit value to write * * @return 0 if success, otherwise errorcode */ int mbuf_write_u32(struct mbuf *mb, uint32_t v) { return mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v)); } /** * Write a 64-bit value to a memory buffer * * @param mb Memory buffer * @param v 64-bit value to write * * @return 0 if success, otherwise errorcode */ int mbuf_write_u64(struct mbuf *mb, uint64_t v) { return mbuf_write_mem(mb, (uint8_t *)&v, sizeof(v)); } /** * Write a null-terminated string to a memory buffer * * @param mb Memory buffer * @param str Null terminated string to write * * @return 0 if success, otherwise errorcode */ int mbuf_write_str(struct mbuf *mb, const char *str) { if (!str) return EINVAL; return mbuf_write_mem(mb, (const uint8_t *)str, strlen(str)); } /** * Write a pointer-length string to a memory buffer * * @param mb Memory buffer * @param pl Pointer-length string * * @return 0 if success, otherwise errorcode */ int mbuf_write_pl(struct mbuf *mb, const struct pl *pl) { if (!pl) return EINVAL; return mbuf_write_mem(mb, (const uint8_t *)pl->p, pl->l); } /** * Read a block of memory from a memory buffer * * @param mb Memory buffer * @param buf Buffer to read data to * @param size Size of buffer * * @return 0 if success, otherwise errorcode */ int mbuf_read_mem(struct mbuf *mb, uint8_t *buf, size_t size) { if (!mb || !buf) return EINVAL; if (size > mbuf_get_left(mb)) { DEBUG_WARNING("tried to read beyond mbuf end (%u > %u)\n", size, mbuf_get_left(mb)); return EOVERFLOW; } memcpy(buf, mb->buf + mb->pos, size); mb->pos += size; return 0; } /** * Read an 8-bit value from a memory buffer * * @param mb Memory buffer * * @return 8-bit value */ uint8_t mbuf_read_u8(struct mbuf *mb) { uint8_t v; return (0 == mbuf_read_mem(mb, &v, sizeof(v))) ? v : 0; } /** * Read a 16-bit value from a memory buffer * * @param mb Memory buffer * * @return 16-bit value */ uint16_t mbuf_read_u16(struct mbuf *mb) { uint16_t v; return (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0; } /** * Read a 32-bit value from a memory buffer * * @param mb Memory buffer * * @return 32-bit value */ uint32_t mbuf_read_u32(struct mbuf *mb) { uint32_t v; return (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0; } /** * Read a 64-bit value from a memory buffer * * @param mb Memory buffer * * @return 64-bit value */ uint64_t mbuf_read_u64(struct mbuf *mb) { uint64_t v; return (0 == mbuf_read_mem(mb, (uint8_t *)&v, sizeof(v))) ? v : 0; } /** * Read a string from a memory buffer * * @param mb Memory buffer * @param str Buffer to read string to * @param size Size of buffer * * @return 0 if success, otherwise errorcode */ int mbuf_read_str(struct mbuf *mb, char *str, size_t size) { if (!mb || !str) return EINVAL; while (size--) { const uint8_t c = mbuf_read_u8(mb); *str++ = c; if ('\0' == c) break; } return 0; } /** * Duplicate a null-terminated string from a memory buffer * * @param mb Memory buffer * @param strp Pointer to destination string; allocated and set * @param len Length of string * * @return 0 if success, otherwise errorcode */ int mbuf_strdup(struct mbuf *mb, char **strp, size_t len) { char *str; int err; if (!mb || !strp) return EINVAL; str = mem_alloc(len + 1, NULL); if (!str) return ENOMEM; err = mbuf_read_mem(mb, (uint8_t *)str, len); if (err) goto out; str[len] = '\0'; out: if (err) mem_deref(str); else *strp = str; return err; } static int vprintf_handler(const char *p, size_t size, void *arg) { struct mbuf *mb = arg; return mbuf_write_mem(mb, (const uint8_t *)p, size); } /** * Print a formatted variable argument list to a memory buffer * * @param mb Memory buffer * @param fmt Formatted string * @param ap Variable argument list * * @return 0 if success, otherwise errorcode */ int mbuf_vprintf(struct mbuf *mb, const char *fmt, va_list ap) { return re_vhprintf(fmt, ap, vprintf_handler, mb); } /** * Print a formatted string to a memory buffer * * @param mb Memory buffer * @param fmt Formatted string * * @return 0 if success, otherwise errorcode */ int mbuf_printf(struct mbuf *mb, const char *fmt, ...) { int err = 0; va_list ap; va_start(ap, fmt); err = re_vhprintf(fmt, ap, vprintf_handler, mb); va_end(ap); return err; } /** * Write a pointer-length string to a memory buffer, excluding a section * * @param mb Memory buffer * @param pl Pointer-length string * @param skip Part of pl to exclude * * @return 0 if success, otherwise errorcode * * @todo: create substf variante */ int mbuf_write_pl_skip(struct mbuf *mb, const struct pl *pl, const struct pl *skip) { struct pl r; int err; if (!pl || !skip) return EINVAL; if (pl->p > skip->p || (skip->p + skip->l) > (pl->p + pl->l)) return ERANGE; r.p = pl->p; r.l = skip->p - pl->p; err = mbuf_write_mem(mb, (const uint8_t *)r.p, r.l); if (err) return err; r.p = skip->p + skip->l; r.l = pl->p + pl->l - r.p; return mbuf_write_mem(mb, (const uint8_t *)r.p, r.l); } /** * Write n bytes of value 'c' to a memory buffer * * @param mb Memory buffer * @param c Value to write * @param n Number of bytes to write * * @return 0 if success, otherwise errorcode */ int mbuf_fill(struct mbuf *mb, uint8_t c, size_t n) { size_t rsize; if (!mb || !n) return EINVAL; rsize = mb->pos + n; if (rsize > mb->size) { const size_t dsize = mb->size ? (mb->size * 2) : DEFAULT_SIZE; int err; err = mbuf_resize(mb, MAX(rsize, dsize)); if (err) return err; } memset(mb->buf + mb->pos, c, n); mb->pos += n; mb->end = MAX(mb->end, mb->pos); return 0; } /** * Debug the memory buffer * * @param pf Print handler * @param mb Memory buffer * * @return 0 if success, otherwise errorcode */ int mbuf_debug(struct re_printf *pf, const struct mbuf *mb) { if (!mb) return 0; return re_hprintf(pf, "buf=%p pos=%zu end=%zu size=%zu", mb->buf, mb->pos, mb->end, mb->size); } re-1.1.0/src/mbuf/mod.mk000066400000000000000000000001051373627245400147600ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += mbuf/mbuf.c re-1.1.0/src/md5/000077500000000000000000000000001373627245400134105ustar00rootroot00000000000000re-1.1.0/src/md5/md5.c000066400000000000000000000275451373627245400142560ustar00rootroot00000000000000/* Copyright (C) 1999, 2000, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.c,v 1.6 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.c is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Clarified derivation from RFC 1321; now handles byte order either statically or dynamically; added missing #include in library. 2002-03-11 lpd Corrected argument list for main(), and added int return type, in test program and T value program. 2002-02-21 lpd Added missing #include in test program. 2000-07-03 lpd Patched to eliminate warnings about "constant is unsigned in ANSI C, signed in traditional"; made test program self-checking. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5). 1999-05-03 lpd Original version. */ #include "md5.h" #include #undef BYTE_ORDER /* 1 = big-endian, -1 = little-endian, 0 = unknown */ #ifdef ARCH_IS_BIG_ENDIAN # define BYTE_ORDER (ARCH_IS_BIG_ENDIAN ? 1 : -1) #else # define BYTE_ORDER 0 #endif #define T_MASK ((md5_word_t)~0) #define T1 /* 0xd76aa478 */ (T_MASK ^ 0x28955b87) #define T2 /* 0xe8c7b756 */ (T_MASK ^ 0x173848a9) #define T3 0x242070db #define T4 /* 0xc1bdceee */ (T_MASK ^ 0x3e423111) #define T5 /* 0xf57c0faf */ (T_MASK ^ 0x0a83f050) #define T6 0x4787c62a #define T7 /* 0xa8304613 */ (T_MASK ^ 0x57cfb9ec) #define T8 /* 0xfd469501 */ (T_MASK ^ 0x02b96afe) #define T9 0x698098d8 #define T10 /* 0x8b44f7af */ (T_MASK ^ 0x74bb0850) #define T11 /* 0xffff5bb1 */ (T_MASK ^ 0x0000a44e) #define T12 /* 0x895cd7be */ (T_MASK ^ 0x76a32841) #define T13 0x6b901122 #define T14 /* 0xfd987193 */ (T_MASK ^ 0x02678e6c) #define T15 /* 0xa679438e */ (T_MASK ^ 0x5986bc71) #define T16 0x49b40821 #define T17 /* 0xf61e2562 */ (T_MASK ^ 0x09e1da9d) #define T18 /* 0xc040b340 */ (T_MASK ^ 0x3fbf4cbf) #define T19 0x265e5a51 #define T20 /* 0xe9b6c7aa */ (T_MASK ^ 0x16493855) #define T21 /* 0xd62f105d */ (T_MASK ^ 0x29d0efa2) #define T22 0x02441453 #define T23 /* 0xd8a1e681 */ (T_MASK ^ 0x275e197e) #define T24 /* 0xe7d3fbc8 */ (T_MASK ^ 0x182c0437) #define T25 0x21e1cde6 #define T26 /* 0xc33707d6 */ (T_MASK ^ 0x3cc8f829) #define T27 /* 0xf4d50d87 */ (T_MASK ^ 0x0b2af278) #define T28 0x455a14ed #define T29 /* 0xa9e3e905 */ (T_MASK ^ 0x561c16fa) #define T30 /* 0xfcefa3f8 */ (T_MASK ^ 0x03105c07) #define T31 0x676f02d9 #define T32 /* 0x8d2a4c8a */ (T_MASK ^ 0x72d5b375) #define T33 /* 0xfffa3942 */ (T_MASK ^ 0x0005c6bd) #define T34 /* 0x8771f681 */ (T_MASK ^ 0x788e097e) #define T35 0x6d9d6122 #define T36 /* 0xfde5380c */ (T_MASK ^ 0x021ac7f3) #define T37 /* 0xa4beea44 */ (T_MASK ^ 0x5b4115bb) #define T38 0x4bdecfa9 #define T39 /* 0xf6bb4b60 */ (T_MASK ^ 0x0944b49f) #define T40 /* 0xbebfbc70 */ (T_MASK ^ 0x4140438f) #define T41 0x289b7ec6 #define T42 /* 0xeaa127fa */ (T_MASK ^ 0x155ed805) #define T43 /* 0xd4ef3085 */ (T_MASK ^ 0x2b10cf7a) #define T44 0x04881d05 #define T45 /* 0xd9d4d039 */ (T_MASK ^ 0x262b2fc6) #define T46 /* 0xe6db99e5 */ (T_MASK ^ 0x1924661a) #define T47 0x1fa27cf8 #define T48 /* 0xc4ac5665 */ (T_MASK ^ 0x3b53a99a) #define T49 /* 0xf4292244 */ (T_MASK ^ 0x0bd6ddbb) #define T50 0x432aff97 #define T51 /* 0xab9423a7 */ (T_MASK ^ 0x546bdc58) #define T52 /* 0xfc93a039 */ (T_MASK ^ 0x036c5fc6) #define T53 0x655b59c3 #define T54 /* 0x8f0ccc92 */ (T_MASK ^ 0x70f3336d) #define T55 /* 0xffeff47d */ (T_MASK ^ 0x00100b82) #define T56 /* 0x85845dd1 */ (T_MASK ^ 0x7a7ba22e) #define T57 0x6fa87e4f #define T58 /* 0xfe2ce6e0 */ (T_MASK ^ 0x01d3191f) #define T59 /* 0xa3014314 */ (T_MASK ^ 0x5cfebceb) #define T60 0x4e0811a1 #define T61 /* 0xf7537e82 */ (T_MASK ^ 0x08ac817d) #define T62 /* 0xbd3af235 */ (T_MASK ^ 0x42c50dca) #define T63 0x2ad7d2bb #define T64 /* 0xeb86d391 */ (T_MASK ^ 0x14792c6e) static void md5_process(md5_state_t *pms, const md5_byte_t *data /*[64]*/) { md5_word_t a = pms->abcd[0], b = pms->abcd[1], c = pms->abcd[2], d = pms->abcd[3]; md5_word_t t; #if BYTE_ORDER > 0 /* Define storage only for big-endian CPUs. */ md5_word_t X[16]; #else /* Define storage for little-endian or both types of CPUs. */ md5_word_t xbuf[16]; const md5_word_t *X; #endif { #if BYTE_ORDER == 0 /* * Determine dynamically whether this is a big-endian or * little-endian machine, since we can use a more efficient * algorithm on the latter. */ static const int w = 1; if (*((const md5_byte_t *)&w)) /* dynamic little-endian */ #endif #if BYTE_ORDER <= 0 /* little-endian */ { /* * On little-endian machines, we can process * properly aligned data without copying it. */ if (!((data - (const md5_byte_t *)0) & 3)) { /* data are properly aligned */ X = (const md5_word_t *)(void *)data; } else { /* not aligned */ memcpy(xbuf, data, 64); X = xbuf; } } #endif #if BYTE_ORDER == 0 else /* dynamic big-endian */ #endif #if BYTE_ORDER >= 0 /* big-endian */ { /* * On big-endian machines, we must arrange * the bytes in the right order. */ const md5_byte_t *xp = data; int i; # if BYTE_ORDER == 0 X = xbuf; /* (dynamic only) */ # else # define xbuf X /* (static only) */ # endif for (i = 0; i < 16; ++i, xp += 4) xbuf[i] = xp[0] + (xp[1] << 8) + (xp[2] << 16) + (xp[3] << 24); } #endif } #define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32 - (n)))) /* Round 1. */ /* Let [abcd k s i] denote the operation a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s). */ #define F(x, y, z) (((x) & (y)) | (~(x) & (z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + F(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 7, T1); SET(d, a, b, c, 1, 12, T2); SET(c, d, a, b, 2, 17, T3); SET(b, c, d, a, 3, 22, T4); SET(a, b, c, d, 4, 7, T5); SET(d, a, b, c, 5, 12, T6); SET(c, d, a, b, 6, 17, T7); SET(b, c, d, a, 7, 22, T8); SET(a, b, c, d, 8, 7, T9); SET(d, a, b, c, 9, 12, T10); SET(c, d, a, b, 10, 17, T11); SET(b, c, d, a, 11, 22, T12); SET(a, b, c, d, 12, 7, T13); SET(d, a, b, c, 13, 12, T14); SET(c, d, a, b, 14, 17, T15); SET(b, c, d, a, 15, 22, T16); #undef SET /* Round 2. */ /* Let [abcd k s i] denote the operation a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s). */ #define G(x, y, z) (((x) & (z)) | ((y) & ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + G(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 1, 5, T17); SET(d, a, b, c, 6, 9, T18); SET(c, d, a, b, 11, 14, T19); SET(b, c, d, a, 0, 20, T20); SET(a, b, c, d, 5, 5, T21); SET(d, a, b, c, 10, 9, T22); SET(c, d, a, b, 15, 14, T23); SET(b, c, d, a, 4, 20, T24); SET(a, b, c, d, 9, 5, T25); SET(d, a, b, c, 14, 9, T26); SET(c, d, a, b, 3, 14, T27); SET(b, c, d, a, 8, 20, T28); SET(a, b, c, d, 13, 5, T29); SET(d, a, b, c, 2, 9, T30); SET(c, d, a, b, 7, 14, T31); SET(b, c, d, a, 12, 20, T32); #undef SET /* Round 3. */ /* Let [abcd k s t] denote the operation a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s). */ #define H(x, y, z) ((x) ^ (y) ^ (z)) #define SET(a, b, c, d, k, s, Ti)\ t = a + H(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 5, 4, T33); SET(d, a, b, c, 8, 11, T34); SET(c, d, a, b, 11, 16, T35); SET(b, c, d, a, 14, 23, T36); SET(a, b, c, d, 1, 4, T37); SET(d, a, b, c, 4, 11, T38); SET(c, d, a, b, 7, 16, T39); SET(b, c, d, a, 10, 23, T40); SET(a, b, c, d, 13, 4, T41); SET(d, a, b, c, 0, 11, T42); SET(c, d, a, b, 3, 16, T43); SET(b, c, d, a, 6, 23, T44); SET(a, b, c, d, 9, 4, T45); SET(d, a, b, c, 12, 11, T46); SET(c, d, a, b, 15, 16, T47); SET(b, c, d, a, 2, 23, T48); #undef SET /* Round 4. */ /* Let [abcd k s t] denote the operation a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s). */ #define I(x, y, z) ((y) ^ ((x) | ~(z))) #define SET(a, b, c, d, k, s, Ti)\ t = a + I(b,c,d) + X[k] + Ti; \ a = ROTATE_LEFT(t, s) + b /* Do the following 16 operations. */ SET(a, b, c, d, 0, 6, T49); SET(d, a, b, c, 7, 10, T50); SET(c, d, a, b, 14, 15, T51); SET(b, c, d, a, 5, 21, T52); SET(a, b, c, d, 12, 6, T53); SET(d, a, b, c, 3, 10, T54); SET(c, d, a, b, 10, 15, T55); SET(b, c, d, a, 1, 21, T56); SET(a, b, c, d, 8, 6, T57); SET(d, a, b, c, 15, 10, T58); SET(c, d, a, b, 6, 15, T59); SET(b, c, d, a, 13, 21, T60); SET(a, b, c, d, 4, 6, T61); SET(d, a, b, c, 11, 10, T62); SET(c, d, a, b, 2, 15, T63); SET(b, c, d, a, 9, 21, T64); #undef SET /* Then perform the following additions. (That is increment each of the four registers by the value it had before this block was started.) */ pms->abcd[0] += a; pms->abcd[1] += b; pms->abcd[2] += c; pms->abcd[3] += d; } void md5_init(md5_state_t *pms) { pms->count[0] = pms->count[1] = 0; pms->abcd[0] = 0x67452301; pms->abcd[1] = /*0xefcdab89*/ T_MASK ^ 0x10325476; pms->abcd[2] = /*0x98badcfe*/ T_MASK ^ 0x67452301; pms->abcd[3] = 0x10325476; } void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes) { const md5_byte_t *p = data; int left = nbytes; int offset = (pms->count[0] >> 3) & 63; md5_word_t nbits = (md5_word_t)(nbytes << 3); if (nbytes <= 0) return; /* Update the message length. */ pms->count[1] += nbytes >> 29; pms->count[0] += nbits; if (pms->count[0] < nbits) pms->count[1]++; /* Process an initial partial block. */ if (offset) { int copy = (offset + nbytes > 64 ? 64 - offset : nbytes); memcpy(pms->buf + offset, p, copy); if (offset + copy < 64) return; p += copy; left -= copy; md5_process(pms, pms->buf); } /* Process full blocks. */ for (; left >= 64; p += 64, left -= 64) md5_process(pms, p); /* Process a final partial block. */ if (left) memcpy(pms->buf, p, left); } void md5_finish(md5_state_t *pms, md5_byte_t digest[16]) { static const md5_byte_t pad[64] = { 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; md5_byte_t data[8]; int i; /* Save the length before padding. */ for (i = 0; i < 8; ++i) data[i] = (md5_byte_t)(pms->count[i >> 2] >> ((i & 3) << 3)); /* Pad to 56 bytes mod 64. */ md5_append(pms, pad, ((55 - (pms->count[0] >> 3)) & 63) + 1); /* Append the length. */ md5_append(pms, data, 8); for (i = 0; i < 16; ++i) digest[i] = (md5_byte_t)(pms->abcd[i >> 2] >> ((i & 3) << 3)); } re-1.1.0/src/md5/md5.h000066400000000000000000000064661373627245400142620ustar00rootroot00000000000000/* Copyright (C) 1999, 2002 Aladdin Enterprises. All rights reserved. This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. L. Peter Deutsch ghost@aladdin.com */ /* $Id: md5.h,v 1.4 2002/04/13 19:20:28 lpd Exp $ */ /* Independent implementation of MD5 (RFC 1321). This code implements the MD5 Algorithm defined in RFC 1321, whose text is available at http://www.ietf.org/rfc/rfc1321.txt The code is derived from the text of the RFC, including the test suite (section A.5) but excluding the rest of Appendix A. It does not include any code or documentation that is identified in the RFC as being copyrighted. The original and principal author of md5.h is L. Peter Deutsch . Other authors are noted in the change history that follows (in reverse chronological order): 2002-04-13 lpd Removed support for non-ANSI compilers; removed references to Ghostscript; clarified derivation from RFC 1321; now handles byte order either statically or dynamically. 1999-11-04 lpd Edited comments slightly for automatic TOC extraction. 1999-10-18 lpd Fixed typo in header comment (ansi2knr rather than md5); added conditionalization for C++ compilation from Martin Purschke . 1999-05-03 lpd Original version. */ #ifndef md5_INCLUDED # define md5_INCLUDED /* * This package supports both compile-time and run-time determination of CPU * byte order. If ARCH_IS_BIG_ENDIAN is defined as 0, the code will be * compiled to run only on little-endian CPUs; if ARCH_IS_BIG_ENDIAN is * defined as non-zero, the code will be compiled to run only on big-endian * CPUs; if ARCH_IS_BIG_ENDIAN is not defined, the code will be compiled to * run on either big- or little-endian CPUs, but will run slightly less * efficiently on either one than if ARCH_IS_BIG_ENDIAN is defined. */ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { md5_word_t count[2]; /* message length in bits, lsw first */ md5_word_t abcd[4]; /* digest buffer */ md5_byte_t buf[64]; /* accumulate block */ } md5_state_t; #ifdef __cplusplus extern "C" { #endif /* Initialize the algorithm. */ void md5_init(md5_state_t *pms); /* Append a string to the message. */ void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); #ifdef __cplusplus } /* end extern "C" */ #endif #endif /* md5_INCLUDED */ re-1.1.0/src/md5/mod.mk000066400000000000000000000001641373627245400145210ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # ifeq ($(USE_OPENSSL),) SRCS += md5/md5.c endif SRCS += md5/wrap.c re-1.1.0/src/md5/wrap.c000066400000000000000000000020551373627245400145270ustar00rootroot00000000000000/** * @file wrap.c MD5 wrappers * * Copyright (C) 2010 Creytiv.com */ #ifdef USE_OPENSSL #include #include #else #include "md5.h" #endif #include #include #include #include #include /** * Calculate the MD5 hash from a buffer * * @param d Data buffer (input) * @param n Number of input bytes * @param md Calculated MD5 hash (output) */ void md5(const uint8_t *d, size_t n, uint8_t *md) { #ifdef USE_OPENSSL (void)MD5(d, n, md); #else md5_state_t state; md5_init(&state); md5_append(&state, d, (int)n); md5_finish(&state, md); #endif } /** * Calculate the MD5 hash from a formatted string * * @param md Calculated MD5 hash * @param fmt Formatted string * * @return 0 if success, otherwise errorcode */ int md5_printf(uint8_t *md, const char *fmt, ...) { struct mbuf mb; va_list ap; int err; mbuf_init(&mb); va_start(ap, fmt); err = mbuf_vprintf(&mb, fmt, ap); va_end(ap); if (!err) md5(mb.buf, mb.end, md); mbuf_reset(&mb); return err; } re-1.1.0/src/mem/000077500000000000000000000000001373627245400135015ustar00rootroot00000000000000re-1.1.0/src/mem/mem.c000066400000000000000000000220121373627245400144200ustar00rootroot00000000000000/** * @file mem.c Memory management with reference counting * * Copyright (C) 2010 Creytiv.com */ #include #include #include #ifdef HAVE_PTHREAD #include #endif #include #include #include #include #include #define DEBUG_MODULE "mem" #define DEBUG_LEVEL 5 #include #ifndef RELEASE #define MEM_DEBUG 1 /**< Enable memory debugging */ #endif /** Defines a reference-counting memory object */ struct mem { uint32_t nrefs; /**< Number of references */ mem_destroy_h *dh; /**< Destroy handler */ #if MEM_DEBUG struct le le; /**< Linked list element */ uint32_t magic; /**< Magic number */ size_t size; /**< Size of memory object */ #endif }; #if MEM_DEBUG /* Memory debugging */ static struct list meml = LIST_INIT; static const uint32_t mem_magic = 0xe7fb9ac4; static ssize_t threshold = -1; /**< Memory threshold, disabled by default */ static struct memstat memstat = { 0,0,0,0,~0,0 }; #ifdef HAVE_PTHREAD static pthread_mutex_t mem_mutex = PTHREAD_MUTEX_INITIALIZER; static inline void mem_lock(void) { pthread_mutex_lock(&mem_mutex); } static inline void mem_unlock(void) { pthread_mutex_unlock(&mem_mutex); } #else #define mem_lock() /**< Stub */ #define mem_unlock() /**< Stub */ #endif /** Update statistics for mem_zalloc() */ #define STAT_ALLOC(m, size) \ mem_lock(); \ memstat.bytes_cur += (size); \ memstat.bytes_peak = max(memstat.bytes_cur, memstat.bytes_peak); \ ++memstat.blocks_cur; \ memstat.blocks_peak = max(memstat.blocks_cur, memstat.blocks_peak); \ memstat.size_min = min(memstat.size_min, size); \ memstat.size_max = max(memstat.size_max, size); \ mem_unlock(); \ (m)->size = (size); \ (m)->magic = mem_magic; /** Update statistics for mem_realloc() */ #define STAT_REALLOC(m, size) \ mem_lock(); \ memstat.bytes_cur += ((size) - (m)->size); \ memstat.bytes_peak = max(memstat.bytes_cur, memstat.bytes_peak); \ memstat.size_min = min(memstat.size_min, size); \ memstat.size_max = max(memstat.size_max, size); \ mem_unlock(); \ (m)->size = (size) /** Update statistics for mem_deref() */ #define STAT_DEREF(m) \ mem_lock(); \ memstat.bytes_cur -= (m)->size; \ --memstat.blocks_cur; \ mem_unlock(); \ memset((m), 0xb5, sizeof(struct mem) + (m)->size) /** Check magic number in memory object */ #define MAGIC_CHECK(m) \ if (mem_magic != (m)->magic) { \ DEBUG_WARNING("%s: magic check failed 0x%08x (%p)\n", \ __REFUNC__, (m)->magic, (m)+1); \ BREAKPOINT; \ } #else #define STAT_ALLOC(m, size) #define STAT_REALLOC(m, size) #define STAT_DEREF(m) #define MAGIC_CHECK(m) #endif /** * Allocate a new reference-counted memory object * * @param size Size of memory object * @param dh Optional destructor, called when destroyed * * @return Pointer to allocated object */ void *mem_alloc(size_t size, mem_destroy_h *dh) { struct mem *m; #if MEM_DEBUG mem_lock(); if (-1 != threshold && (memstat.blocks_cur >= (size_t)threshold)) { mem_unlock(); return NULL; } mem_unlock(); #endif m = malloc(sizeof(*m) + size); if (!m) return NULL; #if MEM_DEBUG memset(&m->le, 0, sizeof(struct le)); mem_lock(); list_append(&meml, &m->le, m); mem_unlock(); #endif m->nrefs = 1; m->dh = dh; STAT_ALLOC(m, size); return (void *)(m + 1); } /** * Allocate a new reference-counted memory object. Memory is zeroed. * * @param size Size of memory object * @param dh Optional destructor, called when destroyed * * @return Pointer to allocated object */ void *mem_zalloc(size_t size, mem_destroy_h *dh) { void *p; p = mem_alloc(size, dh); if (!p) return NULL; memset(p, 0, size); return p; } /** * Re-allocate a reference-counted memory object * * @param data Memory object * @param size New size of memory object * * @return New pointer to allocated object * * @note Realloc NULL pointer is not supported */ void *mem_realloc(void *data, size_t size) { struct mem *m, *m2; if (!data) return NULL; m = ((struct mem *)data) - 1; MAGIC_CHECK(m); #if MEM_DEBUG mem_lock(); /* Simulate OOM */ if (-1 != threshold && size > m->size) { if (memstat.blocks_cur >= (size_t)threshold) { mem_unlock(); return NULL; } } list_unlink(&m->le); mem_unlock(); #endif m2 = realloc(m, sizeof(*m2) + size); #if MEM_DEBUG mem_lock(); list_append(&meml, m2 ? &m2->le : &m->le, m2 ? m2 : m); mem_unlock(); #endif if (!m2) { return NULL; } STAT_REALLOC(m2, size); return (void *)(m2 + 1); } #ifndef SIZE_MAX #define SIZE_MAX (~((size_t)0)) #endif /** * Re-allocate a reference-counted array * * @param ptr Pointer to existing array, NULL to allocate a new array * @param nmemb Number of members in array * @param membsize Number of bytes in each member * @param dh Optional destructor, only used when ptr is NULL * * @return New pointer to allocated array */ void *mem_reallocarray(void *ptr, size_t nmemb, size_t membsize, mem_destroy_h *dh) { size_t tsize; if (membsize && nmemb > SIZE_MAX / membsize) { return NULL; } tsize = nmemb * membsize; if (ptr) { return mem_realloc(ptr, tsize); } else { return mem_alloc(tsize, dh); } } /** * Reference a reference-counted memory object * * @param data Memory object * * @return Memory object (same as data) */ void *mem_ref(void *data) { struct mem *m; if (!data) return NULL; m = ((struct mem *)data) - 1; MAGIC_CHECK(m); ++m->nrefs; return data; } /** * Dereference a reference-counted memory object. When the reference count * is zero, the destroy handler will be called (if present) and the memory * will be freed * * @param data Memory object * * @return Always NULL */ void *mem_deref(void *data) { struct mem *m; if (!data) return NULL; m = ((struct mem *)data) - 1; MAGIC_CHECK(m); if (--m->nrefs > 0) return NULL; if (m->dh) m->dh(data); /* NOTE: check if the destructor called mem_ref() */ if (m->nrefs > 0) return NULL; #if MEM_DEBUG mem_lock(); list_unlink(&m->le); mem_unlock(); #endif STAT_DEREF(m); free(m); return NULL; } /** * Get number of references to a reference-counted memory object * * @param data Memory object * * @return Number of references */ uint32_t mem_nrefs(const void *data) { struct mem *m; if (!data) return 0; m = ((struct mem *)data) - 1; MAGIC_CHECK(m); return m->nrefs; } #if MEM_DEBUG static bool debug_handler(struct le *le, void *arg) { struct mem *m = le->data; const uint8_t *p = (const uint8_t *)(m + 1); size_t i; (void)arg; (void)re_fprintf(stderr, " %p: nrefs=%-2u", p, m->nrefs); (void)re_fprintf(stderr, " size=%-7u", m->size); (void)re_fprintf(stderr, " ["); for (i=0; i<16; i++) { if (i >= m->size) (void)re_fprintf(stderr, " "); else (void)re_fprintf(stderr, "%02x ", p[i]); } (void)re_fprintf(stderr, "] ["); for (i=0; i<16; i++) { if (i >= m->size) (void)re_fprintf(stderr, " "); else (void)re_fprintf(stderr, "%c", isprint(p[i]) ? p[i] : '.'); } (void)re_fprintf(stderr, "]"); MAGIC_CHECK(m); (void)re_fprintf(stderr, "\n"); return false; } #endif /** * Debug all allocated memory objects */ void mem_debug(void) { #if MEM_DEBUG uint32_t n; mem_lock(); n = list_count(&meml); mem_unlock(); if (!n) return; DEBUG_WARNING("Memory leaks (%u):\n", n); mem_lock(); (void)list_apply(&meml, true, debug_handler, NULL); mem_unlock(); #endif } /** * Set the memory allocation threshold. This is only used for debugging * and out-of-memory simulation * * @param n Threshold value */ void mem_threshold_set(ssize_t n) { #if MEM_DEBUG mem_lock(); threshold = n; mem_unlock(); #else (void)n; #endif } /** * Print memory status * * @param pf Print handler for debug output * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int mem_status(struct re_printf *pf, void *unused) { #if MEM_DEBUG struct memstat stat; uint32_t c; int err = 0; (void)unused; mem_lock(); memcpy(&stat, &memstat, sizeof(stat)); c = list_count(&meml); mem_unlock(); err |= re_hprintf(pf, "Memory status: (%u bytes overhead pr block)\n", sizeof(struct mem)); err |= re_hprintf(pf, " Cur: %u blocks, %u bytes (total %u bytes)\n", stat.blocks_cur, stat.bytes_cur, stat.bytes_cur +(stat.blocks_cur*sizeof(struct mem))); err |= re_hprintf(pf, " Peak: %u blocks, %u bytes (total %u bytes)\n", stat.blocks_peak, stat.bytes_peak, stat.bytes_peak +(stat.blocks_peak*sizeof(struct mem))); err |= re_hprintf(pf, " Block size: min=%u, max=%u\n", stat.size_min, stat.size_max); err |= re_hprintf(pf, " Total %u blocks allocated\n", c); return err; #else (void)pf; (void)unused; return 0; #endif } /** * Get memory statistics * * @param mstat Returned memory statistics * * @return 0 if success, otherwise errorcode */ int mem_get_stat(struct memstat *mstat) { if (!mstat) return EINVAL; #if MEM_DEBUG mem_lock(); memcpy(mstat, &memstat, sizeof(*mstat)); mem_unlock(); return 0; #else return ENOSYS; #endif } re-1.1.0/src/mem/mod.mk000066400000000000000000000001301373627245400146030ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += mem/mem.c SRCS += mem/secure.c re-1.1.0/src/mem/secure.c000066400000000000000000000014101373627245400151270ustar00rootroot00000000000000/** * @file mem/secure.c Secure memory functions * * Copyright (C) 2010 Creytiv.com */ #include #include /** * Compare two byte strings in constant time. This function can be used * by secure code to compare secret data, such as authentication tags, * to avoid side-channel attacks. * * @param s1 First byte string * @param s2 Second byte string * @param n Number of bytes * * @return a negative number if argument errors * 0 if both byte strings matching * a positive number if not matching */ int mem_seccmp(const volatile uint8_t *volatile s1, const volatile uint8_t *volatile s2, size_t n) { uint8_t val = 0; if (!s1 || !s2) return -1; while (n--) val |= *s1++ ^ *s2++; return val; } re-1.1.0/src/mod/000077500000000000000000000000001373627245400135025ustar00rootroot00000000000000re-1.1.0/src/mod/dl.c000066400000000000000000000024411373627245400142460ustar00rootroot00000000000000/** * @file dl.c Interface to dynamic linking loader * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include "mod_internal.h" #define DEBUG_MODULE "dl" #define DEBUG_LEVEL 5 #include static const int dl_flag = RTLD_NOW | RTLD_LOCAL; /** * Load a dynamic library file * * @param name Name of library to load * * @return Opaque library handle, NULL if not loaded */ void *_mod_open(const char *name) { void *h; if (!name) return NULL; h = dlopen(name, dl_flag); if (!h) { DEBUG_WARNING("mod: %s (%s)\n", name, dlerror()); return NULL; } return h; } /** * Resolve address of symbol in dynamic library * * @param h Library handle * @param symbol Name of symbol to resolve * * @return Address, NULL if failure */ void *_mod_sym(void *h, const char *symbol) { void *sym; const char *err; if (!h || !symbol) return NULL; (void)dlerror(); /* Clear any existing error */ sym = dlsym(h, symbol); err = dlerror(); if (err) { DEBUG_WARNING("dlsym: %s\n", err); return NULL; } return sym; } /** * Unload a dynamic library * * @param h Library handle */ void _mod_close(void *h) { int err; if (!h) return; err = dlclose(h); if (0 != err) { DEBUG_WARNING("dlclose: %d\n", err); } } re-1.1.0/src/mod/mod.c000066400000000000000000000075641373627245400144410ustar00rootroot00000000000000/** * @file mod.c Loadable modules * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "mod_internal.h" #define DEBUG_MODULE "mod" #define DEBUG_LEVEL 5 #include /** Defines a loadable module */ struct mod { struct le le; /**< Linked list element */ void *h; /**< Module handler */ const struct mod_export *me; /**< Module exports */ }; static struct list modl; /* struct mod */ /** * Initialise module loading */ void mod_init(void) { list_init(&modl); } /** * Unload all modules */ void mod_close(void) { list_flush(&modl); } static void mod_destructor(void *data) { struct mod *m = data; const struct mod_export *me = m->me; int err; if (me && me->close && (err = me->close())) { DEBUG_NOTICE("close: error (%m)\n", err); } list_unlink(&m->le); _mod_close(m->h); } /** * Find a module by name in the list of loaded modules * * @param name Name of module to find * * @return Module if found, NULL if not found */ struct mod *mod_find(const char *name) { struct le *le; struct pl x; if (!name) return NULL; if (re_regex(name, strlen(name), "[/]*[^./]+" MOD_EXT, NULL, &x)) return NULL; for (le = modl.head; le; le = le->next) { struct mod *m = le->data; if (0 == pl_strcasecmp(&x, m->me->name)) return m; } return NULL; } /** * Load and initialise a loadable module by name * * @param mp Pointer to allocated module object * @param name Name of loadable module * * @return 0 if success, otherwise errorcode */ int mod_load(struct mod **mp, const char *name) { struct mod *m; int err = 0; if (!mp || !name) return EINVAL; /* check if already loaded */ m = mod_find(name); if (m) { DEBUG_NOTICE("module already loaded: %s\n", name); return EALREADY; } m = mem_zalloc(sizeof(*m), mod_destructor); if (!m) return ENOMEM; list_append(&modl, &m->le, m); m->h = _mod_open(name); if (!m->h) { err = ENOENT; goto out; } m->me = _mod_sym(m->h, "exports"); if (!m->me) { err = ELIBBAD; goto out; } if (m->me->init && (err = m->me->init())) goto out; out: if (err) mem_deref(m); else *mp = m; return err; } /** * Add and initialise an external module with exports * * @param mp Pointer to allocated module object * @param me Module exports * * @return 0 if success, otherwise errorcode */ int mod_add(struct mod **mp, const struct mod_export *me) { struct mod *m; int err = 0; if (!mp || !me) return EINVAL; /* check if already loaded */ m = mod_find(me->name); if (m) { DEBUG_NOTICE("module already loaded: %s\n", me->name); return EALREADY; } m = mem_zalloc(sizeof(*m), mod_destructor); if (!m) return ENOMEM; list_append(&modl, &m->le, m); m->me = me; if (m->me->init) err = m->me->init(); if (err) mem_deref(m); else *mp = m; return err; } /** * Get module export from a loadable module * * @param m Loadable module * * @return Module export */ const struct mod_export *mod_export(const struct mod *m) { return m ? m->me : NULL; } /** * Get the list of loaded modules * * @return Module list */ struct list *mod_list(void) { return &modl; } /** * Debug loadable modules * * @param pf Print handler for debug output * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int mod_debug(struct re_printf *pf, void *unused) { struct le *le; int err; (void)unused; err = re_hprintf(pf, "\n--- Modules (%u) ---\n", list_count(&modl)); for (le = modl.head; le && !err; le = le->next) { const struct mod *m = le->data; const struct mod_export *me = m->me; err = re_hprintf(pf, " %16s type=%-12s ref=%u\n", me->name, me->type, mem_nrefs(m)); } err |= re_hprintf(pf, "\n"); return err; } re-1.1.0/src/mod/mod.mk000066400000000000000000000002561373627245400146150ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += mod/mod.c # Unix dlopen ifdef HAVE_DLFCN_H SRCS += mod/dl.c endif ifeq ($(OS),win32) SRCS += mod/win32/dll.c endif re-1.1.0/src/mod/mod_internal.h000066400000000000000000000004401373627245400163240ustar00rootroot00000000000000/** * @file mod_internal.h Internal interface to loadable module * * Copyright (C) 2010 Creytiv.com */ #ifdef __cplusplus extern "C" { #endif void *_mod_open(const char *name); void *_mod_sym(void *h, const char *symbol); void _mod_close(void *h); #ifdef __cplusplus } #endif re-1.1.0/src/mod/win32/000077500000000000000000000000001373627245400144445ustar00rootroot00000000000000re-1.1.0/src/mod/win32/dll.c000066400000000000000000000024311373627245400153630ustar00rootroot00000000000000/** * @file dll.c Dynamic library loading for Windows * * Copyright (C) 2010 Creytiv.com */ #include #include #include "../mod_internal.h" #define DEBUG_MODULE "dll" #define DEBUG_LEVEL 5 #include /** * Open a DLL file * * @param name Name of DLL to open * * @return Handle (NULL if failed) */ void *_mod_open(const char *name) { HINSTANCE DllHandle = 0; DEBUG_INFO("loading %s\n", name); DllHandle = LoadLibraryA(name); if (!DllHandle) { DEBUG_WARNING("open: %s LoadLibraryA() failed\n", name); return NULL; } return DllHandle; } /** * Resolve a symbol address in a DLL * * @param h DLL Handle * @param symbol Symbol to resolve * * @return Address of symbol */ void *_mod_sym(void *h, const char *symbol) { HINSTANCE DllHandle = (HINSTANCE)h; union { FARPROC sym; void *ptr; } u; if (!DllHandle) return NULL; DEBUG_INFO("get symbol: %s\n", symbol); u.sym = GetProcAddress(DllHandle, symbol); if (!u.sym) { DEBUG_WARNING("GetProcAddress: no symbol %s\n", symbol); return NULL; } return u.ptr; } /** * Close a DLL * * @param h DLL Handle */ void _mod_close(void *h) { HINSTANCE DllHandle = (HINSTANCE)h; DEBUG_INFO("unloading %p\n", h); if (!DllHandle) return; FreeLibrary(DllHandle); } re-1.1.0/src/mqueue/000077500000000000000000000000001373627245400142245ustar00rootroot00000000000000re-1.1.0/src/mqueue/mod.mk000066400000000000000000000001771373627245400153410ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += mqueue/mqueue.c ifeq ($(OS),win32) SRCS += mqueue/win32/pipe.c endif re-1.1.0/src/mqueue/mqueue.c000066400000000000000000000053501373627245400156740ustar00rootroot00000000000000/** * @file mqueue.c Thread Safe Message Queue * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include "mqueue.h" #define MAGIC 0x14553399 #ifdef WIN32 #include #include #define close closesocket #endif /** * Defines a Thread-safe Message Queue * * The Message Queue can be used to communicate between two threads. The * receiving thread must run the re_main() loop which will be woken up on * incoming messages from other threads. The sender thread can be any thread. */ struct mqueue { int pfd[2]; mqueue_h *h; void *arg; }; struct msg { void *data; uint32_t magic; int id; }; static void destructor(void *arg) { struct mqueue *q = arg; if (q->pfd[0] >= 0) { fd_close(q->pfd[0]); (void)close(q->pfd[0]); } if (q->pfd[1] >= 0) (void)close(q->pfd[1]); } static void event_handler(int flags, void *arg) { struct mqueue *mq = arg; struct msg msg; ssize_t n; if (!(flags & FD_READ)) return; n = pipe_read(mq->pfd[0], &msg, sizeof(msg)); if (n < 0) return; if (n != sizeof(msg)) { (void)re_fprintf(stderr, "mqueue: short read of %d bytes\n", n); return; } if (msg.magic != MAGIC) { (void)re_fprintf(stderr, "mqueue: bad magic on read (%08x)\n", msg.magic); return; } mq->h(msg.id, msg.data, mq->arg); } /** * Allocate a new Message Queue * * @param mqp Pointer to allocated Message Queue * @param h Message handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int mqueue_alloc(struct mqueue **mqp, mqueue_h *h, void *arg) { struct mqueue *mq; int err = 0; if (!mqp || !h) return EINVAL; mq = mem_zalloc(sizeof(*mq), destructor); if (!mq) return ENOMEM; mq->h = h; mq->arg = arg; mq->pfd[0] = mq->pfd[1] = -1; if (pipe(mq->pfd) < 0) { err = errno; goto out; } err = net_sockopt_blocking_set(mq->pfd[0], false); if (err) goto out; err = net_sockopt_blocking_set(mq->pfd[1], false); if (err) goto out; err = fd_listen(mq->pfd[0], FD_READ, event_handler, mq); if (err) goto out; out: if (err) mem_deref(mq); else *mqp = mq; return err; } /** * Push a new message onto the Message Queue * * @param mq Message Queue * @param id General purpose Identifier * @param data Application data * * @return 0 if success, otherwise errorcode */ int mqueue_push(struct mqueue *mq, int id, void *data) { struct msg msg; ssize_t n; if (!mq) return EINVAL; msg.id = id; msg.data = data; msg.magic = MAGIC; n = pipe_write(mq->pfd[1], &msg, sizeof(msg)); if (n < 0) return errno; return (n != sizeof(msg)) ? EPIPE : 0; } re-1.1.0/src/mqueue/mqueue.h000066400000000000000000000007101373627245400156740ustar00rootroot00000000000000/** * @file mqueue.h Thread Safe Message Queue -- Internal API * * Copyright (C) 2010 Creytiv.com */ #ifdef WIN32 int pipe(int fds[2]); ssize_t pipe_read(int s, void *buf, size_t len); ssize_t pipe_write(int s, const void *buf, size_t len); #else static inline ssize_t pipe_read(int s, void *buf, size_t len) { return read(s, buf, len); } static inline ssize_t pipe_write(int s, const void *buf, size_t len) { return write(s, buf, len); } #endif re-1.1.0/src/mqueue/win32/000077500000000000000000000000001373627245400151665ustar00rootroot00000000000000re-1.1.0/src/mqueue/win32/pipe.c000066400000000000000000000026451373627245400162760ustar00rootroot00000000000000/** * @file pipe.c Pipe-emulation for Windows * * Copyright (C) 2010 Creytiv.com */ #include #include #include "../mqueue.h" /* * Emulate pipe on Windows -- pipe() with select() is not working */ int pipe(int fds[2]) { SOCKET s, rd, wr; struct sockaddr_in serv_addr; int len = sizeof(serv_addr); if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) return ENOSYS; memset((void *) &serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(0); serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); if (bind(s, (SOCKADDR *) & serv_addr, len) == SOCKET_ERROR) goto error; if (listen(s, 1) == SOCKET_ERROR) goto error; if (getsockname(s, (SOCKADDR *) &serv_addr, &len) == SOCKET_ERROR) goto error; if ((wr = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET) goto error; if (connect(wr, (SOCKADDR *) &serv_addr, len) == SOCKET_ERROR) { closesocket(wr); goto error; } rd = accept(s, (SOCKADDR *) &serv_addr, &len); if (rd == INVALID_SOCKET) { closesocket(wr); goto error; } fds[0] = rd; fds[1] = wr; closesocket(s); return 0; error: closesocket(s); return ENOSYS; } ssize_t pipe_read(int s, void *buf, size_t len) { int ret = recv(s, buf, len, 0); if (ret < 0 && WSAGetLastError() == WSAECONNRESET) ret = 0; return ret; } ssize_t pipe_write(int s, const void *buf, size_t len) { return send(s, buf, len, 0); } re-1.1.0/src/msg/000077500000000000000000000000001373627245400135115ustar00rootroot00000000000000re-1.1.0/src/msg/ctype.c000066400000000000000000000022451373627245400150040ustar00rootroot00000000000000/** * @file ctype.c Content-Type decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include /** * Decode a pointer-length string into Content-Type header * * @param ctype Content-Type header * @param pl Pointer-length string * * @return 0 for success, otherwise errorcode */ int msg_ctype_decode(struct msg_ctype *ctype, const struct pl *pl) { struct pl ws; if (!ctype || !pl) return EINVAL; if (re_regex(pl->p, pl->l, "[ \t\r\n]*[^ \t\r\n;/]+[ \t\r\n]*/[ \t\r\n]*[^ \t\r\n;]+" "[^]*", &ws, &ctype->type, NULL, NULL, &ctype->subtype, &ctype->params)) return EBADMSG; if (ws.p != pl->p) return EBADMSG; return 0; } /** * Compare Content-Type * * @param ctype Content-Type header * @param type Media type * @param subtype Media sub-type * * @return true if match, false if no match */ bool msg_ctype_cmp(const struct msg_ctype *ctype, const char *type, const char *subtype) { if (!ctype || !type || !subtype) return false; if (pl_strcasecmp(&ctype->type, type)) return false; if (pl_strcasecmp(&ctype->subtype, subtype)) return false; return true; } re-1.1.0/src/msg/mod.mk000066400000000000000000000001311373627245400146140ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += msg/ctype.c SRCS += msg/param.c re-1.1.0/src/msg/param.c000066400000000000000000000024331373627245400147570ustar00rootroot00000000000000/** * @file param.c SIP Parameter decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include /** * Check if a parameter exists * * @param pl Pointer-length string * @param name Parameter name * @param val Returned parameter value * * @return 0 for success, otherwise errorcode */ int msg_param_exists(const struct pl *pl, const char *name, struct pl *val) { struct pl v1, v2; char xpr[128]; if (!pl || !name || !val) return EINVAL; (void)re_snprintf(xpr, sizeof(xpr), ";[ \t\r\n]*%s[ \t\r\n;=]*", name); if (re_regex(pl->p, pl->l, xpr, &v1, &v2)) return ENOENT; if (!v2.l && v2.p < pl->p + pl->l) return ENOENT; val->p = v1.p - 1; val->l = v2.p - val->p; return 0; } /** * Decode a Parameter * * @param pl Pointer-length string * @param name Parameter name * @param val Returned parameter value * * @return 0 for success, otherwise errorcode */ int msg_param_decode(const struct pl *pl, const char *name, struct pl *val) { char expr[128]; struct pl v; if (!pl || !name || !val) return EINVAL; (void)re_snprintf(expr, sizeof(expr), ";[ \t\r\n]*%s[ \t\r\n]*=[ \t\r\n]*[~ \t\r\n;]+", name); if (re_regex(pl->p, pl->l, expr, NULL, NULL, NULL, &v)) return ENOENT; *val = v; return 0; } re-1.1.0/src/natbd/000077500000000000000000000000001373627245400140135ustar00rootroot00000000000000re-1.1.0/src/natbd/filtering.c000066400000000000000000000137371373627245400161550ustar00rootroot00000000000000/** * @file filtering.c NAT Filtering Behaviour Discovery * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #define DEBUG_MODULE "natbd_filtering" #define DEBUG_LEVEL 5 #include /* Determining NAT Filtering Behavior This will also require at most three tests. These tests should be performed using a port that wasn't used for mapping or other tests as packets sent during those tests may affect results. In test I, the client performs the UDP connectivity test. The server will return its alternate address and port in OTHER-ADDRESS in the binding response. If OTHER-ADDRESS is not returned, the server does not support this usage and this test cannot be run. In test II, the client sends a binding request to the primary address of the server with the CHANGE-REQUEST attribute set to change-port and change-IP. This will cause the server to send its response from its alternate IP address and alternate port. If the client receives a response the current behaviour of the NAT is Endpoint-Independent Filtering. If no response is received, test III must be performed to distinguish between Address-Dependent Filtering and Address and Port-Dependent Filtering. In test III, the client sends a binding request to the original server address with CHANGE-REQUEST set to change-port. If the client receives a response the current behaviour is Address- Dependent Filtering; if no response is received the current behaviour is Address and Port-Dependent Filtering. */ /** Defines a NAT Filtering Behaviour Discovery session */ struct nat_filtering { struct stun *stun; /**< STUN instance */ struct sa srv; /**< Server IP address and port */ int test_phase; /**< State machine */ nat_filtering_h *fh; /**< Result handler */ void *arg; /**< Handler argument */ }; static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct stun_change_req change_req; struct nat_filtering *nf = arg; struct stun_attr *attr; (void)reason; if (err == ECONNABORTED) { nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg); return; } attr = stun_msg_attr(msg, STUN_ATTR_OTHER_ADDR); if (!err && !attr) { DEBUG_WARNING("no OTHER-ADDRESS in response - abort\n"); nf->fh(EINVAL, NAT_TYPE_UNKNOWN, nf->arg); return; } switch (nf->test_phase) { case 1: /* Test I completed */ if (err || scode) { DEBUG_WARNING("Test I: stun_response_handler: %m\n", err); nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg); return; } /* Start Test II */ /* In test II, the client sends a binding request to the primary address of the server with the CHANGE-REQUEST attribute set to change-port and change-IP. */ ++nf->test_phase; change_req.ip = true; change_req.port = true; err = stun_request(NULL, nf->stun, IPPROTO_UDP, NULL, &nf->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nf, 2, STUN_ATTR_SOFTWARE, stun_software, STUN_ATTR_CHANGE_REQ, &change_req); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg); } break; case 2: /* Test II completed */ /* If the client receives a response the current behaviour of the NAT is Endpoint Independent Filtering. */ if (0 == err) { if (!scode) { nf->fh(0, NAT_TYPE_ENDP_INDEP, nf->arg); } else { nf->fh(EINVAL, NAT_TYPE_UNKNOWN, nf->arg); } return; } /* Start Test III - the client sends a binding request to the original server address with CHANGE-REQUEST set to change-port */ ++nf->test_phase; change_req.ip = false; change_req.port = true; err = stun_request(NULL, nf->stun, IPPROTO_UDP, NULL, &nf->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nf, 2, STUN_ATTR_SOFTWARE, stun_software, STUN_ATTR_CHANGE_REQ, &change_req); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nf->fh(err, NAT_TYPE_UNKNOWN, nf->arg); } break; case 3: /* Test III completed */ DEBUG_INFO("Test III completed\n"); if (0 == err && !scode) { nf->fh(0, NAT_TYPE_ADDR_DEP, nf->arg); } else { nf->fh(0, NAT_TYPE_ADDR_PORT_DEP, nf->arg); } break; default: DEBUG_WARNING("invalid test phase %d\n", nf->test_phase); nf->fh(EINVAL, NAT_TYPE_UNKNOWN, nf->arg); return; } } static void filtering_destructor(void *data) { struct nat_filtering *nf = data; mem_deref(nf->stun); } /** * Allocate a NAT Filtering Behaviour Discovery session * * @param nfp Pointer to allocated NAT filtering object * @param srv STUN Server IP address and port number * @param conf STUN configuration (Optional) * @param fh Filtering result handler * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_filtering_alloc(struct nat_filtering **nfp, const struct sa *srv, const struct stun_conf *conf, nat_filtering_h *fh, void *arg) { struct nat_filtering *nf; int err; if (!nfp || !srv || !fh) return EINVAL; nf = mem_zalloc(sizeof(*nf), filtering_destructor); if (!nf) return ENOMEM; err = stun_alloc(&nf->stun, conf, NULL, NULL); if (err) goto out; sa_cpy(&nf->srv, srv); nf->fh = fh; nf->arg = arg; out: if (err) mem_deref(nf); else *nfp = nf; return err; } /** * Start a NAT Filtering Behaviour Discovery session * * @param nf NAT filtering object * * @return 0 if success, errorcode if failure */ int nat_filtering_start(struct nat_filtering *nf) { if (!nf) return EINVAL; nf->test_phase = 1; return stun_request(NULL, nf->stun, IPPROTO_UDP, NULL, &nf->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nf, 1, STUN_ATTR_SOFTWARE, stun_software); } re-1.1.0/src/natbd/genalg.c000066400000000000000000000065101373627245400154160ustar00rootroot00000000000000/** * @file genalg.c Detecting Generic ALGs * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #define DEBUG_MODULE "natbd_genalg" #define DEBUG_LEVEL 7 #include /* Detecting Generic ALGs A number of NAT boxes are now being deployed into the market which try to provide "generic" ALG functionality. These generic ALGs hunt for IP addresses, either in text or binary form within a packet, and rewrite them if they match a binding. This behavior can be detected because the STUN server returns both the MAPPED-ADDRESS and XOR- MAPPED-ADDRESS in the same response. If the result in the two does not match, there a NAT with a generic ALG in the path. */ /** Defines a NAT Generic ALG detection session */ struct nat_genalg { struct stun *stun; /**< STUN Client */ struct sa srv; /**< Server address and port */ int proto; /**< IP protocol */ nat_genalg_h *h; /**< Result handler */ void *arg; /**< Handler argument */ }; static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct stun_attr *xmap, *map; struct nat_genalg *ng = arg; int status = 0; (void)reason; if (err) { ng->h(err, 0, NULL, -1, NULL, ng->arg); return; } switch (scode) { case 0: map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); xmap = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!map || !xmap) { ng->h(EINVAL, scode, reason, -1, NULL, ng->arg); break; } status = sa_cmp(&map->v.sa, &xmap->v.sa, SA_ALL) ? -1 : 1; ng->h(0, scode, reason, status, &xmap->v.sa, ng->arg); break; default: ng->h(0, scode, reason, -1, NULL, ng->arg); break; } } static void genalg_destructor(void *data) { struct nat_genalg *ng = data; mem_deref(ng->stun); } /** * Allocate a new NAT Generic ALG detection session * * @param ngp Pointer to allocated NAT Generic ALG object * @param srv STUN Server IP address and port * @param proto Transport protocol * @param conf STUN configuration (Optional) * @param gh Generic ALG handler * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_genalg_alloc(struct nat_genalg **ngp, const struct sa *srv, int proto, const struct stun_conf *conf, nat_genalg_h *gh, void *arg) { struct nat_genalg *ng; int err; if (!ngp || !srv || !proto || !gh) return EINVAL; ng = mem_zalloc(sizeof(*ng), genalg_destructor); if (!ng) return ENOMEM; err = stun_alloc(&ng->stun, conf, NULL, NULL); if (err) goto out; sa_cpy(&ng->srv, srv); ng->proto = proto; ng->h = gh; ng->arg = arg; out: if (err) mem_deref(ng); else *ngp = ng; return err; } /** * Start the NAT Generic ALG detection * * @param ng NAT Generic ALG object * * @return 0 if success, errorcode if failure */ int nat_genalg_start(struct nat_genalg *ng) { int err; if (!ng) return EINVAL; err = stun_request(NULL, ng->stun, ng->proto, NULL, &ng->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, ng, 1, STUN_ATTR_SOFTWARE, stun_software); return err; } re-1.1.0/src/natbd/hairpinning.c000066400000000000000000000165671373627245400165040ustar00rootroot00000000000000/** * @file hairpinning.c NAT Hairpinning Behaviour discovery * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "natbd_hairpinning" #define DEBUG_LEVEL 5 #include /* Diagnosing NAT Hairpinning STUN Binding Requests allow a a client to determine whether it is behind a NAT that support hairpinning of datagrams. To perform this test, the client first sends a Binding Request to its STUN server to determine its mapped address. The client then sends a STUN Binding Request to this mapped address from a different port. If the client receives its own request, the NAT hairpins datagrams. This test applies to UDP, TCP, or TCP/TLS connections. */ /** Defines NAT Hairpinning Behaviour Discovery */ struct nat_hairpinning { struct stun *stun; /**< STUN Client */ int proto; /**< IP Protocol */ struct sa srv; /**< Server address and port */ struct udp_sock *us; /**< UDP socket */ struct tcp_conn *tc; /**< Client TCP Connection */ struct tcp_sock *ts; /**< Server TCP Socket */ struct tcp_conn *tc2; /**< Server TCP Connection */ nat_hairpinning_h *hph; /**< Result handler */ void *arg; /**< Handler argument */ }; static void hairpinning_destructor(void *data) { struct nat_hairpinning *nh = data; mem_deref(nh->us); mem_deref(nh->tc); mem_deref(nh->ts); mem_deref(nh->tc2); mem_deref(nh->stun); } static void msg_recv(struct nat_hairpinning *nh, int proto, void *sock, const struct sa *src, struct mbuf *mb) { struct stun_unknown_attr ua; struct stun_msg *msg; if (0 != stun_msg_decode(&msg, mb, &ua)) return; switch (stun_msg_class(msg)) { case STUN_CLASS_REQUEST: (void)stun_reply(proto, sock, src, 0, msg, NULL, 0, false, 3, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, stun_software); break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(nh->stun, msg, &ua); break; default: DEBUG_WARNING("unknown class 0x%04x\n", stun_msg_class(msg)); break; } mem_deref(msg); } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct nat_hairpinning *nh = arg; msg_recv(nh, IPPROTO_UDP, nh->us, src, mb); } static void stun_response_handler2(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_hairpinning *nh = arg; (void)reason; (void)msg; if (err || scode) { nh->hph(0, false, nh->arg); return; } /* Hairpinning supported */ nh->hph(0, true, nh->arg); } static int hairpin_send(struct nat_hairpinning *nh, const struct sa *srv) { return stun_request(NULL, nh->stun, nh->proto, NULL, srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler2, nh, 1, STUN_ATTR_SOFTWARE, stun_software); } /* * TCP Connections: STUN Client2 to Embedded STUN Server */ static void tcp_recv_handler2(struct mbuf *mb, void *arg) { struct nat_hairpinning *nh = arg; msg_recv(nh, IPPROTO_TCP, nh->tc2, NULL, mb); } static void tcp_close_handler2(int err, void *arg) { struct nat_hairpinning *nh = arg; if (err) nh->hph(err, false, nh->arg); } static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_hairpinning *nh = arg; const struct stun_attr *attr; (void)reason; if (err) { nh->hph(err, false, nh->arg); return; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (scode || !attr) { nh->hph(EBADMSG, false, nh->arg); return; } /* Send hairpinning test message */ err = hairpin_send(nh, &attr->v.sa); if (err) { DEBUG_WARNING("hairpin_send: (%m)\n", err); } if (err) nh->hph(err, false, nh->arg); } static int mapped_send(struct nat_hairpinning *nh) { return stun_request(NULL, nh->stun, nh->proto, nh->us ? (void *)nh->us : (void *)nh->tc, &nh->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nh, 1, STUN_ATTR_SOFTWARE, stun_software); } static void tcp_conn_handler(const struct sa *peer, void *arg) { struct nat_hairpinning *nh = arg; int err; (void)peer; err = tcp_accept(&nh->tc2, nh->ts, NULL, tcp_recv_handler2, tcp_close_handler2, nh); if (err) { DEBUG_WARNING("TCP conn: tcp_accept: %m\n", err); } } /* * TCP Connection: STUN Client to STUN Server */ static void tcp_estab_handler(void *arg) { struct nat_hairpinning *nh = arg; int err; err = mapped_send(nh); if (err) { DEBUG_WARNING("TCP established: mapped_send (%m)\n", err); nh->hph(err, false, nh->arg); } } static void tcp_recv_handler(struct mbuf *mb, void *arg) { struct nat_hairpinning *nh = arg; int err; err = stun_recv(nh->stun, mb); if (err && ENOENT != err) { DEBUG_WARNING("stun recv: %m\n", err); } } static void tcp_close_handler(int err, void *arg) { struct nat_hairpinning *nh = arg; if (err) nh->hph(err, false, nh->arg); } /** * Allocate a new NAT Hairpinning discovery session * * @param nhp Pointer to allocated NAT Hairpinning object * @param proto Transport protocol * @param srv STUN Server IP address and port number * @param proto Transport protocol * @param conf STUN configuration (Optional) * @param hph Hairpinning result handler * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_hairpinning_alloc(struct nat_hairpinning **nhp, const struct sa *srv, int proto, const struct stun_conf *conf, nat_hairpinning_h *hph, void *arg) { struct nat_hairpinning *nh; struct sa local; int err; if (!srv || !hph) return EINVAL; nh = mem_zalloc(sizeof(*nh), hairpinning_destructor); if (!nh) return ENOMEM; err = stun_alloc(&nh->stun, conf, NULL, NULL); if (err) goto out; sa_cpy(&nh->srv, srv); nh->proto = proto; nh->hph = hph; nh->arg = arg; switch (proto) { case IPPROTO_UDP: err = udp_listen(&nh->us, NULL, udp_recv_handler, nh); break; case IPPROTO_TCP: sa_set_in(&local, 0, 0); /* * Part I - Allocate and bind all sockets */ err = tcp_sock_alloc(&nh->ts, &local, tcp_conn_handler, nh); if (err) break; err = tcp_conn_alloc(&nh->tc, &nh->srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, nh); if (err) break; err = tcp_sock_bind(nh->ts, &local); if (err) break; err = tcp_sock_local_get(nh->ts, &local); if (err) break; err = tcp_conn_bind(nh->tc, &local); if (err) break; /* * Part II - Listen and connect all sockets */ err = tcp_sock_listen(nh->ts, 5); break; default: err = EPROTONOSUPPORT; break; } out: if (err) mem_deref(nh); else *nhp = nh; return err; } /** * Start a new NAT Hairpinning discovery session * * @param nh NAT Hairpinning object * * @return 0 if success, errorcode if failure */ int nat_hairpinning_start(struct nat_hairpinning *nh) { if (!nh) return EINVAL; switch (nh->proto) { case IPPROTO_UDP: return mapped_send(nh); case IPPROTO_TCP: return tcp_conn_connect(nh->tc, &nh->srv); default: return EPROTONOSUPPORT; } } re-1.1.0/src/natbd/lifetime.c000066400000000000000000000154031373627245400157600ustar00rootroot00000000000000/** * @file lifetime.c NAT Binding Lifetime Discovery * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "natbd_lifetime" #define DEBUG_LEVEL 5 #include /* Binding Lifetime Discovery STUN can also be used to probe the lifetimes of the bindings created by the NAT. For many NAT devices, an absolute refresh interval cannot be determined; bindings might be closed quicker under heavy load or might not behave as the tests suggest. For this reason applications that require reliable bindings must send keep-alives as frequently as required by all NAT devices that will be encountered. */ /** Defines a NAT Binding Lifetime Discovery session */ struct nat_lifetime { struct stun *stun; /**< STUN Client */ struct stun_ctrans *ctx; /**< STUN Transaction 1 */ struct stun_ctrans *cty; /**< STUN Transaction 2 */ struct udp_sock *us_x; /**< First UDP socket */ struct udp_sock *us_y; /**< Second UDP socket */ struct sa srv; /**< Server IP-address/port */ struct sa map; /**< Mapped IP address/port */ struct tmr tmr; /**< Refresh timer */ bool probing; /**< Probing flag */ struct nat_lifetime_interval interval; /**< Lifetime intervals */ nat_lifetime_h *lh; /**< Result handler */ void *arg; /**< Handler argument */ }; static void timeout(void *arg); static void binding_ok(struct nat_lifetime *nl); static void binding_expired(struct nat_lifetime *nl); /* * X socket */ static void msg_recv(struct stun *stun, const struct sa *src, struct mbuf *mb) { int err; (void)src; err = stun_recv(stun, mb); if (err && ENOENT != err) { DEBUG_WARNING("msg_recv: stunc_recv(): (%m)\n", err); } } static void udp_recv_handler_x(const struct sa *src, struct mbuf *mb, void *arg) { struct nat_lifetime *nl = arg; /* Forward response to socket */ msg_recv(nl->stun, src, mb); } static void stun_response_handler_x(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_lifetime *nl = arg; struct stun_attr *attr; (void)reason; if (err) { DEBUG_WARNING("stun_response_handler X: %m\n", err); goto out; } switch (scode) { case 0: attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { err = EPROTO; break; } nl->map = attr->v.xor_mapped_addr; DEBUG_INFO("Starting timer of %d seconds...[zzz]...\n", nl->interval.cur); tmr_start(&nl->tmr, nl->interval.cur*1000, timeout, nl); return; default: err = EPROTO; break; } out: nl->lh(err, &nl->interval, nl->arg); binding_expired(nl); } /* * Y socket */ static void udp_recv_handler_y(const struct sa *src, struct mbuf *mb, void *arg) { struct nat_lifetime *nl = arg; (void)src; (void)mb; if (!nl->probing) { DEBUG_WARNING("Y: hmm, not probing?\n"); } binding_expired(nl); } static void stun_response_handler_y(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_lifetime *nl = arg; (void)reason; (void)msg; if (err) { binding_expired(nl); return; } switch (scode) { case 0: binding_ok(nl); break; default: nl->lh(EBADMSG, &nl->interval, nl->arg); break; } } /* * Common */ static int start_test(struct nat_lifetime *nl) { nl->probing = false; tmr_cancel(&nl->tmr); nl->ctx = mem_deref(nl->ctx); return stun_request(&nl->ctx, nl->stun, IPPROTO_UDP, nl->us_x, &nl->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler_x, nl, 1, STUN_ATTR_SOFTWARE, stun_software); } static void timeout(void *arg) { struct nat_lifetime *nl = arg; const uint16_t rp = sa_port(&nl->map); int err; nl->probing = true; nl->cty = mem_deref(nl->cty); err = stun_request(&nl->cty, nl->stun, IPPROTO_UDP, nl->us_y, &nl->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler_y, nl, 2, STUN_ATTR_RESP_PORT, &rp, STUN_ATTR_SOFTWARE, stun_software); if (err) goto out; return; out: DEBUG_WARNING("timeout: (%m)\n", err); nl->lh(err, &nl->interval, nl->arg); (void)start_test(nl); } /* Binding OK - recalculate current interval */ static void binding_ok(struct nat_lifetime *nl) { nl->interval.min = max(1, nl->interval.cur); if (nl->interval.max > 0) nl->interval.cur = (nl->interval.min + nl->interval.max) / 2; else nl->interval.cur *= 2; nl->lh(0, &nl->interval, nl->arg); (void)start_test(nl); } /* Request timed out - recalculate current interval */ static void binding_expired(struct nat_lifetime *nl) { nl->interval.max = nl->interval.cur; nl->interval.cur = (nl->interval.min + nl->interval.max) / 2; nl->lh(0, &nl->interval, nl->arg); (void)start_test(nl); } static void lifetime_destructor(void *data) { struct nat_lifetime *nl = data; tmr_cancel(&nl->tmr); mem_deref(nl->ctx); mem_deref(nl->cty); mem_deref(nl->us_x); mem_deref(nl->us_y); mem_deref(nl->stun); } /** * Allocate a new NAT Lifetime discovery session * * @param nlp Pointer to allocated NAT Lifetime object * @param srv STUN Server IP address and port number * @param interval Initial interval in [seconds] * @param conf STUN configuration (Optional) * @param lh Lifetime handler - called for each probe * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_lifetime_alloc(struct nat_lifetime **nlp, const struct sa *srv, uint32_t interval, const struct stun_conf *conf, nat_lifetime_h *lh, void *arg) { struct nat_lifetime *nl; int err; if (!nlp || !srv || !interval || !lh) return EINVAL; nl = mem_zalloc(sizeof(*nl), lifetime_destructor); if (!nl) return ENOMEM; tmr_init(&nl->tmr); err = stun_alloc(&nl->stun, conf, NULL, NULL); if (err) goto out; err = udp_listen(&nl->us_x, NULL, udp_recv_handler_x, nl); if (err) goto out; err = udp_listen(&nl->us_y, NULL, udp_recv_handler_y, nl); if (err) goto out; sa_cpy(&nl->srv, srv); nl->interval.min = 1; nl->interval.cur = interval; nl->lh = lh; nl->arg = arg; out: if (err) mem_deref(nl); else *nlp = nl; return err; } /** * Start a new NAT Lifetime discovery session * * @param nl NAT Lifetime object * * @return 0 if success, errorcode if failure */ int nat_lifetime_start(struct nat_lifetime *nl) { if (!nl) return EINVAL; return start_test(nl); } re-1.1.0/src/natbd/mapping.c000066400000000000000000000201761373627245400156200ustar00rootroot00000000000000/** * @file mapping.c NAT Mapping Behaviour discovery * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "natbd_mapping" #define DEBUG_LEVEL 5 #include /* Determining NAT Mapping Behavior This will require at most three tests. In test I, the client performs the UDP connectivity test. The server will return its alternate address and port in OTHER-ADDRESS in the binding response. If OTHER-ADDRESS is not returned, the server does not support this usage and this test cannot be run. The client examines the XOR- MAPPED-ADDRESS attribute. If this address and port are the same as the local IP address and port of the socket used to send the request, the client knows that it is not NATed and the effective mapping will be Endpoint-Independent. In test II, the client sends a Binding Request to the alternate address, but primary port. If the XOR-MAPPED-ADDRESS in the Binding Response is the same as test I the NAT currently has Endpoint- Independent Mapping. If not, test III is performed: the client sends a Binding Request to the alternate address and port. If the XOR- MAPPED-ADDRESS matches test II, the NAT currently has Address- Dependent Mapping; if it doesn't match it currently has Address and Port-Dependent Mapping. */ /** Defines a NAT Mapping Behaviour Discovery session */ struct nat_mapping { struct stun *stun; /**< STUN Instance */ struct udp_sock *us; /**< UDP socket */ struct tcp_conn *tc; /**< TCP connection */ struct sa laddr; /**< Local IP address and port */ struct sa map[3]; /**< XOR Mapped address/ports */ struct sa srv; /**< STUN server address/port */ nat_mapping_h *mh; /**< Result handler */ void *arg; /**< Handler argument */ int proto; /**< IP Protocol */ uint32_t test_phase; /**< State machine */ struct tcp_conn *tcv[3]; /**< TCP Connections */ }; static int mapping_send(struct nat_mapping *nm); static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct nat_mapping *nm = arg; struct stun_attr *map, *other; if (err) { DEBUG_WARNING("stun_response_handler: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); return; } switch (scode) { case 0: other = stun_msg_attr(msg, STUN_ATTR_OTHER_ADDR); map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!map) map = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!map || !other) { DEBUG_WARNING("missing attributes: %s %s\n", map ? "" : "MAPPED-ADDR", other ? "" : "OTHER-ADDR"); nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg); return; } nm->map[nm->test_phase-1] = map->v.sa; break; default: DEBUG_WARNING("Binding Error Resp: %u %s\n", scode, reason); nm->mh(EPROTO, NAT_TYPE_UNKNOWN, nm->arg); return; } switch (nm->test_phase) { case 1: /* Test I completed */ if (sa_cmp(&nm->laddr, &nm->map[0], SA_ALL)) { nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg); return; } /* Start Test II - the client sends a Binding Request to the alternate *address* */ ++nm->test_phase; sa_set_port(&other->v.other_addr, sa_port(&nm->srv)); sa_cpy(&nm->srv, &other->v.other_addr); err = mapping_send(nm); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } break; case 2: /* Test II completed */ if (sa_cmp(&nm->map[0], &nm->map[1], SA_ALL)) { nm->mh(0, NAT_TYPE_ENDP_INDEP, nm->arg); return; } /* Start Test III - the client sends a Binding Request to the alternate address and port */ ++nm->test_phase; sa_set_port(&nm->srv, sa_port(&other->v.other_addr)); err = mapping_send(nm); if (err) { DEBUG_WARNING("stunc_request_send: (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } break; case 3: /* Test III completed */ if (sa_cmp(&nm->map[1], &nm->map[2], SA_ALL)) { nm->mh(0, NAT_TYPE_ADDR_DEP, nm->arg); } else { nm->mh(0, NAT_TYPE_ADDR_PORT_DEP, nm->arg); } ++nm->test_phase; break; default: DEBUG_WARNING("invalid test phase %d\n", nm->test_phase); nm->mh(EINVAL, NAT_TYPE_UNKNOWN, nm->arg); break; } } static int mapping_send(struct nat_mapping *nm) { switch (nm->proto) { case IPPROTO_UDP: return stun_request(NULL, nm->stun, IPPROTO_UDP, nm->us, &nm->srv, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nm, 1, STUN_ATTR_SOFTWARE, stun_software); case IPPROTO_TCP: nm->tc = mem_deref(nm->tc); nm->tc = mem_ref(nm->tcv[nm->test_phase-1]); return tcp_conn_connect(nm->tc, &nm->srv); default: return EPROTONOSUPPORT; } } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct nat_mapping *nm = arg; int err; (void)src; err = stun_recv(nm->stun, mb); if (err && ENOENT != err) { DEBUG_WARNING("udp_recv_handler: stunc_recv(): (%m)\n", err); } } static void mapping_destructor(void *data) { struct nat_mapping *nm = data; int i; mem_deref(nm->us); mem_deref(nm->tc); for (i=0; i<3; i++) mem_deref(nm->tcv[i]); mem_deref(nm->stun); } static void tcp_estab_handler(void *arg) { struct nat_mapping *nm = arg; int err; err = stun_request(NULL, nm->stun, IPPROTO_TCP, nm->tc, NULL, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, nm, 1, STUN_ATTR_SOFTWARE, stun_software); if (err) { DEBUG_WARNING("TCP established: mapping_send (%m)\n", err); nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } } static void tcp_recv_handler(struct mbuf *mb, void *arg) { struct nat_mapping *nm = arg; int err; err = stun_recv(nm->stun, mb); if (err && ENOENT != err) { DEBUG_WARNING("stunc recv: %m\n", err); } } static void tcp_close_handler(int err, void *arg) { struct nat_mapping *nm = arg; DEBUG_NOTICE("TCP Connection Closed (%m)\n", err); if (err) { nm->mh(err, NAT_TYPE_UNKNOWN, nm->arg); } } /** * Allocate a new NAT Mapping Behaviour Discovery session * * @param nmp Pointer to allocated NAT Mapping object * @param laddr Local IP address * @param srv STUN Server IP address and port * @param proto Transport protocol * @param conf STUN configuration (Optional) * @param mh Mapping handler * @param arg Handler argument * * @return 0 if success, errorcode if failure */ int nat_mapping_alloc(struct nat_mapping **nmp, const struct sa *laddr, const struct sa *srv, int proto, const struct stun_conf *conf, nat_mapping_h *mh, void *arg) { struct nat_mapping *nm; int i, err; if (!nmp || !laddr || !srv || !mh) return EINVAL; nm = mem_zalloc(sizeof(*nm), mapping_destructor); if (!nm) return ENOMEM; err = stun_alloc(&nm->stun, conf, NULL, NULL); if (err) goto out; nm->proto = proto; sa_cpy(&nm->laddr, laddr); switch (proto) { case IPPROTO_UDP: err = udp_listen(&nm->us, &nm->laddr, udp_recv_handler, nm); if (err) goto out; err = udp_local_get(nm->us, &nm->laddr); if (err) goto out; break; case IPPROTO_TCP: /* Allocate and bind 3 TCP Sockets */ for (i=0; i<3; i++) { err = tcp_conn_alloc(&nm->tcv[i], srv, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, nm); if (err) goto out; err = tcp_conn_bind(nm->tcv[i], &nm->laddr); if (err) goto out; err = tcp_conn_local_get(nm->tcv[i], &nm->laddr); if (err) goto out; } break; default: err = EPROTONOSUPPORT; goto out; } sa_cpy(&nm->srv, srv); nm->mh = mh; nm->arg = arg; *nmp = nm; out: if (err) mem_deref(nm); return err; } /** * Start a new NAT Mapping Behaviour Discovery session * * @param nm NAT Mapping object * * @return 0 if success, errorcode if failure */ int nat_mapping_start(struct nat_mapping *nm) { if (!nm) return EINVAL; nm->test_phase = 1; return mapping_send(nm); } re-1.1.0/src/natbd/mod.mk000066400000000000000000000003061373627245400151220ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += natbd/filtering.c SRCS += natbd/genalg.c SRCS += natbd/hairpinning.c SRCS += natbd/lifetime.c SRCS += natbd/mapping.c SRCS += natbd/natstr.c re-1.1.0/src/natbd/natstr.c000066400000000000000000000013541373627245400154750ustar00rootroot00000000000000/** * @file natstr.c NAT Behaviour Discovery strings * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include /** * Get the name of the NAT Mapping/Filtering type * * @param type NAT Mapping/Filtering type * * @return Name of the NAT Mapping/Filtering type */ const char *nat_type_str(enum nat_type type) { switch (type) { case NAT_TYPE_UNKNOWN: return "Unknown"; case NAT_TYPE_ENDP_INDEP: return "Endpoint Independent"; case NAT_TYPE_ADDR_DEP: return "Address Dependent"; case NAT_TYPE_ADDR_PORT_DEP: return "Address and Port Dependent"; default: return "???"; } } re-1.1.0/src/net/000077500000000000000000000000001373627245400135115ustar00rootroot00000000000000re-1.1.0/src/net/bsd/000077500000000000000000000000001373627245400142615ustar00rootroot00000000000000re-1.1.0/src/net/bsd/brt.c000066400000000000000000000036601373627245400152210ustar00rootroot00000000000000/** * @file bsd/brt.c BSD routing table code * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include /* * See https://github.com/boundary/libdnet/blob/master/src/route-bsd.c */ #ifdef __APPLE__ #define RT_MSGHDR_ALIGNMENT sizeof(uint32_t) #else #define RT_MSGHDR_ALIGNMENT sizeof(unsigned long) #endif #define ROUNDUP(a) \ ((a) > 0 \ ? (1 + (((size_t)(a) - 1) | (RT_MSGHDR_ALIGNMENT - 1))) \ : RT_MSGHDR_ALIGNMENT) int net_rt_list(net_rt_h *rth, void *arg) { /* net.route.0.inet.flags.gateway */ int mib[] = {CTL_NET, PF_ROUTE, 0, AF_UNSPEC, NET_RT_FLAGS, RTF_GATEWAY}; char ifname[IFNAMSIZ], *buf, *p; struct rt_msghdr *rt; struct sockaddr *sa, *sa_tab[RTAX_MAX]; struct sa dst, gw; size_t l; int i, err = 0; if (sysctl(mib, sizeof(mib)/sizeof(int), 0, &l, 0, 0) < 0) return errno; if (!l) return ENOENT; buf = mem_alloc(l, NULL); if (!buf) return ENOMEM; if (sysctl(mib, sizeof(mib)/sizeof(int), buf, &l, 0, 0) < 0) { err = errno; goto out; } for (p = buf; prtm_msglen) { rt = (void *)p; /* buffer is aligned */ sa = (struct sockaddr *)(rt + 1); if (rt->rtm_type != RTM_GET) continue; if (!(rt->rtm_flags & RTF_UP)) continue; for (i=0; irtm_addrs & (1 << i)) { sa_tab[i] = sa; sa = (struct sockaddr *) ((char *)sa + ROUNDUP(sa->sa_len)); } else { sa_tab[i] = NULL; } } if ((rt->rtm_addrs & RTA_DST) == RTA_DST) { err = sa_set_sa(&dst, sa_tab[RTAX_DST]); if (err) continue; } if ((rt->rtm_addrs & RTA_GATEWAY) == RTA_GATEWAY) { err = sa_set_sa(&gw, sa_tab[RTAX_GATEWAY]); if (err) continue; } if_indextoname(rt->rtm_index, ifname); if (rth(ifname, &dst, 0, &gw, arg)) break; } out: mem_deref(buf); return err; } re-1.1.0/src/net/if.c000066400000000000000000000103071373627245400142540ustar00rootroot00000000000000/** * @file net/if.c Network interface code * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #define DEBUG_MODULE "netif" #define DEBUG_LEVEL 5 #include /** Interface address entry */ struct ifentry { int af; /**< Address family */ char *ifname; /**< Interface name */ struct sa *ip; /**< IP address */ size_t sz; /**< Size of buffer */ bool found; /**< Found flag */ }; static bool if_getname_handler(const char *ifname, const struct sa *sa, void *arg) { struct ifentry *ife = arg; if (ife->af != sa_af(sa)) return false; if (sa_cmp(sa, ife->ip, SA_ADDR)) { str_ncpy(ife->ifname, ifname, ife->sz); ife->found = true; return true; } return false; } /** * Get the name of the interface for a given IP address * * @param ifname Buffer for returned network interface name * @param sz Size of buffer * @param af Address Family * @param ip Given IP address * * @return 0 if success, otherwise errorcode */ int net_if_getname(char *ifname, size_t sz, int af, const struct sa *ip) { struct ifentry ife; int err; if (!ifname || !sz || !ip) return EINVAL; ife.af = af; ife.ifname = ifname; ife.ip = (struct sa *)ip; ife.sz = sz; ife.found = false; err = net_if_list(if_getname_handler, &ife); return ife.found ? err : ENODEV; } static bool if_getaddr_handler(const char *ifname, const struct sa *sa, void *arg) { struct ifentry *ife = arg; /* Match name of interface? */ if (str_isset(ife->ifname) && 0 != str_casecmp(ife->ifname, ifname)) return false; if (!sa_isset(sa, SA_ADDR)) return false; #if 1 /* skip loopback and link-local IP */ if (sa_is_loopback(sa) || sa_is_linklocal(sa)) return false; #endif /* Match address family */ if (ife->af != sa_af(sa)) return false; /* Match - copy address */ sa_cpy(ife->ip, sa); ife->found = true; return ife->found; } /** * Get IP address for a given network interface * * @param ifname Network interface name (optional) * @param af Address Family * @param ip Returned IP address * * @return 0 if success, otherwise errorcode * * @deprecated Works for IPv4 only */ int net_if_getaddr(const char *ifname, int af, struct sa *ip) { struct ifentry ife; int err; if (!ip) return EINVAL; ife.af = af; ife.ifname = (char *)ifname; ife.ip = ip; ife.sz = 0; ife.found = false; #ifdef HAVE_GETIFADDRS err = net_getifaddrs(if_getaddr_handler, &ife); #else err = net_if_list(if_getaddr_handler, &ife); #endif return ife.found ? err : ENODEV; } static bool if_debug_handler(const char *ifname, const struct sa *sa, void *arg) { struct re_printf *pf = arg; (void)re_hprintf(pf, " %10s: %j\n", ifname, sa); return false; } /** * Debug network interfaces * * @param pf Print handler for debug output * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int net_if_debug(struct re_printf *pf, void *unused) { int err; (void)unused; err = re_hprintf(pf, "net interfaces:\n"); #ifdef HAVE_GETIFADDRS err |= net_getifaddrs(if_debug_handler, pf); #else err |= net_if_list(if_debug_handler, pf); #endif return err; } static bool linklocal_handler(const char *ifname, const struct sa *sa, void *arg) { void **argv = arg; int af = *(int *)argv[1]; if (argv[0] && 0 != str_casecmp(argv[0], ifname)) return false; if (af != AF_UNSPEC && af != sa_af(sa)) return false; if (sa_is_linklocal(sa)) { *((struct sa *)argv[2]) = *sa; return true; } return false; } /** * Get the Link-local address for a specific network interface * * @param ifname Name of the interface * @param af Address family * @param ip Returned link-local address * * @return 0 if success, otherwise errorcode */ int net_if_getlinklocal(const char *ifname, int af, struct sa *ip) { struct sa addr; void *argv[3]; int err; if (!ip) return EINVAL; sa_init(&addr, sa_af(ip)); argv[0] = (void *)ifname; argv[1] = ⁡ argv[2] = &addr; err = net_if_apply(linklocal_handler, argv); if (err) return err; if (!sa_isset(&addr, SA_ADDR)) return ENOENT; *ip = addr; return 0; } re-1.1.0/src/net/ifaddrs.c000066400000000000000000000023041373627245400152700ustar00rootroot00000000000000/** * @file ifaddrs.c Network interface code using getifaddrs(). * * Copyright (C) 2010 Creytiv.com */ #include #include #define __USE_MISC 1 /**< Use MISC code */ #include #include #include #include #include #include #define DEBUG_MODULE "ifaddrs" #define DEBUG_LEVEL 5 #include /** * Get a list of all network interfaces including name and IP address. * Both IPv4 and IPv6 are supported. * * @param ifh Interface handler, called once per network interface. * @param arg Handler argument. * * @return 0 if success, otherwise errorcode. */ int net_getifaddrs(net_ifaddr_h *ifh, void *arg) { struct ifaddrs *ifa, *ifp; int err; if (!ifh) return EINVAL; if (0 != getifaddrs(&ifa)) { err = errno; DEBUG_WARNING("getifaddrs: %m\n", err); return err; } for (ifp = ifa; ifa; ifa = ifa->ifa_next) { struct sa sa; DEBUG_INFO("ifaddr: %10s flags=%08x\n", ifa->ifa_name, ifa->ifa_flags); if (ifa->ifa_flags & IFF_UP) { err = sa_set_sa(&sa, ifa->ifa_addr); if (err) continue; if (ifh(ifa->ifa_name, &sa, arg)) break; } } freeifaddrs(ifp); return 0; } re-1.1.0/src/net/linux/000077500000000000000000000000001373627245400146505ustar00rootroot00000000000000re-1.1.0/src/net/linux/rt.c000066400000000000000000000127711373627245400154510ustar00rootroot00000000000000/** * @file linux/rt.c Routing table code for Linux. See rtnetlink(7) * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #define __USE_POSIX 1 /**< Use POSIX flag */ #include #define __USE_MISC 1 #include #undef __STRICT_ANSI__ #include #include #include #include #include #include #include #include #define DEBUG_MODULE "linuxrt" #define DEBUG_LEVEL 5 #include /* Override macros to avoid casting alignment warning */ #undef RTM_RTA #define RTM_RTA(r) (void *)(((char *)(r)) + NLMSG_ALIGN(sizeof(struct rtmsg))) #undef RTA_NEXT #define RTA_NEXT(rta, len) ((len) -= RTA_ALIGN((rta)->rta_len), \ (void *)(((char *)(rta)) + RTA_ALIGN((rta)->rta_len))) #undef NLMSG_NEXT #define NLMSG_NEXT(nlh,len) ((len) -= NLMSG_ALIGN((nlh)->nlmsg_len), \ (void*)(((char*)(nlh)) + NLMSG_ALIGN((nlh)->nlmsg_len))) enum {BUFSIZE = 8192}; /** Defines a network route */ struct net_rt { char ifname[IFNAMSIZ]; /**< Interface name */ struct sa dst; /**< Destination IP address/network */ int dstlen; /**< Prefix length of destination */ struct sa gw; /**< Gateway IP address */ }; static int read_sock(int fd, uint8_t *buf, size_t size, int seq, int pid) { struct nlmsghdr *nlhdr; int n = 0, len = 0; do { /* Receive response from the kernel */ if ((n = recv(fd, buf, size - len, 0)) < 0) { DEBUG_WARNING("SOCK READ: %m\n", errno); return -1; } nlhdr = (struct nlmsghdr *)(void *)buf; /* Check if the header is valid */ if (0 == NLMSG_OK(nlhdr, (uint32_t)n) || NLMSG_ERROR == nlhdr->nlmsg_type) { DEBUG_WARNING("Error in received packet\n"); return -1; } /* Check if the its the last message */ if (NLMSG_DONE == nlhdr->nlmsg_type) { break; } else{ /* Else move the pointer to buffer appropriately */ buf += n; len += n; } /* Check if its a multi part message */ if (0 == (nlhdr->nlmsg_flags & NLM_F_MULTI)) { /* return if its not */ break; } } while (nlhdr->nlmsg_seq != (uint32_t)seq || nlhdr->nlmsg_pid != (uint32_t)pid); return len; } /* Parse one route */ static int rt_parse(const struct nlmsghdr *nlhdr, struct net_rt *rt) { struct rtmsg *rtmsg; struct rtattr *rtattr; int len; rtmsg = (struct rtmsg *)NLMSG_DATA(nlhdr); /* If the route does not belong to main routing table then return. */ if (RT_TABLE_MAIN != rtmsg->rtm_table) return EINVAL; sa_init(&rt->dst, rtmsg->rtm_family); rt->dstlen = rtmsg->rtm_dst_len; /* get the rtattr field */ rtattr = (struct rtattr *)RTM_RTA(rtmsg); len = RTM_PAYLOAD(nlhdr); for (;RTA_OK(rtattr, len); rtattr = RTA_NEXT(rtattr, len)) { switch (rtattr->rta_type) { case RTA_OIF: if_indextoname(*(int *)RTA_DATA(rtattr), rt->ifname); break; case RTA_GATEWAY: switch (rtmsg->rtm_family) { case AF_INET: sa_init(&rt->gw, AF_INET); rt->gw.u.in.sin_addr.s_addr = *(uint32_t *)RTA_DATA(rtattr); break; #ifdef HAVE_INET6 case AF_INET6: sa_set_in6(&rt->gw, RTA_DATA(rtattr), 0); break; #endif default: DEBUG_WARNING("RTA_DST: unknown family %d\n", rtmsg->rtm_family); break; } break; #if 0 case RTA_PREFSRC: rt->srcaddr = *(uint32_t *)RTA_DATA(rtattr); break; #endif case RTA_DST: switch (rtmsg->rtm_family) { case AF_INET: sa_init(&rt->dst, AF_INET); rt->dst.u.in.sin_addr.s_addr = *(uint32_t *)RTA_DATA(rtattr); break; #ifdef HAVE_INET6 case AF_INET6: sa_set_in6(&rt->dst, RTA_DATA(rtattr), 0); break; #endif default: DEBUG_WARNING("RTA_DST: unknown family %d\n", rtmsg->rtm_family); break; } break; } } return 0; } /** * List all entries in the routing table * * @param rth Route entry handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int net_rt_list(net_rt_h *rth, void *arg) { union { uint8_t buf[BUFSIZE]; struct nlmsghdr msg[1]; } u; struct nlmsghdr *nlmsg; int sock, len, seq = 0, err = 0; if (!rth) return EINVAL; /* Create Socket */ if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { DEBUG_WARNING("list: socket(): (%m)\n", errno); return errno; } /* Initialize the buffer */ memset(u.buf, 0, sizeof(u.buf)); /* point the header and the msg structure pointers into the buffer */ nlmsg = u.msg; /* Fill in the nlmsg header*/ nlmsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); nlmsg->nlmsg_type = RTM_GETROUTE; nlmsg->nlmsg_flags = NLM_F_DUMP | NLM_F_REQUEST; nlmsg->nlmsg_seq = seq++; nlmsg->nlmsg_pid = getpid(); /* Send the request */ if (send(sock, nlmsg, nlmsg->nlmsg_len, 0) < 0) { err = errno; DEBUG_WARNING("list: write to socket failed (%m)\n", err); goto out; } /* Read the response */ if ((len = read_sock(sock, u.buf, sizeof(u.buf), seq, getpid())) < 0) { err = errno; DEBUG_WARNING("list: read from socket failed (%m)\n", err); goto out; } /* Parse and print the response */ for (;NLMSG_OK(nlmsg,(uint32_t)len);nlmsg = NLMSG_NEXT(nlmsg,len)) { struct net_rt rt; memset(&rt, 0, sizeof(struct net_rt)); if (0 != rt_parse(nlmsg, &rt)) continue; #ifdef HAVE_INET6 if (AF_INET6 == sa_af(&rt.dst) && IN6_IS_ADDR_UNSPECIFIED(&rt.dst.u.in6.sin6_addr)) continue; #endif if (rth(rt.ifname, &rt.dst, rt.dstlen, &rt.gw, arg)) break; } out: (void)close(sock); return err; } re-1.1.0/src/net/mod.mk000066400000000000000000000010541373627245400146210ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # # Generic files SRCS += net/if.c SRCS += net/net.c SRCS += net/netstr.c SRCS += net/rt.c SRCS += net/sock.c SRCS += net/sockopt.c # Platform dependant files ifneq ($(OS),win32) SRCS += net/posix/pif.c else SRCS += net/win32/wif.c endif # Routing ifeq ($(OS),linux) SRCS += net/linux/rt.c CFLAGS += -DHAVE_ROUTE_LIST else ifneq ($(HAVE_SYS_SYSCTL_H),) ifneq ($(HAVE_NET_ROUTE_H),) SRCS += net/bsd/brt.c CFLAGS += -DHAVE_ROUTE_LIST endif endif endif ifdef HAVE_GETIFADDRS SRCS += net/ifaddrs.c endif re-1.1.0/src/net/net.c000066400000000000000000000054641373627245400144540ustar00rootroot00000000000000/** * @file net.c Networking code. * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #if !defined(WIN32) #define __USE_BSD 1 /**< Use BSD code */ #include #include #endif #include #include #include #include #include #define DEBUG_MODULE "net" #define DEBUG_LEVEL 5 #include /** * Get the IP address of the host * * @param af Address Family * @param ip Returned IP address * * @return 0 if success, otherwise errorcode */ int net_hostaddr(int af, struct sa *ip) { char hostname[256]; struct in_addr in; struct hostent *he; if (-1 == gethostname(hostname, sizeof(hostname))) return errno; he = gethostbyname(hostname); if (!he) return ENOENT; if (af != he->h_addrtype) return EAFNOSUPPORT; /* Get the first entry */ memcpy(&in, he->h_addr_list[0], sizeof(in)); sa_set_in(ip, ntohl(in.s_addr), 0); return 0; } /** * Get the default source IP address * * @param af Address Family * @param ip Returned IP address * * @return 0 if success, otherwise errorcode */ int net_default_source_addr_get(int af, struct sa *ip) { #if defined(WIN32) return net_hostaddr(af, ip); #else char ifname[64] = ""; #ifdef HAVE_ROUTE_LIST /* Get interface with default route */ (void)net_rt_default_get(af, ifname, sizeof(ifname)); #endif /* First try with default interface */ if (0 == net_if_getaddr(ifname, af, ip)) return 0; /* Then try first real IP */ if (0 == net_if_getaddr(NULL, af, ip)) return 0; return net_if_getaddr4(ifname, af, ip); #endif } /** * Get a list of all network interfaces including name and IP address. * Both IPv4 and IPv6 are supported * * @param ifh Interface handler, called once per network interface * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int net_if_apply(net_ifaddr_h *ifh, void *arg) { #ifdef HAVE_GETIFADDRS return net_getifaddrs(ifh, arg); #else return net_if_list(ifh, arg); #endif } static bool net_rt_handler(const char *ifname, const struct sa *dst, int dstlen, const struct sa *gw, void *arg) { void **argv = arg; struct sa *ip = argv[1]; (void)dst; (void)dstlen; if (0 == str_cmp(ifname, argv[0])) { *ip = *gw; return true; } return false; } /** * Get the IP-address of the default gateway * * @param af Address Family * @param gw Returned Gateway address * * @return 0 if success, otherwise errorcode */ int net_default_gateway_get(int af, struct sa *gw) { char ifname[64]; void *argv[2]; int err; if (!af || !gw) return EINVAL; err = net_rt_default_get(af, ifname, sizeof(ifname)); if (err) return err; argv[0] = ifname; argv[1] = gw; err = net_rt_list(net_rt_handler, argv); if (err) return err; return 0; } re-1.1.0/src/net/netstr.c000066400000000000000000000014641373627245400152010ustar00rootroot00000000000000/** * @file netstr.c Network strings * * Copyright (C) 2010 Creytiv.com */ #include #include #include /** * Get the name of a protocol * * @param proto Protocol * * @return Protocol name */ const char *net_proto2name(int proto) { switch (proto) { case IPPROTO_UDP: return "UDP"; case IPPROTO_TCP: return "TCP"; #ifdef IPPROTO_SCTP case IPPROTO_SCTP: return "SCTP"; #endif default: return "???"; } } /** * Get the name of a address family * * @param af Address family * * @return Address family name */ const char *net_af2name(int af) { switch (af) { case AF_UNSPEC: return "AF_UNSPEC"; case AF_INET: return "AF_INET"; #ifdef HAVE_INET6 case AF_INET6: return "AF_INET6"; #endif default: return "???"; } } re-1.1.0/src/net/posix/000077500000000000000000000000001373627245400146535ustar00rootroot00000000000000re-1.1.0/src/net/posix/pif.c000066400000000000000000000065011373627245400155770ustar00rootroot00000000000000/** * @file posix/pif.c POSIX network interface code * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #define __USE_POSIX 1 /**< Use POSIX code */ #define __USE_XOPEN2K 1/**< Use POSIX.1:2001 code */ #include #define __USE_MISC 1 /**< Use MISC code */ #include #include /*#include */ #ifdef __sun #include #endif #include #include #include #include #include #define DEBUG_MODULE "posixif" #define DEBUG_LEVEL 5 #include /** * Get IP address for a given network interface * * @param ifname Network interface name * @param af Address Family * @param ip Returned IP address * * @return 0 if success, otherwise errorcode * * @deprecated Works for IPv4 only */ int net_if_getaddr4(const char *ifname, int af, struct sa *ip) { struct addrinfo hints, *res, *r; int error, err; if (AF_INET != af) return EAFNOSUPPORT; memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_DGRAM; error = getaddrinfo(NULL, "0", &hints, &res); if (error) { DEBUG_WARNING("get_ifaddr: getaddrinfo(): %s\n", gai_strerror(error)); return EADDRNOTAVAIL; } err = ENOENT; for (r = res; r; r = r->ai_next) { struct ifreq ifrr; int fd = -1; fd = socket(r->ai_family, SOCK_DGRAM, 0); if (fd < 0) { continue; } ifrr.ifr_addr.sa_family = r->ai_family; str_ncpy(ifrr.ifr_name, ifname, sizeof(ifrr.ifr_name)); if (ioctl(fd, SIOCGIFADDR, &ifrr) < 0) { err = errno; goto next; } err = sa_set_sa(ip, &ifrr.ifr_ifru.ifru_addr); next: (void)close(fd); } freeaddrinfo(res); return err; } /** * Enumerate all network interfaces * * @param ifh Interface handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode * * @deprecated Works for IPv4 only */ int net_if_list(net_ifaddr_h *ifh, void *arg) { struct ifreq ifrv[32], *ifr; struct ifconf ifc; int sockfd = -1; int err = 0; if (0 > (sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP))) { err = errno; DEBUG_WARNING("interface list: socket(): (%m)\n", err); goto out; } ifc.ifc_len = sizeof(ifrv); ifc.ifc_req = ifrv; if (0 != ioctl(sockfd, SIOCGIFCONF, &ifc)) { err = errno; DEBUG_WARNING("interface list: ioctl SIOCFIFCONF: %m\n", err); goto out; } for (ifr = ifc.ifc_req; (char *)ifr < ((char *)ifc.ifc_buf + ifc.ifc_len); ++ifr) { struct ifreq ifrr; struct sa sa; if (ifr->ifr_addr.sa_data == (ifr+1)->ifr_addr.sa_data) continue; /* duplicate, skip it */ if (ioctl(sockfd, SIOCGIFFLAGS, ifr)) continue; /* failed to get flags, skip it */ #if 0 if (ifr->ifr_flags & IFF_LOOPBACK) continue; #endif if (!(ifr->ifr_flags & IFF_UP)) continue; ifrr.ifr_addr.sa_family = AF_INET; str_ncpy(ifrr.ifr_name, ifr->ifr_name, sizeof(ifrr.ifr_name)); if (ioctl(sockfd, SIOCGIFADDR, &ifrr) < 0) { err = errno; continue; } err = sa_set_sa(&sa, &ifrr.ifr_ifru.ifru_addr); if (err) { DEBUG_WARNING("if_list: sa_set_sa %m\n", err); break; } if (ifh && ifh(ifr->ifr_name, &sa, arg)) break; } out: if (sockfd >= 0) (void)close(sockfd); return err; } re-1.1.0/src/net/rt.c000066400000000000000000000056371373627245400143150ustar00rootroot00000000000000/** * @file net/rt.c Generic routing table code * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #include #include #include struct net_rt { int af; char *ifname; size_t size; int prefix; }; static bool rt_debug_handler(const char *ifname, const struct sa *dst, int dstlen, const struct sa *gw, void *arg) { char addr[64]; struct re_printf *pf = arg; int err = 0; (void)re_snprintf(addr, sizeof(addr), "%j/%d", dst, dstlen); err |= re_hprintf(pf, " %-44s", addr); err |= re_hprintf(pf, "%-40j", gw); err |= re_hprintf(pf, " %-15s ", ifname); #ifdef HAVE_INET6 if (AF_INET6 == sa_af(dst)) { const struct sockaddr_in6 *sin6 = &dst->u.in6; const struct in6_addr *in6 = &sin6->sin6_addr; if (IN6_IS_ADDR_MULTICAST(in6)) err |= re_hprintf(pf, " MULTICAST"); if (IN6_IS_ADDR_LINKLOCAL(in6)) err |= re_hprintf(pf, " LINKLOCAL"); if (IN6_IS_ADDR_SITELOCAL(in6)) err |= re_hprintf(pf, " SITELOCAL"); } #endif err |= re_hprintf(pf, "\n"); return 0 != err; } /** * Dump the routing table * * @param pf Print function for output * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int net_rt_debug(struct re_printf *pf, void *unused) { int err = 0; (void)unused; err |= re_hprintf(pf, "net routes:\n"); err |= re_hprintf(pf, " Destination " "Next Hop" " Iface " "Type\n"); err |= net_rt_list(rt_debug_handler, pf); return err; } static bool rt_default_get_handler(const char *_ifname, const struct sa *dst, int dstlen, const struct sa *gw, void *arg) { struct net_rt *rt = arg; (void)dstlen; (void)gw; if (sa_af(dst) != rt->af) return false; switch (rt->af) { case AF_INET: if (0 == sa_in(dst)) { str_ncpy(rt->ifname, _ifname, rt->size); return true; } break; #ifdef HAVE_INET6 case AF_INET6: if (IN6_IS_ADDR_MULTICAST(&dst->u.in6.sin6_addr)) return false; if (IN6_IS_ADDR_LINKLOCAL(&dst->u.in6.sin6_addr)) return false; if (dstlen < rt->prefix) { rt->prefix = dstlen; str_ncpy(rt->ifname, _ifname, rt->size); return false; } break; #endif } return false; } /** * Get the interface name of the default route * * @param af Address family * @param ifname Buffer for returned interface name * @param size Size of buffer * * @return 0 if success, otherwise errorcode */ int net_rt_default_get(int af, char *ifname, size_t size) { struct net_rt rt; int err; rt.af = af; rt.ifname = ifname; rt.size = size; rt.prefix = 256; err = net_rt_list(rt_default_get_handler, &rt); if (err) return err; return '\0' != ifname[0] ? 0 : EINVAL; } #ifndef HAVE_ROUTE_LIST /* We must provide a stub */ int net_rt_list(net_rt_h *rth, void *arg) { (void)rth; (void)arg; return ENOSYS; } #endif re-1.1.0/src/net/sock.c000066400000000000000000000030501373627245400146120ustar00rootroot00000000000000/** * @file net/sock.c Networking sockets code * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #define DEBUG_MODULE "netsock" #define DEBUG_LEVEL 5 #include static bool inited = false; #ifdef WIN32 static int wsa_init(void) { WORD wVersionRequested = MAKEWORD(2, 2); WSADATA wsaData; int err; err = WSAStartup(wVersionRequested, &wsaData); if (err != 0) { DEBUG_WARNING("Could not load winsock (%m)\n", err); return err; } /* Confirm that the WinSock DLL supports 2.2.*/ /* Note that if the DLL supports versions greater */ /* than 2.2 in addition to 2.2, it will still return */ /* 2.2 in wVersion since that is the version we */ /* requested. */ if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2 ) { WSACleanup(); DEBUG_WARNING("Bad winsock verion (%d.%d)\n", HIBYTE(wsaData.wVersion), LOBYTE(wsaData.wVersion)); return EINVAL; } return 0; } #endif /** * Initialise network sockets * * @return 0 if success, otherwise errorcode */ int net_sock_init(void) { int err = 0; DEBUG_INFO("sock init: inited=%d\n", inited); if (inited) return 0; #ifdef WIN32 err = wsa_init(); #endif inited = true; return err; } /** * Cleanup network sockets */ void net_sock_close(void) { #ifdef WIN32 const int err = WSACleanup(); if (0 != err) { DEBUG_WARNING("sock close: WSACleanup (%d)\n", err); } #endif inited = false; DEBUG_INFO("sock close\n"); } re-1.1.0/src/net/sockopt.c000066400000000000000000000040221373627245400153350ustar00rootroot00000000000000/** * @file sockopt.c Networking socket options * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #define DEBUG_MODULE "sockopt" #define DEBUG_LEVEL 5 #include /** Platform independent buffer type cast */ #ifdef WIN32 #define BUF_CAST (char *) #else #define BUF_CAST #endif /** * Set socket option blocking or non-blocking * * @param fd Socket file descriptor * @param blocking true for blocking, false for non-blocking * * @return 0 if success, otherwise errorcode */ int net_sockopt_blocking_set(int fd, bool blocking) { #ifdef WIN32 unsigned long noblock = !blocking; int err = 0; if (0 != ioctlsocket(fd, FIONBIO, &noblock)) { err = WSAGetLastError(); DEBUG_WARNING("nonblock set: fd=%d err=%d (%m)\n", fd, err, err); } return err; #else int flags; int err = 0; flags = fcntl(fd, F_GETFL); if (-1 == flags) { err = errno; DEBUG_WARNING("sockopt set: fnctl F_GETFL: (%m)\n", err); goto out; } if (blocking) flags &= ~O_NONBLOCK; else flags |= O_NONBLOCK; if (-1 == fcntl(fd, F_SETFL, flags)) { err = errno; DEBUG_WARNING("sockopt set: fcntl F_SETFL non-block (%m)\n", err); } out: return err; #endif } /** * Set socket option to reuse address and port * * @param fd Socket file descriptor * @param reuse true for reuse, false for no reuse * * @return 0 if success, otherwise errorcode */ int net_sockopt_reuse_set(int fd, bool reuse) { int r = reuse; #ifdef SO_REUSEADDR if (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, BUF_CAST &r, sizeof(r))) { DEBUG_WARNING("SO_REUSEADDR: %m\n", errno); return errno; } #endif #ifdef SO_REUSEPORT if (-1 == setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, BUF_CAST &r, sizeof(r))) { DEBUG_INFO("SO_REUSEPORT: %m\n", errno); return errno; } #endif #if !defined(SO_REUSEADDR) && !defined(SO_REUSEPORT) (void)r; (void)fd; (void)reuse; return ENOSYS; #else return 0; #endif } re-1.1.0/src/net/win32/000077500000000000000000000000001373627245400144535ustar00rootroot00000000000000re-1.1.0/src/net/win32/wif.c000066400000000000000000000047621373627245400154150ustar00rootroot00000000000000/** * @file wif.c Windows network interface code * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #define DEBUG_MODULE "wif" #define DEBUG_LEVEL 5 #include /** * List interfaces using GetAdaptersAddresses, which handles both * IPv4 and IPv6 address families. * * This is available from Windows XP and Windows Server 2003 */ static int if_list_gaa(net_ifaddr_h *ifh, void *arg) { IP_ADAPTER_ADDRESSES addrv[64], *cur; ULONG ret, len = sizeof(addrv); const ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST; HANDLE hLib; union { FARPROC proc; ULONG (WINAPI *gaa)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG); } u; bool stop = false; int err = 0; hLib = LoadLibrary(TEXT("iphlpapi.dll")); if (!hLib) return ENOSYS; u.proc = GetProcAddress(hLib, TEXT("GetAdaptersAddresses")); if (!u.proc) { err = ENOSYS; goto out; } ret = (*u.gaa)(AF_UNSPEC, flags, NULL, addrv, &len); if (ret != ERROR_SUCCESS) { DEBUG_WARNING("if_list: GetAdaptersAddresses ret=%u\n", ret); err = ENODEV; goto out; } for (cur = addrv; cur && !stop; cur = cur->Next) { PIP_ADAPTER_UNICAST_ADDRESS ip; /* an interface can have many IP-addresses */ for (ip = cur->FirstUnicastAddress; ip; ip = ip->Next) { struct sa sa; sa_set_sa(&sa, ip->Address.lpSockaddr); if (ifh && ifh(cur->AdapterName, &sa, arg)) { stop = true; break; } } } out: FreeLibrary(hLib); return err; } /** * List interfaces using GetAdaptersInfo, which handles only IPv4 family. * * This is available from Windows 2000, and also works under Wine. */ static int if_list_gai(net_ifaddr_h *ifh, void *arg) { IP_ADAPTER_INFO info[32]; PIP_ADAPTER_INFO p = info; ULONG ulOutBufLen = sizeof(info); DWORD ret; ret = GetAdaptersInfo(info, &ulOutBufLen); if (ret != ERROR_SUCCESS) { DEBUG_WARNING("if_list: GetAdaptersInfo ret=%u\n", ret); return ENODEV; } for (p = info; p; p = p->Next) { struct sa sa; if (sa_set_str(&sa, p->IpAddressList.IpAddress.String, 0)) continue; if (ifh && ifh(p->AdapterName, &sa, arg)) break; } return 0; } /** * Enumerate all network interfaces * * @param ifh Interface handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int net_if_list(net_ifaddr_h *ifh, void *arg) { /* Try both methods .. */ if (!if_list_gaa(ifh, arg)) return 0; return if_list_gai(ifh, arg); } re-1.1.0/src/odict/000077500000000000000000000000001373627245400140255ustar00rootroot00000000000000re-1.1.0/src/odict/entry.c000066400000000000000000000043101373627245400153300ustar00rootroot00000000000000/** * @file odict/entry.c Ordered Dictionary -- entry * * Copyright (C) 2010 - 2015 Creytiv.com */ #include "re_types.h" #include "re_fmt.h" #include "re_mem.h" #include "re_list.h" #include "re_hash.h" #include "re_odict.h" static void destructor(void *arg) { struct odict_entry *e = arg; switch (e->type) { case ODICT_OBJECT: case ODICT_ARRAY: mem_deref(e->u.odict); break; case ODICT_STRING: mem_deref(e->u.str); break; default: break; } hash_unlink(&e->he); list_unlink(&e->le); mem_deref(e->key); } int odict_entry_add(struct odict *o, const char *key, int type, ...) { struct odict_entry *e; va_list ap; int err; if (!o || !key) return EINVAL; e = mem_zalloc(sizeof(*e), destructor); if (!e) return ENOMEM; e->type = type; err = str_dup(&e->key, key); if (err) goto out; va_start(ap, type); switch (e->type) { case ODICT_OBJECT: case ODICT_ARRAY: e->u.odict = mem_ref(va_arg(ap, struct odict *)); break; case ODICT_STRING: err = str_dup(&e->u.str, va_arg(ap, const char *)); break; case ODICT_INT: e->u.integer = va_arg(ap, int64_t); break; case ODICT_DOUBLE: e->u.dbl = va_arg(ap, double); break; case ODICT_BOOL: e->u.boolean = va_arg(ap, int); break; case ODICT_NULL: break; default: err = EINVAL; break; } va_end(ap); if (err) goto out; list_append(&o->lst, &e->le, e); hash_append(o->ht, hash_fast_str(e->key), &e->he, e); out: if (err) mem_deref(e); return err; } void odict_entry_del(struct odict *o, const char *key) { mem_deref((struct odict_entry *)odict_lookup(o, key)); } int odict_entry_debug(struct re_printf *pf, const struct odict_entry *e) { int err; if (!e) return 0; err = re_hprintf(pf, "%s", e->key); switch (e->type) { case ODICT_OBJECT: case ODICT_ARRAY: err |= re_hprintf(pf, ":%H", odict_debug, e->u.odict); break; case ODICT_STRING: err |= re_hprintf(pf, ":%s", e->u.str); break; case ODICT_INT: err |= re_hprintf(pf, ":%lli", e->u.integer); break; case ODICT_DOUBLE: err |= re_hprintf(pf, ":%f", e->u.dbl); break; case ODICT_BOOL: err |= re_hprintf(pf, ":%s", e->u.boolean ? "true" : "false"); break; case ODICT_NULL: break; } return err; } re-1.1.0/src/odict/get.c000066400000000000000000000025761373627245400147620ustar00rootroot00000000000000/** * @file get.c Ordered Dictionary -- high level accessors * * Copyright (C) 2010 Creytiv.com */ #include "re_types.h" #include "re_fmt.h" #include "re_mem.h" #include "re_list.h" #include "re_hash.h" #include "re_odict.h" const struct odict_entry *odict_get_type(const struct odict *o, enum odict_type type, const char *key) { const struct odict_entry *entry; if (!o || !key) return NULL; entry = odict_lookup(o, key); if (!entry) return NULL; if (entry->type != type) return NULL; return entry; } const char *odict_string(const struct odict *o, const char *key) { const struct odict_entry *entry; entry = odict_get_type(o, ODICT_STRING, key); if (!entry) return NULL; return entry->u.str; } bool odict_get_number(const struct odict *o, uint64_t *num, const char *key) { const struct odict_entry *entry; if (!o || !key) return false; entry = odict_lookup(o, key); if (!entry) return false; switch (entry->type) { case ODICT_DOUBLE: if (num) *num = (uint64_t)entry->u.dbl; break; case ODICT_INT: if (num) *num = entry->u.integer; break; default: return false; } return true; } bool odict_get_boolean(const struct odict *o, bool *value, const char *key) { const struct odict_entry *entry; entry = odict_get_type(o, ODICT_BOOL, key); if (!entry) return false; if (value) *value = entry->u.boolean; return true; } re-1.1.0/src/odict/mod.mk000066400000000000000000000002151373627245400151330ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 - 2015 Creytiv.com # SRCS += odict/entry.c SRCS += odict/odict.c SRCS += odict/type.c SRCS += odict/get.c re-1.1.0/src/odict/odict.c000066400000000000000000000033471373627245400153020ustar00rootroot00000000000000/** * @file odict.c Ordered Dictionary * * Copyright (C) 2010 Creytiv.com */ #include "re_types.h" #include "re_fmt.h" #include "re_mem.h" #include "re_list.h" #include "re_hash.h" #include "re_odict.h" static void destructor(void *arg) { struct odict *o = arg; hash_clear(o->ht); list_flush(&o->lst); mem_deref(o->ht); } int odict_alloc(struct odict **op, uint32_t hash_size) { struct odict *o; int err; if (!op || !hash_size) return EINVAL; o = mem_zalloc(sizeof(*o), destructor); if (!o) return ENOMEM; err = hash_alloc(&o->ht, hash_valid_size(hash_size)); if (err) goto out; out: if (err) mem_deref(o); else *op = o; return err; } const struct odict_entry *odict_lookup(const struct odict *o, const char *key) { struct le *le; if (!o || !key) return NULL; le = list_head(hash_list(o->ht, hash_fast_str(key))); while (le) { const struct odict_entry *e = le->data; if (!str_cmp(e->key, key)) return e; le = le->next; } return NULL; } size_t odict_count(const struct odict *o, bool nested) { struct le *le; size_t n = 0; if (!o) return 0; if (!nested) return list_count(&o->lst); for (le=o->lst.head; le; le=le->next) { const struct odict_entry *e = le->data; switch (e->type) { case ODICT_OBJECT: case ODICT_ARRAY: n += odict_count(e->u.odict, true); break; default: n += 1; /* count all entries */ break; } } return n; } int odict_debug(struct re_printf *pf, const struct odict *o) { struct le *le; int err; if (!o) return 0; err = re_hprintf(pf, "{"); for (le=o->lst.head; le; le=le->next) { const struct odict_entry *e = le->data; err |= re_hprintf(pf, " %H", odict_entry_debug, e); } err |= re_hprintf(pf, " }"); return err; } re-1.1.0/src/odict/type.c000066400000000000000000000017041373627245400151540ustar00rootroot00000000000000/** * @file type.c Ordered Dictionary -- value types * * Copyright (C) 2010 - 2015 Creytiv.com */ #include "re_types.h" #include "re_fmt.h" #include "re_mem.h" #include "re_list.h" #include "re_hash.h" #include "re_odict.h" bool odict_type_iscontainer(enum odict_type type) { switch (type) { case ODICT_OBJECT: case ODICT_ARRAY: return true; default: return false; } } bool odict_type_isreal(enum odict_type type) { switch (type) { case ODICT_STRING: case ODICT_INT: case ODICT_DOUBLE: case ODICT_BOOL: case ODICT_NULL: return true; default: return false; } } const char *odict_type_name(enum odict_type type) { switch (type) { case ODICT_OBJECT: return "Object"; case ODICT_ARRAY: return "Array"; case ODICT_STRING: return "String"; case ODICT_INT: return "Integer"; case ODICT_DOUBLE: return "Double"; case ODICT_BOOL: return "Boolean"; case ODICT_NULL: return "Null"; default: return "???"; } } re-1.1.0/src/rtmp/000077500000000000000000000000001373627245400137055ustar00rootroot00000000000000re-1.1.0/src/rtmp/README.md000066400000000000000000000063301373627245400151660ustar00rootroot00000000000000RTMP module ----------- This module implements Real Time Messaging Protocol (RTMP) [1]. Functional overview: ------------------- ``` RTMP Specification v1.0 .......... YES RTMP with TCP transport .......... YES RTMPS (RTMP over TLS) ............ NO RTMPE (RTMP over Adobe Encryption) NO RTMPT (RTMP over HTTP) ........... NO RTMFP (RTMP over UDP) ............ NO Transport: Client ........................... YES Server ........................... YES IPv4 ............................. YES IPv6 ............................. YES DNS Resolving A/AAAA ............. YES RTMP Components: RTMP Handshake ................... YES RTMP Header encoding and decoding. YES RTMP Chunking .................... YES RTMP Dechunking .................. YES AMF0 (Action Message Format) ..... YES AMF3 (Action Message Format) ..... NO Send and receive audio/video ..... YES Regular and extended timestamp ... YES Multiple streams ................. YES ``` TODO: ---- - [x] improve AMF encoding API - [x] implement AMF transaction matching - [x] add support for Data Message - [x] add support for AMF Strict Array (type 10) - [ ] add support for TLS encryption - [x] add support for extended timestamp Protocol stack: -------------- .-------. .-------. .-------. | AMF | | Audio | | Video | '-------' '-------' '-------' | | | +----------+----------' | .-------. | RTMP | '-------' | | .-------. | TCP | '-------' Message Sequence: ---------------- ``` Client Server |----------------- TCP Connect -------------->| | | | | | | |<-------------- 3-way Handshake ------------>| | | | | | | |----------- Command Message(connect) ------->| chunkid=3, streamid=0, tid=1 | | |<------- Window Acknowledgement Size --------| chunkid=2, streamid=0 | | |<----------- Set Peer Bandwidth -------------| chunkid=2, streamid=0 | | |-------- Window Acknowledgement Size ------->| | | |<------ User Control Message(StreamBegin) ---| chunkid=2, streamid=0 | | |<------------ Command Message ---------------| chunkid=3, streamid=0, tid=1 | (_result- connect response) | ``` Interop: ------- - Wowza Streaming Engine 4.7.1 - Youtube service - FFmpeg's RTMP module References: ---------- [1] http://wwwimages.adobe.com/www.adobe.com/content/dam/acom/en/devnet/rtmp/pdf/rtmp_specification_1.0.pdf [2] https://wwwimages2.adobe.com/content/dam/acom/en/devnet/flv/video_file_format_spec_v10_1.pdf [3] https://en.wikipedia.org/wiki/Action_Message_Format [4] https://wwwimages2.adobe.com/content/dam/acom/en/devnet/pdf/amf0-file-format-specification.pdf re-1.1.0/src/rtmp/amf.c000066400000000000000000000054041373627245400146170ustar00rootroot00000000000000/** * @file rtmp/amf.c Real Time Messaging Protocol (RTMP) -- AMF Commands * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" int rtmp_command_header_encode(struct mbuf *mb, const char *name, uint64_t tid) { int err; if (!mb || !name) return EINVAL; err = rtmp_amf_encode_string(mb, name); err |= rtmp_amf_encode_number(mb, tid); return err; } int rtmp_amf_command(const struct rtmp_conn *conn, uint32_t stream_id, const char *command, unsigned body_propc, ...) { struct mbuf *mb; va_list ap; int err; if (!conn || !command) return EINVAL; mb = mbuf_alloc(512); if (!mb) return ENOMEM; err = rtmp_amf_encode_string(mb, command); if (err) goto out; if (body_propc) { va_start(ap, body_propc); err = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT, body_propc, &ap); va_end(ap); if (err) goto out; } err = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN, RTMP_TYPE_AMF0, stream_id, mb->buf, mb->end); if (err) goto out; out: mem_deref(mb); return err; } int rtmp_amf_reply(struct rtmp_conn *conn, uint32_t stream_id, bool success, const struct odict *req, unsigned body_propc, ...) { struct mbuf *mb; va_list ap; uint64_t tid; int err; if (!conn || !req) return EINVAL; if (!odict_get_number(req, &tid, "1")) return EPROTO; if (tid == 0) return EPROTO; mb = mbuf_alloc(512); if (!mb) return ENOMEM; err = rtmp_command_header_encode(mb, success ? "_result" : "_error", tid); if (err) goto out; if (body_propc) { va_start(ap, body_propc); err = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT, body_propc, &ap); va_end(ap); if (err) goto out; } err = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN, RTMP_TYPE_AMF0, stream_id, mb->buf, mb->end); if (err) goto out; out: mem_deref(mb); return err; } int rtmp_amf_data(const struct rtmp_conn *conn, uint32_t stream_id, const char *command, unsigned body_propc, ...) { struct mbuf *mb; va_list ap; int err; if (!conn || !command) return EINVAL; mb = mbuf_alloc(512); if (!mb) return ENOMEM; err = rtmp_amf_encode_string(mb, command); if (err) goto out; if (body_propc) { va_start(ap, body_propc); err = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT, body_propc, &ap); va_end(ap); if (err) goto out; } err = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN, RTMP_TYPE_DATA, stream_id, mb->buf, mb->end); if (err) goto out; out: mem_deref(mb); return err; } re-1.1.0/src/rtmp/amf_dec.c000066400000000000000000000073631373627245400154400ustar00rootroot00000000000000/** * @file rtmp/amf_dec.c Real Time Messaging Protocol (RTMP) -- AMF Decoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" enum { AMF_HASH_SIZE = 32 }; static int amf_decode_value(struct odict *dict, const char *key, struct mbuf *mb); static int amf_decode_object(struct odict *dict, struct mbuf *mb) { char *key = NULL; uint16_t len; int err = 0; while (mbuf_get_left(mb) > 0) { if (mbuf_get_left(mb) < 2) return ENODATA; len = ntohs(mbuf_read_u16(mb)); if (len == 0) { uint8_t val; if (mbuf_get_left(mb) < 1) return ENODATA; val = mbuf_read_u8(mb); if (val == RTMP_AMF_TYPE_OBJECT_END) return 0; else return EBADMSG; } if (mbuf_get_left(mb) < len) return ENODATA; err = mbuf_strdup(mb, &key, len); if (err) return err; err = amf_decode_value(dict, key, mb); key = mem_deref(key); if (err) return err; } return 0; } static int amf_decode_value(struct odict *dict, const char *key, struct mbuf *mb) { union { uint64_t i; double f; } num; struct odict *object = NULL; char *str = NULL; uint32_t i, array_len; uint8_t type; uint16_t len; bool boolean; int err = 0; if (mbuf_get_left(mb) < 1) return ENODATA; type = mbuf_read_u8(mb); switch (type) { case RTMP_AMF_TYPE_NUMBER: if (mbuf_get_left(mb) < 8) return ENODATA; num.i = sys_ntohll(mbuf_read_u64(mb)); err = odict_entry_add(dict, key, ODICT_DOUBLE, num.f); break; case RTMP_AMF_TYPE_BOOLEAN: if (mbuf_get_left(mb) < 1) return ENODATA; boolean = !!mbuf_read_u8(mb); err = odict_entry_add(dict, key, ODICT_BOOL, boolean); break; case RTMP_AMF_TYPE_STRING: if (mbuf_get_left(mb) < 2) return ENODATA; len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) return ENODATA; err = mbuf_strdup(mb, &str, len); if (err) return err; err = odict_entry_add(dict, key, ODICT_STRING, str); mem_deref(str); break; case RTMP_AMF_TYPE_NULL: err = odict_entry_add(dict, key, ODICT_NULL); break; case RTMP_AMF_TYPE_ECMA_ARRAY: if (mbuf_get_left(mb) < 4) return ENODATA; array_len = ntohl(mbuf_read_u32(mb)); (void)array_len; /* ignore array length */ /* fallthrough */ case RTMP_AMF_TYPE_OBJECT: err = odict_alloc(&object, 32); if (err) return err; err = amf_decode_object(object, mb); if (err) { mem_deref(object); return err; } err = odict_entry_add(dict, key, ODICT_OBJECT, object); mem_deref(object); break; case RTMP_AMF_TYPE_STRICT_ARRAY: if (mbuf_get_left(mb) < 4) return ENODATA; array_len = ntohl(mbuf_read_u32(mb)); if (!array_len) return EPROTO; err = odict_alloc(&object, 32); if (err) return err; for (i=0; i 0) { char key[16]; re_snprintf(key, sizeof(key), "%u", ix++); /* note: key is the numerical index */ err = amf_decode_value(msg, key, mb); if (err) goto out; } out: if (err) mem_deref(msg); else *msgp = msg; return err; } re-1.1.0/src/rtmp/amf_enc.c000066400000000000000000000101541373627245400154420ustar00rootroot00000000000000/** * @file rtmp/amf_enc.c Real Time Messaging Protocol (RTMP) -- AMF Encoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" static int rtmp_amf_encode_key(struct mbuf *mb, const char *key) { size_t len; int err; len = str_len(key); if (len > 65535) return EOVERFLOW; err = mbuf_write_u16(mb, htons((uint16_t)len)); err |= mbuf_write_str(mb, key); return err; } static int rtmp_amf_encode_object_start(struct mbuf *mb) { return mbuf_write_u8(mb, RTMP_AMF_TYPE_OBJECT); } static int rtmp_amf_encode_array_start(struct mbuf *mb, uint8_t type, uint32_t length) { int err; err = mbuf_write_u8(mb, type); err |= mbuf_write_u32(mb, htonl(length)); return err; } static int rtmp_amf_encode_object_end(struct mbuf *mb) { int err; err = mbuf_write_u16(mb, 0); err |= mbuf_write_u8(mb, RTMP_AMF_TYPE_OBJECT_END); return err; } static bool container_has_key(enum rtmp_amf_type type) { switch (type) { case RTMP_AMF_TYPE_OBJECT: return true; case RTMP_AMF_TYPE_ECMA_ARRAY: return true; case RTMP_AMF_TYPE_STRICT_ARRAY: return false; default: return false; } } int rtmp_amf_encode_number(struct mbuf *mb, double val) { const union { uint64_t i; double f; } num = { .f = val }; int err; if (!mb) return EINVAL; err = mbuf_write_u8(mb, RTMP_AMF_TYPE_NUMBER); err |= mbuf_write_u64(mb, sys_htonll(num.i)); return err; } int rtmp_amf_encode_boolean(struct mbuf *mb, bool boolean) { int err; if (!mb) return EINVAL; err = mbuf_write_u8(mb, RTMP_AMF_TYPE_BOOLEAN); err |= mbuf_write_u8(mb, !!boolean); return err; } int rtmp_amf_encode_string(struct mbuf *mb, const char *str) { size_t len; int err; if (!mb || !str) return EINVAL; len = str_len(str); if (len > 65535) return EOVERFLOW; err = mbuf_write_u8(mb, RTMP_AMF_TYPE_STRING); err |= mbuf_write_u16(mb, htons((uint16_t)len)); err |= mbuf_write_str(mb, str); return err; } int rtmp_amf_encode_null(struct mbuf *mb) { if (!mb) return EINVAL; return mbuf_write_u8(mb, RTMP_AMF_TYPE_NULL); } /* * NUMBER double * BOOLEAN bool * STRING const char * * OBJECT const char *key sub-count * NULL NULL * ARRAY const char *key sub-count */ int rtmp_amf_vencode_object(struct mbuf *mb, enum rtmp_amf_type container, unsigned propc, va_list *ap) { bool encode_key; unsigned i; int err = 0; if (!mb || !propc || !ap) return EINVAL; encode_key = container_has_key(container); switch (container) { case RTMP_AMF_TYPE_OBJECT: err = rtmp_amf_encode_object_start(mb); break; case RTMP_AMF_TYPE_ECMA_ARRAY: case RTMP_AMF_TYPE_STRICT_ARRAY: err = rtmp_amf_encode_array_start(mb, container, propc); break; case RTMP_AMF_TYPE_ROOT: break; default: return ENOTSUP; } if (err) return err; for (i=0; i #include #include #include #include #include #include #include #include #include #include "rtmp.h" /* * Stateless RTMP chunker */ int rtmp_chunker(unsigned format, uint32_t chunk_id, uint32_t timestamp, uint32_t timestamp_delta, uint8_t msg_type_id, uint32_t msg_stream_id, const uint8_t *payload, size_t payload_len, size_t max_chunk_sz, struct tcp_conn *tc) { const uint8_t *pend = payload + payload_len; struct rtmp_header hdr; struct mbuf *mb; size_t chunk_sz; int err; if (!payload || !payload_len || !max_chunk_sz || !tc) return EINVAL; mb = mbuf_alloc(payload_len + 256); if (!mb) return ENOMEM; memset(&hdr, 0, sizeof(hdr)); hdr.format = format; hdr.chunk_id = chunk_id; hdr.timestamp = timestamp; hdr.timestamp_delta = timestamp_delta; hdr.length = (uint32_t)payload_len; hdr.type_id = msg_type_id; hdr.stream_id = msg_stream_id; chunk_sz = min(payload_len, max_chunk_sz); err = rtmp_header_encode(mb, &hdr); err |= mbuf_write_mem(mb, payload, chunk_sz); if (err) goto out; payload += chunk_sz; hdr.format = 3; while (payload < pend) { const size_t len = pend - payload; chunk_sz = min(len, max_chunk_sz); err = rtmp_header_encode(mb, &hdr); err |= mbuf_write_mem(mb, payload, chunk_sz); if (err) goto out; payload += chunk_sz; } mb->pos = 0; err = tcp_send(tc, mb); if (err) goto out; out: mem_deref(mb); return err; } re-1.1.0/src/rtmp/conn.c000066400000000000000000000503561373627245400150170ustar00rootroot00000000000000/** * @file rtmp/conn.c Real Time Messaging Protocol (RTMP) -- NetConnection * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" enum { WINDOW_ACK_SIZE = 2500000 }; static int req_connect(struct rtmp_conn *conn); static void conn_destructor(void *data) { struct rtmp_conn *conn = data; list_flush(&conn->ctransl); list_flush(&conn->streaml); mem_deref(conn->dnsq6); mem_deref(conn->dnsq4); mem_deref(conn->dnsc); mem_deref(conn->sc); mem_deref(conn->tc); mem_deref(conn->mb); mem_deref(conn->dechunk); mem_deref(conn->uri); mem_deref(conn->app); mem_deref(conn->host); mem_deref(conn->stream); } static int handle_amf_command(struct rtmp_conn *conn, uint32_t stream_id, struct mbuf *mb) { struct odict *msg = NULL; const char *name; int err; err = rtmp_amf_decode(&msg, mb); if (err) return err; name = odict_string(msg, "0"); if (conn->is_client && (0 == str_casecmp(name, "_result") || 0 == str_casecmp(name, "_error"))) { /* forward response to transaction layer */ rtmp_ctrans_response(&conn->ctransl, msg); } else { struct rtmp_stream *strm; if (stream_id == 0) { if (conn->cmdh) conn->cmdh(msg, conn->arg); } else { strm = rtmp_stream_find(conn, stream_id); if (strm) { if (strm->cmdh) strm->cmdh(msg, strm->arg); } } } mem_deref(msg); return 0; } static int handle_user_control_msg(struct rtmp_conn *conn, struct mbuf *mb) { struct rtmp_stream *strm; enum rtmp_event_type event; uint32_t value; int err; if (mbuf_get_left(mb) < 6) return EBADMSG; event = ntohs(mbuf_read_u16(mb)); value = ntohl(mbuf_read_u32(mb)); switch (event) { case RTMP_EVENT_STREAM_BEGIN: case RTMP_EVENT_STREAM_EOF: case RTMP_EVENT_STREAM_DRY: case RTMP_EVENT_STREAM_IS_RECORDED: case RTMP_EVENT_SET_BUFFER_LENGTH: if (value != RTMP_CONTROL_STREAM_ID) { strm = rtmp_stream_find(conn, value); if (strm && strm->ctrlh) strm->ctrlh(event, mb, strm->arg); } break; case RTMP_EVENT_PING_REQUEST: err = rtmp_control(conn, RTMP_TYPE_USER_CONTROL_MSG, RTMP_EVENT_PING_RESPONSE, value); if (err) return err; break; default: break; } return 0; } static int handle_data_message(struct rtmp_conn *conn, uint32_t stream_id, struct mbuf *mb) { struct rtmp_stream *strm; struct odict *msg; int err; err = rtmp_amf_decode(&msg, mb); if (err) return err; strm = rtmp_stream_find(conn, stream_id); if (strm && strm->datah) strm->datah(msg, strm->arg); mem_deref(msg); return 0; } static int rtmp_dechunk_handler(const struct rtmp_header *hdr, struct mbuf *mb, void *arg) { struct rtmp_conn *conn = arg; struct rtmp_stream *strm; uint32_t val; uint32_t was; uint8_t limit; int err = 0; switch (hdr->type_id) { case RTMP_TYPE_SET_CHUNK_SIZE: if (mbuf_get_left(mb) < 4) return EBADMSG; val = ntohl(mbuf_read_u32(mb)); val = val & 0x7fffffff; rtmp_dechunker_set_chunksize(conn->dechunk, val); break; case RTMP_TYPE_ACKNOWLEDGEMENT: if (mbuf_get_left(mb) < 4) return EBADMSG; val = ntohl(mbuf_read_u32(mb)); (void)val; break; case RTMP_TYPE_AMF0: err = handle_amf_command(conn, hdr->stream_id, mb); break; case RTMP_TYPE_WINDOW_ACK_SIZE: if (mbuf_get_left(mb) < 4) return EBADMSG; was = ntohl(mbuf_read_u32(mb)); if (was != 0) conn->window_ack_size = was; break; case RTMP_TYPE_SET_PEER_BANDWIDTH: if (mbuf_get_left(mb) < 5) return EBADMSG; was = ntohl(mbuf_read_u32(mb)); limit = mbuf_read_u8(mb); (void)limit; if (was != 0) conn->window_ack_size = was; err = rtmp_control(conn, RTMP_TYPE_WINDOW_ACK_SIZE, (uint32_t)WINDOW_ACK_SIZE); break; case RTMP_TYPE_USER_CONTROL_MSG: err = handle_user_control_msg(conn, mb); break; /* XXX: common code for audio+video */ case RTMP_TYPE_AUDIO: strm = rtmp_stream_find(conn, hdr->stream_id); if (strm) { if (strm->auh) { strm->auh(hdr->timestamp, mb->buf, mb->end, strm->arg); } } break; case RTMP_TYPE_VIDEO: strm = rtmp_stream_find(conn, hdr->stream_id); if (strm) { if (strm->vidh) { strm->vidh(hdr->timestamp, mb->buf, mb->end, strm->arg); } } break; case RTMP_TYPE_DATA: err = handle_data_message(conn, hdr->stream_id, mb); break; default: break; } return err; } static struct rtmp_conn *rtmp_conn_alloc(bool is_client, rtmp_estab_h *estabh, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg) { struct rtmp_conn *conn; int err; conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return NULL; conn->is_client = is_client; conn->state = RTMP_STATE_UNINITIALIZED; conn->send_chunk_size = RTMP_DEFAULT_CHUNKSIZE; conn->window_ack_size = WINDOW_ACK_SIZE; err = rtmp_dechunker_alloc(&conn->dechunk, RTMP_DEFAULT_CHUNKSIZE, rtmp_dechunk_handler, conn); if (err) goto out; /* must be above 2 */ conn->chunk_id_counter = RTMP_CHUNK_ID_CONN + 1; conn->estabh = estabh; conn->cmdh = cmdh; conn->closeh = closeh; conn->arg = arg; out: if (err) return mem_deref(conn); return conn; } static inline void set_state(struct rtmp_conn *conn, enum rtmp_handshake_state state) { conn->state = state; } static int send_packet(struct rtmp_conn *conn, const uint8_t *pkt, size_t len) { struct mbuf *mb; int err; if (!conn || !pkt || !len) return EINVAL; mb = mbuf_alloc(len); if (!mb) return ENOMEM; (void)mbuf_write_mem(mb, pkt, len); mb->pos = 0; err = tcp_send(conn->tc, mb); if (err) goto out; out: mem_deref(mb); return err; } static int handshake_start(struct rtmp_conn *conn) { uint8_t sig[1+RTMP_HANDSHAKE_SIZE]; int err; sig[0] = RTMP_PROTOCOL_VERSION; sig[1] = 0; sig[2] = 0; sig[3] = 0; sig[4] = 0; sig[5] = VER_MAJOR; sig[6] = VER_MINOR; sig[7] = VER_PATCH; sig[8] = 0; rand_bytes(sig + 9, sizeof(sig) - 9); err = send_packet(conn, sig, sizeof(sig)); if (err) return err; set_state(conn, RTMP_STATE_VERSION_SENT); return 0; } static void conn_close(struct rtmp_conn *conn, int err) { rtmp_close_h *closeh; conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); conn->dnsq6 = mem_deref(conn->dnsq6); conn->dnsq4 = mem_deref(conn->dnsq4); closeh = conn->closeh; if (closeh) { conn->closeh = NULL; closeh(err, conn->arg); } } static void tcp_estab_handler(void *arg) { struct rtmp_conn *conn = arg; int err = 0; if (conn->is_client) { err = handshake_start(conn); } if (err) conn_close(conn, err); } /* Send AMF0 Command or Data */ int rtmp_send_amf_command(const struct rtmp_conn *conn, unsigned format, uint32_t chunk_id, uint8_t type_id, uint32_t msg_stream_id, const uint8_t *cmd, size_t len) { if (!conn || !cmd || !len) return EINVAL; return rtmp_chunker(format, chunk_id, 0, 0, type_id, msg_stream_id, cmd, len, conn->send_chunk_size, conn->tc); } static void connect_resp_handler(bool success, const struct odict *msg, void *arg) { struct rtmp_conn *conn = arg; rtmp_estab_h *estabh; (void)msg; if (!success) { conn_close(conn, EPROTO); return; } conn->connected = true; estabh = conn->estabh; if (estabh) { conn->estabh = NULL; estabh(conn->arg); } } static int send_connect(struct rtmp_conn *conn) { const int ac = 0x0400; /* AAC */ const int vc = 0x0080; /* H264 */ return rtmp_amf_request(conn, RTMP_CONTROL_STREAM_ID, "connect", connect_resp_handler, conn, 1, RTMP_AMF_TYPE_OBJECT, 8, RTMP_AMF_TYPE_STRING, "app", conn->app, RTMP_AMF_TYPE_STRING, "flashVer", "FMLE/3.0", RTMP_AMF_TYPE_STRING, "tcUrl", conn->uri, RTMP_AMF_TYPE_BOOLEAN, "fpad", false, RTMP_AMF_TYPE_NUMBER, "capabilities", 15.0, RTMP_AMF_TYPE_NUMBER, "audioCodecs", (double)ac, RTMP_AMF_TYPE_NUMBER, "videoCodecs", (double)vc, RTMP_AMF_TYPE_NUMBER, "videoFunction", 1.0); } static int client_handle_packet(struct rtmp_conn *conn, struct mbuf *mb) { uint8_t s0; uint8_t s1[RTMP_HANDSHAKE_SIZE]; int err = 0; switch (conn->state) { case RTMP_STATE_VERSION_SENT: if (mbuf_get_left(mb) < (1+RTMP_HANDSHAKE_SIZE)) return ENODATA; s0 = mbuf_read_u8(mb); if (s0 != RTMP_PROTOCOL_VERSION) return EPROTO; (void)mbuf_read_mem(mb, s1, sizeof(s1)); err = send_packet(conn, s1, sizeof(s1)); if (err) return err; set_state(conn, RTMP_STATE_ACK_SENT); break; case RTMP_STATE_ACK_SENT: if (mbuf_get_left(mb) < RTMP_HANDSHAKE_SIZE) return ENODATA; /* S2 (ignored) */ mbuf_advance(mb, RTMP_HANDSHAKE_SIZE); conn->send_chunk_size = 4096; err = rtmp_control(conn, RTMP_TYPE_SET_CHUNK_SIZE, conn->send_chunk_size); if (err) return err; err = send_connect(conn); if (err) return err; set_state(conn, RTMP_STATE_HANDSHAKE_DONE); break; case RTMP_STATE_HANDSHAKE_DONE: err = rtmp_dechunker_receive(conn->dechunk, mb); if (err) return err; break; default: return EPROTO; } return 0; } static int server_handle_packet(struct rtmp_conn *conn, struct mbuf *mb) { uint8_t c0; uint8_t c1[RTMP_HANDSHAKE_SIZE]; int err = 0; switch (conn->state) { case RTMP_STATE_UNINITIALIZED: if (mbuf_get_left(mb) < 1) return ENODATA; c0 = mbuf_read_u8(mb); if (c0 != RTMP_PROTOCOL_VERSION) return EPROTO; /* Send S0 + S1 */ err = handshake_start(conn); if (err) return err; break; case RTMP_STATE_VERSION_SENT: if (mbuf_get_left(mb) < RTMP_HANDSHAKE_SIZE) return ENODATA; (void)mbuf_read_mem(mb, c1, sizeof(c1)); /* Copy C1 to S2 */ err = send_packet(conn, c1, sizeof(c1)); if (err) return err; set_state(conn, RTMP_STATE_ACK_SENT); break; case RTMP_STATE_ACK_SENT: if (mbuf_get_left(mb) < RTMP_HANDSHAKE_SIZE) return ENODATA; /* C2 (ignored) */ mbuf_advance(mb, RTMP_HANDSHAKE_SIZE); conn->send_chunk_size = 4096; err = rtmp_control(conn, RTMP_TYPE_SET_CHUNK_SIZE, conn->send_chunk_size); if (err) return err; set_state(conn, RTMP_STATE_HANDSHAKE_DONE); break; case RTMP_STATE_HANDSHAKE_DONE: err = rtmp_dechunker_receive(conn->dechunk, mb); if (err) return err; break; default: return EPROTO; } return 0; } static void tcp_recv_handler(struct mbuf *mb_pkt, void *arg) { struct rtmp_conn *conn = arg; int err; conn->total_bytes += mbuf_get_left(mb_pkt); /* re-assembly of fragments */ if (conn->mb) { const size_t len = mbuf_get_left(mb_pkt), pos = conn->mb->pos; if ((mbuf_get_left(conn->mb) + len) > RTMP_MESSAGE_LEN_MAX) { err = EOVERFLOW; goto out; } conn->mb->pos = conn->mb->end; err = mbuf_write_mem(conn->mb, mbuf_buf(mb_pkt), mbuf_get_left(mb_pkt)); if (err) goto out; conn->mb->pos = pos; } else { conn->mb = mem_ref(mb_pkt); } while (mbuf_get_left(conn->mb) > 0) { size_t pos; uint32_t nrefs; pos = conn->mb->pos; mem_ref(conn); if (conn->is_client) err = client_handle_packet(conn, conn->mb); else err = server_handle_packet(conn, conn->mb); nrefs = mem_nrefs(conn); mem_deref(conn); if (nrefs == 1) return; if (!conn->tc) return; if (err) { /* rewind */ conn->mb->pos = pos; if (err == ENODATA) err = 0; break; } if (conn->mb->pos >= conn->mb->end) { conn->mb = mem_deref(conn->mb); break; } } if (err) goto out; if (conn->total_bytes >= (conn->last_ack + conn->window_ack_size)) { conn->last_ack = conn->total_bytes; err = rtmp_control(conn, RTMP_TYPE_ACKNOWLEDGEMENT, (uint32_t)conn->total_bytes); if (err) goto out; } out: if (err) conn_close(conn, err); } static void tcp_close_handler(int err, void *arg) { struct rtmp_conn *conn = arg; if (conn->is_client && !conn->connected && conn->srvc > 0) { err = req_connect(conn); if (!err) return; } conn_close(conn, err); } static int req_connect(struct rtmp_conn *conn) { const struct sa *addr; int err = EINVAL; while (conn->srvc > 0) { --conn->srvc; addr = &conn->srvv[conn->srvc]; conn->send_chunk_size = RTMP_DEFAULT_CHUNKSIZE; conn->window_ack_size = WINDOW_ACK_SIZE; conn->state = RTMP_STATE_UNINITIALIZED; conn->last_ack = 0; conn->total_bytes = 0; conn->mb = mem_deref(conn->mb); conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); rtmp_dechunker_set_chunksize(conn->dechunk, RTMP_DEFAULT_CHUNKSIZE); err = tcp_connect(&conn->tc, addr, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, conn); #ifdef USE_TLS if (conn->tls && !err) { err = tls_start_tcp(&conn->sc, conn->tls, conn->tc, 0); if (!err) err = tls_set_verify_server(conn->sc, conn->host); } #endif if (!err) break; } return err; } static bool rr_handler(struct dnsrr *rr, void *arg) { struct rtmp_conn *conn = arg; if (conn->srvc >= ARRAY_SIZE(conn->srvv)) return true; switch (rr->type) { case DNS_TYPE_A: sa_set_in(&conn->srvv[conn->srvc++], rr->rdata.a.addr, conn->port); break; case DNS_TYPE_AAAA: sa_set_in6(&conn->srvv[conn->srvc++], rr->rdata.aaaa.addr, conn->port); break; } return false; } static void query_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct rtmp_conn *conn = arg; (void)hdr; (void)authl; (void)addl; dns_rrlist_apply2(ansl, conn->host, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN, true, rr_handler, conn); /* wait for other (A/AAAA) query to complete */ if (conn->dnsq4 || conn->dnsq6) return; if (conn->srvc == 0) { err = err ? err : EDESTADDRREQ; goto out; } err = req_connect(conn); if (err) goto out; return; out: conn_close(conn, err); } /** * Connect to an RTMP server * * @param connp Pointer to allocated RTMP connection object * @param dnsc DNS Client for resolving FQDN uris * @param uri RTMP uri to connect to * @param tls TLS Context (optional) * @param estabh Established handler * @param cmdh Incoming command handler * @param closeh Close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode * * Example URIs: * * rtmp://a.rtmp.youtube.com/live2/my-stream * rtmp://[::1]/vod/mp4:sample.mp4 */ int rtmp_connect(struct rtmp_conn **connp, struct dnsc *dnsc, const char *uri, struct tls *tls, rtmp_estab_h *estabh, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg) { struct rtmp_conn *conn; struct pl pl_scheme; struct pl pl_hostport; struct pl pl_host; struct pl pl_port; struct pl pl_path; struct pl pl_app; struct pl pl_stream; const char *tok; uint16_t defport; int err; if (!connp || !uri) return EINVAL; if (re_regex(uri, strlen(uri), "[a-z]+://[^/]+/[^]+", &pl_scheme, &pl_hostport, &pl_path)) return EINVAL; tok = pl_strrchr(&pl_path, '/'); if (!tok) return EINVAL; pl_app.p = pl_path.p; pl_app.l = tok - pl_path.p; pl_stream.p = tok + 1; pl_stream.l = pl_path.p + pl_path.l - pl_stream.p; if (!pl_strcasecmp(&pl_scheme, "rtmp")) { tls = NULL; defport = RTMP_PORT; } #ifdef USE_TLS else if (!pl_strcasecmp(&pl_scheme, "rtmps")) { if (!tls) return EINVAL; defport = 443; } #endif else return ENOTSUP; if (uri_decode_hostport(&pl_hostport, &pl_host, &pl_port)) return EINVAL; conn = rtmp_conn_alloc(true, estabh, cmdh, closeh, arg); if (!conn) return ENOMEM; conn->port = pl_isset(&pl_port) ? pl_u32(&pl_port) : defport; conn->tls = tls; err = pl_strdup(&conn->app, &pl_app); err |= pl_strdup(&conn->stream, &pl_stream); err |= pl_strdup(&conn->host, &pl_host); err |= str_dup(&conn->uri, uri); if (err) goto out; if (0 == sa_set(&conn->srvv[0], &pl_host, conn->port)) { conn->srvc = 1; err = req_connect(conn); if (err) goto out; } else { #ifdef HAVE_INET6 struct sa tmp; #endif if (!dnsc) { err = EINVAL; goto out; } conn->dnsc = mem_ref(dnsc); err = dnsc_query(&conn->dnsq4, dnsc, conn->host, DNS_TYPE_A, DNS_CLASS_IN, true, query_handler, conn); if (err) goto out; #ifdef HAVE_INET6 if (0 == net_default_source_addr_get(AF_INET6, &tmp)) { err = dnsc_query(&conn->dnsq6, dnsc, conn->host, DNS_TYPE_AAAA, DNS_CLASS_IN, true, query_handler, conn); if (err) goto out; } #endif } out: if (err) mem_deref(conn); else *connp = conn; return err; } /** * Accept an incoming TCP connection creating an RTMP Server connection * * @param connp Pointer to allocated RTMP connection object * @param ts TCP socket with pending connection * @param tls TLS Context (optional) * @param cmdh Incoming command handler * @param closeh Close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int rtmp_accept(struct rtmp_conn **connp, struct tcp_sock *ts, struct tls *tls, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg) { struct rtmp_conn *conn; int err; if (!connp || !ts) return EINVAL; conn = rtmp_conn_alloc(false, NULL, cmdh, closeh, arg); if (!conn) return ENOMEM; err = tcp_accept(&conn->tc, ts, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, conn); if (err) goto out; #ifdef USE_TLS if (tls) { err = tls_start_tcp(&conn->sc, tls, conn->tc, 0); if (err) goto out; } #endif out: if (err) mem_deref(conn); else *connp = conn; return err; } int rtmp_conn_send_msg(const struct rtmp_conn *conn, unsigned format, uint32_t chunk_id, uint32_t timestamp, uint32_t timestamp_delta, uint8_t msg_type_id, uint32_t msg_stream_id, const uint8_t *payload, size_t payload_len) { if (!conn || !payload || !payload_len) return EINVAL; return rtmp_chunker(format, chunk_id, timestamp, timestamp_delta, msg_type_id, msg_stream_id, payload, payload_len, conn->send_chunk_size, conn->tc); } unsigned rtmp_conn_assign_chunkid(struct rtmp_conn *conn) { if (!conn) return 0; return ++conn->chunk_id_counter; } uint64_t rtmp_conn_assign_tid(struct rtmp_conn *conn) { if (!conn) return 0; return ++conn->tid_counter; } /** * Get the underlying TCP connection from an RTMP connection * * @param conn RTMP Connection * * @return TCP-Connection */ struct tcp_conn *rtmp_conn_tcpconn(const struct rtmp_conn *conn) { return conn ? conn->tc : NULL; } /** * Get the RTMP connection stream name from rtmp_connect * * @param conn RTMP Connection * * @return RTMP Stream name or NULL */ const char *rtmp_conn_stream(const struct rtmp_conn *conn) { return conn ? conn->stream : NULL; } /** * Set callback handlers for the RTMP connection * * @param conn RTMP connection * @param cmdh Incoming command handler * @param closeh Close handler * @param arg Handler argument */ void rtmp_set_handlers(struct rtmp_conn *conn, rtmp_command_h *cmdh, rtmp_close_h *closeh, void *arg) { if (!conn) return; conn->cmdh = cmdh; conn->closeh = closeh; conn->arg = arg; } static const char *rtmp_handshake_name(enum rtmp_handshake_state state) { switch (state) { case RTMP_STATE_UNINITIALIZED: return "UNINITIALIZED"; case RTMP_STATE_VERSION_SENT: return "VERSION_SENT"; case RTMP_STATE_ACK_SENT: return "ACK_SENT"; case RTMP_STATE_HANDSHAKE_DONE: return "HANDSHAKE_DONE"; default: return "?"; } } int rtmp_conn_debug(struct re_printf *pf, const struct rtmp_conn *conn) { int err = 0; if (!conn) return 0; err |= re_hprintf(pf, "role: %s\n", conn->is_client ? "Client" : "Server"); err |= re_hprintf(pf, "state: %s\n", rtmp_handshake_name(conn->state)); err |= re_hprintf(pf, "connected: %d\n", conn->connected); err |= re_hprintf(pf, "chunk_size: send=%u\n", conn->send_chunk_size); err |= re_hprintf(pf, "bytes: %zu\n", conn->total_bytes); err |= re_hprintf(pf, "streams: %u\n", list_count(&conn->streaml)); if (conn->is_client) { err |= re_hprintf(pf, "uri: %s\n", conn->uri); err |= re_hprintf(pf, "app: %s\n", conn->app); err |= re_hprintf(pf, "stream: %s\n", conn->stream); } err |= re_hprintf(pf, "%H\n", rtmp_dechunker_debug, conn->dechunk); return err; } re-1.1.0/src/rtmp/control.c000066400000000000000000000042221373627245400155310ustar00rootroot00000000000000/** * @file rtmp/control.c Real Time Messaging Protocol (RTMP) -- Control * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "rtmp.h" /** * Send an RTMP control message * * @param conn RTMP connection * @param type RTMP Packet type * @param ... Optional packet arguments * * @return 0 if success, otherwise errorcode */ int rtmp_control(const struct rtmp_conn *conn, enum rtmp_packet_type type, ...) { struct mbuf *mb; uint32_t u32; uint16_t event; va_list ap; int err = 0; if (!conn) return EINVAL; mb = mbuf_alloc(8); if (!mb) return ENOMEM; va_start(ap, type); switch (type) { case RTMP_TYPE_SET_CHUNK_SIZE: case RTMP_TYPE_WINDOW_ACK_SIZE: case RTMP_TYPE_ACKNOWLEDGEMENT: u32 = va_arg(ap, uint32_t); err = mbuf_write_u32(mb, htonl(u32)); break; case RTMP_TYPE_USER_CONTROL_MSG: event = va_arg(ap, unsigned); err = mbuf_write_u16(mb, htons(event)); err |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t))); break; case RTMP_TYPE_SET_PEER_BANDWIDTH: err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t))); err |= mbuf_write_u8(mb, va_arg(ap, unsigned)); break; default: err = ENOTSUP; break; } va_end(ap); if (err) goto out; err = rtmp_conn_send_msg(conn, 0, RTMP_CHUNK_ID_CONTROL, 0, 0, type, RTMP_CONTROL_STREAM_ID, mb->buf, mb->end); if (err) goto out; out: mem_deref(mb); return err; } /** * Get the event name as a string * * @param event RTMP Event type * * @return Name of the event as a string */ const char *rtmp_event_name(enum rtmp_event_type event) { switch (event) { case RTMP_EVENT_STREAM_BEGIN: return "StreamBegin"; case RTMP_EVENT_STREAM_EOF: return "StreamEOF"; case RTMP_EVENT_STREAM_DRY: return "StreamDry"; case RTMP_EVENT_SET_BUFFER_LENGTH: return "SetBufferLength"; case RTMP_EVENT_STREAM_IS_RECORDED: return "StreamIsRecorded"; case RTMP_EVENT_PING_REQUEST: return "PingRequest"; case RTMP_EVENT_PING_RESPONSE: return "PingResponse"; default: return "?"; } } re-1.1.0/src/rtmp/ctrans.c000066400000000000000000000044741373627245400153540ustar00rootroot00000000000000/** * @file rtmp/ctrans.c Real Time Messaging Protocol -- AMF Client Transactions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" struct rtmp_ctrans { struct le le; uint64_t tid; rtmp_resp_h *resph; void *arg; }; static void ctrans_destructor(void *data) { struct rtmp_ctrans *ct = data; list_unlink(&ct->le); } static struct rtmp_ctrans *rtmp_ctrans_find(const struct list *ctransl, uint64_t tid) { struct le *le; for (le = list_head(ctransl); le; le = le->next) { struct rtmp_ctrans *ct = le->data; if (tid == ct->tid) return ct; } return NULL; } int rtmp_amf_request(struct rtmp_conn *conn, uint32_t stream_id, const char *command, rtmp_resp_h *resph, void *arg, unsigned body_propc, ...) { struct rtmp_ctrans *ct = NULL; struct mbuf *mb; va_list ap; int err; if (!conn || !command || !resph) return EINVAL; mb = mbuf_alloc(512); if (!mb) return ENOMEM; ct = mem_zalloc(sizeof(*ct), ctrans_destructor); if (!ct) { err = ENOMEM; goto out; } ct->tid = rtmp_conn_assign_tid(conn); ct->resph = resph; ct->arg = arg; err = rtmp_command_header_encode(mb, command, ct->tid); if (err) goto out; if (body_propc) { va_start(ap, body_propc); err = rtmp_amf_vencode_object(mb, RTMP_AMF_TYPE_ROOT, body_propc, &ap); va_end(ap); if (err) goto out; } err = rtmp_send_amf_command(conn, 0, RTMP_CHUNK_ID_CONN, RTMP_TYPE_AMF0, stream_id, mb->buf, mb->end); if (err) goto out; list_append(&conn->ctransl, &ct->le, ct); out: mem_deref(mb); if (err) mem_deref(ct); return err; } int rtmp_ctrans_response(const struct list *ctransl, const struct odict *msg) { struct rtmp_ctrans *ct; uint64_t tid; bool success; rtmp_resp_h *resph; void *arg; if (!ctransl || !msg) return EINVAL; success = (0 == str_casecmp(odict_string(msg, "0"), "_result")); if (!odict_get_number(msg, &tid, "1")) return EPROTO; ct = rtmp_ctrans_find(ctransl, tid); if (!ct) return ENOENT; resph = ct->resph; arg = ct->arg; mem_deref(ct); resph(success, msg, arg); return 0; } re-1.1.0/src/rtmp/dechunk.c000066400000000000000000000117341373627245400155000ustar00rootroot00000000000000/** * @file rtmp/dechunk.c Real Time Messaging Protocol (RTMP) -- Dechunking * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "rtmp.h" enum { MAX_CHUNKS = 64, }; struct rtmp_chunk { struct le le; struct rtmp_header hdr; struct mbuf *mb; }; /** Defines the RTMP Dechunker */ struct rtmp_dechunker { struct list chunkl; /* struct rtmp_chunk */ size_t chunk_sz; rtmp_dechunk_h *chunkh; void *arg; }; static void destructor(void *data) { struct rtmp_dechunker *rd = data; list_flush(&rd->chunkl); } static void chunk_destructor(void *data) { struct rtmp_chunk *chunk = data; list_unlink(&chunk->le); mem_deref(chunk->mb); } static struct rtmp_chunk *create_chunk(struct list *chunkl, const struct rtmp_header *hdr) { struct rtmp_chunk *chunk; chunk = mem_zalloc(sizeof(*chunk), chunk_destructor); if (!chunk) return NULL; chunk->hdr = *hdr; list_append(chunkl, &chunk->le, chunk); return chunk; } static struct rtmp_chunk *find_chunk(const struct list *chunkl, uint32_t chunk_id) { struct le *le; for (le = list_head(chunkl); le; le = le->next) { struct rtmp_chunk *chunk = le->data; if (chunk_id == chunk->hdr.chunk_id) return chunk; } return NULL; } /* * Stateful RTMP de-chunker for receiving complete messages */ int rtmp_dechunker_alloc(struct rtmp_dechunker **rdp, size_t chunk_sz, rtmp_dechunk_h *chunkh, void *arg) { struct rtmp_dechunker *rd; if (!rdp || !chunk_sz || !chunkh) return EINVAL; rd = mem_zalloc(sizeof(*rd), destructor); if (!rd) return ENOMEM; rd->chunk_sz = chunk_sz; rd->chunkh = chunkh; rd->arg = arg; *rdp = rd; return 0; } int rtmp_dechunker_receive(struct rtmp_dechunker *rd, struct mbuf *mb) { struct rtmp_header hdr; struct rtmp_chunk *chunk; size_t chunk_sz, left, msg_len; int err; if (!rd || !mb) return EINVAL; err = rtmp_header_decode(&hdr, mb); if (err) return err; /* find preceding chunk, from chunk id */ chunk = find_chunk(&rd->chunkl, hdr.chunk_id); if (!chunk) { /* only type 0 can create a new chunk stream */ if (hdr.format == 0) { if (list_count(&rd->chunkl) > MAX_CHUNKS) return EOVERFLOW; chunk = create_chunk(&rd->chunkl, &hdr); if (!chunk) return ENOMEM; } else return ENOENT; } switch (hdr.format) { case 0: case 1: case 2: if (hdr.format == 0) { /* copy the whole header */ chunk->hdr = hdr; } else if (hdr.format == 1) { chunk->hdr.timestamp_delta = hdr.timestamp_delta; chunk->hdr.length = hdr.length; chunk->hdr.type_id = hdr.type_id; } else if (hdr.format == 2) { chunk->hdr.timestamp_delta = hdr.timestamp_delta; } msg_len = chunk->hdr.length; chunk_sz = min(msg_len, rd->chunk_sz); if (mbuf_get_left(mb) < chunk_sz) return ENODATA; mem_deref(chunk->mb); chunk->mb = mbuf_alloc(msg_len); if (!chunk->mb) return ENOMEM; err = mbuf_read_mem(mb, chunk->mb->buf, chunk_sz); if (err) return err; chunk->mb->pos = chunk_sz; chunk->mb->end = chunk_sz; chunk->hdr.format = hdr.format; chunk->hdr.ext_ts = hdr.ext_ts; if (hdr.format == 1 || hdr.format == 2) chunk->hdr.timestamp += hdr.timestamp_delta; break; case 3: if (chunk->hdr.ext_ts) { uint32_t ext_ts; if (mbuf_get_left(mb) < 4) return ENODATA; ext_ts = ntohl(mbuf_read_u32(mb)); if (chunk->hdr.format == 0) chunk->hdr.timestamp = ext_ts; else chunk->hdr.timestamp_delta = ext_ts; } if (!chunk->mb) { chunk->mb = mbuf_alloc(chunk->hdr.length); if (!chunk->mb) return ENOMEM; if (chunk->hdr.format == 0) { chunk->hdr.timestamp_delta = chunk->hdr.timestamp; } chunk->hdr.timestamp += chunk->hdr.timestamp_delta; } left = mbuf_get_space(chunk->mb); chunk_sz = min(left, rd->chunk_sz); if (mbuf_get_left(mb) < chunk_sz) return ENODATA; err = mbuf_read_mem(mb, mbuf_buf(chunk->mb), chunk_sz); if (err) return err; chunk->mb->pos += chunk_sz; chunk->mb->end += chunk_sz; break; default: return EPROTO; } if (chunk->mb->pos >= chunk->mb->size) { struct mbuf *buf; chunk->mb->pos = 0; buf = chunk->mb; chunk->mb = NULL; err = rd->chunkh(&chunk->hdr, buf, rd->arg); mem_deref(buf); } return err; } void rtmp_dechunker_set_chunksize(struct rtmp_dechunker *rd, size_t chunk_sz) { if (!rd || !chunk_sz) return; rd->chunk_sz = chunk_sz; } int rtmp_dechunker_debug(struct re_printf *pf, const struct rtmp_dechunker *rd) { struct le *le; int err; if (!rd) return 0; err = re_hprintf(pf, "Dechunker Debug:\n"); err |= re_hprintf(pf, "chunk list: (%u)\n", list_count(&rd->chunkl)); for (le = rd->chunkl.head; le; le = le->next) { const struct rtmp_chunk *msg = le->data; err |= re_hprintf(pf, ".. %H\n", rtmp_header_print, &msg->hdr); } err |= re_hprintf(pf, "\n"); return err; } re-1.1.0/src/rtmp/hdr.c000066400000000000000000000125031373627245400146270ustar00rootroot00000000000000/** * @file rtmp/hdr.c Real Time Messaging Protocol (RTMP) -- Headers * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" enum { RTMP_CHUNK_ID_MIN = 3, RTMP_CHUNK_ID_MAX = 65599, /* 65535 + 64 */ RTMP_CHUNK_OFFSET = 64, TIMESTAMP_24MAX = 0x00ffffff, }; static int mbuf_write_u24_hton(struct mbuf *mb, uint32_t u24) { int err = 0; err |= mbuf_write_u8(mb, u24 >> 16); err |= mbuf_write_u8(mb, u24 >> 8); err |= mbuf_write_u8(mb, u24 >> 0); return err; } static uint32_t mbuf_read_u24_ntoh(struct mbuf *mb) { uint32_t u24; u24 = (uint32_t)mbuf_read_u8(mb) << 16; u24 |= (uint32_t)mbuf_read_u8(mb) << 8; u24 |= (uint32_t)mbuf_read_u8(mb) << 0; return u24; } static int encode_basic_hdr(struct mbuf *mb, unsigned fmt, uint32_t chunk_id) { uint8_t v; int err = 0; if (chunk_id >= 320) { const uint16_t cs_id = chunk_id - RTMP_CHUNK_OFFSET; v = fmt<<6 | 1; err |= mbuf_write_u8(mb, v); err |= mbuf_write_u16(mb, htons(cs_id)); } else if (chunk_id >= RTMP_CHUNK_OFFSET) { const uint8_t cs_id = chunk_id - RTMP_CHUNK_OFFSET; v = fmt<<6 | 0; err |= mbuf_write_u8(mb, v); err |= mbuf_write_u8(mb, cs_id); } else { v = fmt<<6 | chunk_id; err |= mbuf_write_u8(mb, v); } return err; } static int decode_basic_hdr(struct rtmp_header *hdr, struct mbuf *mb) { uint8_t cs_id; uint8_t v; if (mbuf_get_left(mb) < 1) return ENODATA; v = mbuf_read_u8(mb); hdr->format = v>>6; cs_id = v & 0x3f; switch (cs_id) { case 0: if (mbuf_get_left(mb) < 1) return ENODATA; hdr->chunk_id = mbuf_read_u8(mb) + RTMP_CHUNK_OFFSET; break; case 1: if (mbuf_get_left(mb) < 2) return ENODATA; hdr->chunk_id = ntohs(mbuf_read_u16(mb)) + RTMP_CHUNK_OFFSET; break; default: hdr->chunk_id = cs_id; break; } return 0; } static uint32_t ts_24(uint32_t ts) { return ts >= TIMESTAMP_24MAX ? TIMESTAMP_24MAX : ts; } static uint32_t ts_ext(uint32_t ts) { return ts >= TIMESTAMP_24MAX ? ts : 0; } int rtmp_header_encode(struct mbuf *mb, struct rtmp_header *hdr) { int err = 0; if (!mb || !hdr) return EINVAL; err = encode_basic_hdr(mb, hdr->format, hdr->chunk_id); if (err) return err; switch (hdr->format) { case 0: hdr->timestamp_ext = ts_ext(hdr->timestamp); err |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp)); err |= mbuf_write_u24_hton(mb, hdr->length); err |= mbuf_write_u8(mb, hdr->type_id); err |= mbuf_write_u32(mb, sys_htoll(hdr->stream_id)); break; case 1: hdr->timestamp_ext = ts_ext(hdr->timestamp_delta); err |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp_delta)); err |= mbuf_write_u24_hton(mb, hdr->length); err |= mbuf_write_u8(mb, hdr->type_id); break; case 2: hdr->timestamp_ext = ts_ext(hdr->timestamp_delta); err |= mbuf_write_u24_hton(mb, ts_24(hdr->timestamp_delta)); break; case 3: break; } if (hdr->timestamp_ext) { err |= mbuf_write_u32(mb, htonl(hdr->timestamp_ext)); } return err; } int rtmp_header_decode(struct rtmp_header *hdr, struct mbuf *mb) { uint32_t *timestamp_ext = NULL; int err; if (!hdr || !mb) return EINVAL; memset(hdr, 0, sizeof(*hdr)); err = decode_basic_hdr(hdr, mb); if (err) return err; switch (hdr->format) { case 0: if (mbuf_get_left(mb) < 11) return ENODATA; hdr->timestamp = mbuf_read_u24_ntoh(mb); hdr->length = mbuf_read_u24_ntoh(mb); hdr->type_id = mbuf_read_u8(mb); hdr->stream_id = sys_ltohl(mbuf_read_u32(mb)); break; case 1: if (mbuf_get_left(mb) < 7) return ENODATA; hdr->timestamp_delta = mbuf_read_u24_ntoh(mb); hdr->length = mbuf_read_u24_ntoh(mb); hdr->type_id = mbuf_read_u8(mb); break; case 2: if (mbuf_get_left(mb) < 3) return ENODATA; hdr->timestamp_delta = mbuf_read_u24_ntoh(mb); break; case 3: /* no payload */ break; } if (hdr->timestamp == TIMESTAMP_24MAX) timestamp_ext = &hdr->timestamp; else if (hdr->timestamp_delta == TIMESTAMP_24MAX) timestamp_ext = &hdr->timestamp_delta; if (timestamp_ext) { if (mbuf_get_left(mb) < 4) return ENODATA; *timestamp_ext = ntohl(mbuf_read_u32(mb)); hdr->ext_ts = true; } return 0; } int rtmp_header_print(struct re_printf *pf, const struct rtmp_header *hdr) { if (!hdr) return 0; return re_hprintf(pf, "fmt %u, chunk %u, " "timestamp %5u, ts_delta %2u," " len %3u, type %2u (%-14s) stream_id %u", hdr->format, hdr->chunk_id, hdr->timestamp, hdr->timestamp_delta, hdr->length, hdr->type_id, rtmp_packet_type_name(hdr->type_id), hdr->stream_id); } const char *rtmp_packet_type_name(enum rtmp_packet_type type) { switch (type) { case RTMP_TYPE_SET_CHUNK_SIZE: return "Set Chunk Size"; case RTMP_TYPE_ACKNOWLEDGEMENT: return "Acknowledgement"; case RTMP_TYPE_USER_CONTROL_MSG: return "User Control Message"; case RTMP_TYPE_WINDOW_ACK_SIZE: return "Window Acknowledgement Size"; case RTMP_TYPE_SET_PEER_BANDWIDTH:return "Set Peer Bandwidth"; case RTMP_TYPE_AUDIO: return "Audio Message"; case RTMP_TYPE_VIDEO: return "Video Message"; case RTMP_TYPE_DATA: return "Data Message"; case RTMP_TYPE_AMF0: return "AMF"; default: return "?"; } } re-1.1.0/src/rtmp/mod.mk000066400000000000000000000004101373627245400150100ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += rtmp/amf.c SRCS += rtmp/amf_dec.c SRCS += rtmp/amf_enc.c SRCS += rtmp/chunk.c SRCS += rtmp/conn.c SRCS += rtmp/control.c SRCS += rtmp/ctrans.c SRCS += rtmp/dechunk.c SRCS += rtmp/hdr.c SRCS += rtmp/stream.c re-1.1.0/src/rtmp/rtmp.h000066400000000000000000000104421373627245400150410ustar00rootroot00000000000000/** * @file rtmp.h Real Time Messaging Protocol (RTMP) -- Internal API * * Copyright (C) 2010 Creytiv.com */ enum { RTMP_PROTOCOL_VERSION = 3, RTMP_DEFAULT_CHUNKSIZE = 128, RTMP_HANDSHAKE_SIZE = 1536, RTMP_MESSAGE_LEN_MAX = 524288, }; /* Chunk IDs */ enum { RTMP_CHUNK_ID_CONTROL = 2, RTMP_CHUNK_ID_CONN = 3, }; /** Defines the RTMP Handshake State */ enum rtmp_handshake_state { RTMP_STATE_UNINITIALIZED = 0, RTMP_STATE_VERSION_SENT, RTMP_STATE_ACK_SENT, RTMP_STATE_HANDSHAKE_DONE }; /** * Defines an RTMP Connection */ struct rtmp_conn { struct list streaml; struct rtmp_dechunker *dechunk; struct tcp_conn *tc; struct tls_conn *sc; struct mbuf *mb; /* TCP reassembly buffer */ enum rtmp_handshake_state state; size_t total_bytes; size_t last_ack; uint32_t window_ack_size; uint32_t send_chunk_size; unsigned chunk_id_counter; bool is_client; bool connected; rtmp_estab_h *estabh; rtmp_command_h *cmdh; rtmp_close_h *closeh; void *arg; /* client specific: */ struct dnsc *dnsc; struct dns_query *dnsq4; struct dns_query *dnsq6; struct list ctransl; struct sa srvv[16]; struct tls *tls; unsigned srvc; uint64_t tid_counter; uint16_t port; char *app; char *uri; char *stream; char *host; }; /** * Defines an RTMP Stream */ struct rtmp_stream { struct le le; const struct rtmp_conn *conn; /**< Pointer to parent connection */ bool created; uint32_t stream_id; unsigned chunk_id_audio; unsigned chunk_id_video; unsigned chunk_id_data; rtmp_audio_h *auh; rtmp_video_h *vidh; rtmp_command_h *datah; rtmp_command_h *cmdh; rtmp_resp_h *resph; rtmp_control_h *ctrlh; void *arg; }; struct rtmp_header { unsigned format:2; /* type 0-3 */ uint32_t chunk_id; /* from 3-65599 */ uint32_t timestamp; /* 24-bit or 32-bit */ uint32_t timestamp_delta; /* 24-bit */ uint32_t timestamp_ext; uint32_t length; /* 24-bit */ uint8_t type_id; /* enum rtmp_packet_type */ uint32_t stream_id; bool ext_ts; }; /* Command */ int rtmp_command_header_encode(struct mbuf *mb, const char *name, uint64_t tid); /* Connection */ int rtmp_conn_send_msg(const struct rtmp_conn *conn, unsigned format, uint32_t chunk_id, uint32_t timestamp, uint32_t timestamp_delta, uint8_t msg_type_id, uint32_t msg_stream_id, const uint8_t *payload, size_t payload_len); int rtmp_send_amf_command(const struct rtmp_conn *conn, unsigned format, uint32_t chunk_id, uint8_t type_id, uint32_t msg_stream_id, const uint8_t *cmd, size_t len); unsigned rtmp_conn_assign_chunkid(struct rtmp_conn *conn); uint64_t rtmp_conn_assign_tid(struct rtmp_conn *conn); /* Client Transaction */ struct rtmp_ctrans; int rtmp_ctrans_response(const struct list *ctransl, const struct odict *msg); /* * RTMP Chunk */ int rtmp_chunker(unsigned format, uint32_t chunk_id, uint32_t timestamp, uint32_t timestamp_delta, uint8_t msg_type_id, uint32_t msg_stream_id, const uint8_t *payload, size_t payload_len, size_t max_chunk_sz, struct tcp_conn *tc); /* * RTMP Header */ int rtmp_header_encode(struct mbuf *mb, struct rtmp_header *hdr); int rtmp_header_decode(struct rtmp_header *hdr, struct mbuf *mb); int rtmp_header_print(struct re_printf *pf, const struct rtmp_header *hdr); const char *rtmp_packet_type_name(enum rtmp_packet_type type); /* * RTMP De-chunker */ struct rtmp_dechunker; typedef int (rtmp_dechunk_h)(const struct rtmp_header *hdr, struct mbuf *mb, void *arg); int rtmp_dechunker_alloc(struct rtmp_dechunker **rdp, size_t chunk_sz, rtmp_dechunk_h *chunkh, void *arg); int rtmp_dechunker_receive(struct rtmp_dechunker *rd, struct mbuf *mb); void rtmp_dechunker_set_chunksize(struct rtmp_dechunker *rd, size_t chunk_sz); int rtmp_dechunker_debug(struct re_printf *pf, const struct rtmp_dechunker *rd); /* * AMF (Action Message Format) */ int rtmp_amf_encode_number(struct mbuf *mb, double val); int rtmp_amf_encode_boolean(struct mbuf *mb, bool boolean); int rtmp_amf_encode_string(struct mbuf *mb, const char *str); int rtmp_amf_encode_null(struct mbuf *mb); int rtmp_amf_vencode_object(struct mbuf *mb, enum rtmp_amf_type container, unsigned propc, va_list *ap); int rtmp_amf_decode(struct odict **msgp, struct mbuf *mb); re-1.1.0/src/rtmp/stream.c000066400000000000000000000147441373627245400153560ustar00rootroot00000000000000/** * @file rtmp/stream.c Real Time Messaging Protocol (RTMP) -- NetStream * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "rtmp.h" static void destructor(void *data) { struct rtmp_stream *strm = data; list_unlink(&strm->le); if (strm->created) { rtmp_amf_command(strm->conn, 0, "deleteStream", 3, RTMP_AMF_TYPE_NUMBER, 0.0, RTMP_AMF_TYPE_NULL, RTMP_AMF_TYPE_NUMBER, (double)strm->stream_id); } } /** * Allocate a new RTMP Stream object * * @param strmp Pointer to allocated RTMP Stream * @param conn RTMP Connection * @param stream_id Stream id * @param cmdh Command handler * @param ctrlh Control handler * @param auh Audio handler * @param vidh Video handler * @param datah Data handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int rtmp_stream_alloc(struct rtmp_stream **strmp, struct rtmp_conn *conn, uint32_t stream_id, rtmp_command_h *cmdh, rtmp_control_h *ctrlh, rtmp_audio_h *auh, rtmp_video_h *vidh, rtmp_command_h *datah, void *arg) { struct rtmp_stream *strm; if (!strmp || !conn) return EINVAL; strm = mem_zalloc(sizeof(*strm), destructor); if (!strm) return ENOMEM; strm->conn = conn; strm->stream_id = stream_id; strm->cmdh = cmdh; strm->ctrlh = ctrlh; strm->auh = auh; strm->vidh = vidh; strm->datah = datah; strm->arg = arg; strm->chunk_id_audio = rtmp_conn_assign_chunkid(conn); strm->chunk_id_video = rtmp_conn_assign_chunkid(conn); strm->chunk_id_data = rtmp_conn_assign_chunkid(conn); list_append(&conn->streaml, &strm->le, strm); *strmp = strm; return 0; } static void createstream_handler(bool success, const struct odict *msg, void *arg) { struct rtmp_stream *strm = arg; uint64_t num; if (!success) goto out; if (!odict_get_number(msg, &num, "3")) { success = false; goto out; } strm->stream_id = (uint32_t)num; if (strm->stream_id == 0) { success = false; goto out; } strm->created = true; out: if (strm->resph) strm->resph(success, msg, strm->arg); } /** * Create a new RTMP Stream by sending "createStream" to the RTMP Server. * * @param strmp Pointer to allocated RTMP Stream * @param conn RTMP Connection * @param resph RTMP Response handler * @param cmdh Command handler * @param ctrlh Control handler * @param auh Audio handler * @param vidh Video handler * @param datah Data handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int rtmp_stream_create(struct rtmp_stream **strmp, struct rtmp_conn *conn, rtmp_resp_h *resph, rtmp_command_h *cmdh, rtmp_control_h *ctrlh, rtmp_audio_h *auh, rtmp_video_h *vidh, rtmp_command_h *datah, void *arg) { struct rtmp_stream *strm; int err; if (!strmp || !conn) return EINVAL; err = rtmp_stream_alloc(&strm, conn, (uint32_t)-1, cmdh, ctrlh, auh, vidh, datah, arg); if (err) return err; strm->resph = resph; err = rtmp_amf_request(conn, 0, "createStream", createstream_handler, strm, 1, RTMP_AMF_TYPE_NULL); if (err) goto out; out: if (err) mem_deref(strm); else *strmp = strm; return err; } /** * Start playing an RTMP Stream by sending "play" to the RTMP Server * * @param strm RTMP Stream * @param name Stream name * * @return 0 if success, otherwise errorcode */ int rtmp_play(struct rtmp_stream *strm, const char *name) { if (!strm || !name) return EINVAL; return rtmp_amf_command(strm->conn, strm->stream_id, "play", 4, RTMP_AMF_TYPE_NUMBER, 0.0, RTMP_AMF_TYPE_NULL, RTMP_AMF_TYPE_STRING, name, RTMP_AMF_TYPE_NUMBER, -2000.0); } /** * Start publishing an RTMP Stream by sending "publish" to the RTMP Server * * @param strm RTMP Stream * @param name Stream name * * @return 0 if success, otherwise errorcode */ int rtmp_publish(struct rtmp_stream *strm, const char *name) { if (!strm || !name) return EINVAL; return rtmp_amf_command(strm->conn, strm->stream_id, "publish", 4, RTMP_AMF_TYPE_NUMBER, 0.0, RTMP_AMF_TYPE_NULL, RTMP_AMF_TYPE_STRING, name, RTMP_AMF_TYPE_STRING, "live"); } /** * Send metadata on the stream to the RTMP Server * * @param strm RTMP Stream * * @return 0 if success, otherwise errorcode */ int rtmp_meta(struct rtmp_stream *strm) { if (!strm) return EINVAL; return rtmp_amf_data(strm->conn, strm->stream_id, "@setDataFrame", 2, RTMP_AMF_TYPE_STRING, "onMetaData", RTMP_AMF_TYPE_ECMA_ARRAY, 2, RTMP_AMF_TYPE_NUMBER, "audiocodecid", 10.0, RTMP_AMF_TYPE_NUMBER, "videocodecid", 7.0); } /** * Send audio packet on the RTMP Stream * * @param strm RTMP Stream * @param timestamp Timestamp in [milliseconds] * @param pld Audio payload * @param len Payload length * * @return 0 if success, otherwise errorcode */ int rtmp_send_audio(struct rtmp_stream *strm, uint32_t timestamp, const uint8_t *pld, size_t len) { uint32_t chunk_id; if (!strm || !pld || !len) return EINVAL; chunk_id = strm->chunk_id_audio; return rtmp_conn_send_msg(strm->conn, 0, chunk_id, timestamp, 0, RTMP_TYPE_AUDIO, strm->stream_id, pld, len); } /** * Send video packet on the RTMP Stream * * @param strm RTMP Stream * @param timestamp Timestamp in [milliseconds] * @param pld Video payload * @param len Payload length * * @return 0 if success, otherwise errorcode */ int rtmp_send_video(struct rtmp_stream *strm, uint32_t timestamp, const uint8_t *pld, size_t len) { uint32_t chunk_id; if (!strm || !pld || !len) return EINVAL; chunk_id = strm->chunk_id_video; return rtmp_conn_send_msg(strm->conn, 0, chunk_id, timestamp, 0, RTMP_TYPE_VIDEO, strm->stream_id, pld, len); } /** * Find an RTMP Stream by stream id * * @param conn RTMP Connection * @param stream_id Stream id * * @return RTMP Stream if found, or NULL if not found */ struct rtmp_stream *rtmp_stream_find(const struct rtmp_conn *conn, uint32_t stream_id) { struct le *le; if (!conn) return NULL; for (le = list_head(&conn->streaml); le; le = le->next) { struct rtmp_stream *strm = le->data; if (stream_id == strm->stream_id) return strm; } return NULL; } re-1.1.0/src/rtp/000077500000000000000000000000001373627245400135305ustar00rootroot00000000000000re-1.1.0/src/rtp/fb.c000066400000000000000000000065251373627245400142730ustar00rootroot00000000000000/** * @file fb.c Real-time Transport Control Protocol (RTCP)-Based Feedback * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "rtcp.h" #define DEBUG_MODULE "rtcp_pb" #define DEBUG_LEVEL 5 #include enum { GNACK_SIZE = 4, SLI_SIZE = 4 }; /* Encode functions */ /** * Encode an RTCP Generic NACK (GNACK) message * * @param mb Buffer to encode into * @param pid Packet ID * @param blp Bitmask of following lost packets (BLP) * * @return 0 for success, otherwise errorcode */ int rtcp_rtpfb_gnack_encode(struct mbuf *mb, uint16_t pid, uint16_t blp) { int err; err = mbuf_write_u16(mb, htons(pid)); err |= mbuf_write_u16(mb, htons(blp)); return err; } /** * Encode an RTCP Slice Loss Indication (SLI) message * * @param mb Buffer to encode into * @param first Macroblock (MB) address of the first lost macroblock * @param number Number of lost macroblocks * @param picid Picture ID * * @return 0 for success, otherwise errorcode */ int rtcp_psfb_sli_encode(struct mbuf *mb, uint16_t first, uint16_t number, uint8_t picid) { const uint32_t v = first<<19 | number<<6 | picid; return mbuf_write_u32(mb, htonl(v)); } /* Decode functions */ /** * Decode an RTCP Transport Layer Feedback Message * * @param mb Buffer to decode * @param msg RTCP Message to decode into * * @return 0 for success, otherwise errorcode */ int rtcp_rtpfb_decode(struct mbuf *mb, struct rtcp_msg *msg) { size_t i, sz; if (!msg) return EINVAL; switch (msg->hdr.count) { case RTCP_RTPFB_GNACK: sz = msg->r.fb.n * sizeof(*msg->r.fb.fci.gnackv); msg->r.fb.fci.gnackv = mem_alloc(sz, NULL); if (!msg->r.fb.fci.gnackv) return ENOMEM; if (mbuf_get_left(mb) < msg->r.fb.n * GNACK_SIZE) return EBADMSG; for (i=0; ir.fb.n; i++) { msg->r.fb.fci.gnackv[i].pid = ntohs(mbuf_read_u16(mb)); msg->r.fb.fci.gnackv[i].blp = ntohs(mbuf_read_u16(mb)); } break; default: DEBUG_NOTICE("unknown RTPFB fmt %d\n", msg->hdr.count); break; } return 0; } /** * Decode an RTCP Payload-Specific Feedback Message * * @param mb Buffer to decode * @param msg RTCP Message to decode into * * @return 0 for success, otherwise errorcode */ int rtcp_psfb_decode(struct mbuf *mb, struct rtcp_msg *msg) { size_t i, sz; if (!msg) return EINVAL; switch (msg->hdr.count) { case RTCP_PSFB_PLI: /* no params */ break; case RTCP_PSFB_SLI: sz = msg->r.fb.n * sizeof(*msg->r.fb.fci.sliv); msg->r.fb.fci.sliv = mem_alloc(sz, NULL); if (!msg->r.fb.fci.sliv) return ENOMEM; if (mbuf_get_left(mb) < msg->r.fb.n * SLI_SIZE) return EBADMSG; for (i=0; ir.fb.n; i++) { const uint32_t v = ntohl(mbuf_read_u32(mb)); msg->r.fb.fci.sliv[i].first = v>>19 & 0x1fff; msg->r.fb.fci.sliv[i].number = v>> 6 & 0x1fff; msg->r.fb.fci.sliv[i].picid = v>> 0 & 0x003f; } break; case RTCP_PSFB_AFB: sz = msg->r.fb.n * 4; if (mbuf_get_left(mb) < sz) return EBADMSG; msg->r.fb.fci.afb = mbuf_alloc_ref(mb); if (!msg->r.fb.fci.afb) return ENOMEM; msg->r.fb.fci.afb->end = msg->r.fb.fci.afb->pos + sz; mbuf_advance(mb, sz); break; default: DEBUG_NOTICE("unknown PSFB fmt %d\n", msg->hdr.count); break; } return 0; } re-1.1.0/src/rtp/member.c000066400000000000000000000016531373627245400151500ustar00rootroot00000000000000/** * @file member.c Real-time Transport Control Protocol member * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "rtcp.h" static void destructor(void *data) { struct rtp_member *mbr = data; hash_unlink(&mbr->le); mem_deref(mbr->s); } struct rtp_member *member_add(struct hash *ht, uint32_t src) { struct rtp_member *mbr; mbr = mem_zalloc(sizeof(*mbr), destructor); if (!mbr) return NULL; hash_append(ht, src, &mbr->le, mbr); mbr->src = src; return mbr; } static bool hash_cmp_handler(struct le *le, void *arg) { const struct rtp_member *mbr = le->data; return mbr->src == *(uint32_t *)arg; } struct rtp_member *member_find(struct hash *ht, uint32_t src) { return list_ledata(hash_lookup(ht, src, hash_cmp_handler, &src)); } re-1.1.0/src/rtp/mod.mk000066400000000000000000000003541373627245400146420ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += rtp/fb.c SRCS += rtp/member.c SRCS += rtp/ntp.c SRCS += rtp/pkt.c SRCS += rtp/rr.c SRCS += rtp/rtcp.c SRCS += rtp/rtp.c SRCS += rtp/sdes.c SRCS += rtp/sess.c SRCS += rtp/source.c re-1.1.0/src/rtp/ntp.c000066400000000000000000000041101373627245400144710ustar00rootroot00000000000000/** * @file ntp.c NTP Routines * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_SYS_TIME_H #include #else #include #endif #include #include #include #include #include #include "rtcp.h" /* * Unix time: seconds relative to 0h January 1, 1970 * NTP time: seconds relative to 0h UTC on 1 January 1900 */ /* Number of seconds from 1900 to 1970 */ #define UNIX_NTP_OFFSET 0x83aa7e80 /** * Convert from Unix time to NTP time * * @param ntp NTP time to convert to (output) * @param tv Unix time to convert from (input) */ void unix2ntp(struct ntp_time *ntp, const struct timeval *tv) { ntp->hi = (uint32_t)(tv->tv_sec + UNIX_NTP_OFFSET); ntp->lo = (uint32_t)((double)tv->tv_usec*(double)(1LL<<32)*1.0e-6); } /** * Convert from NTP time to Unix time * * @param tv Unix time to convert to (output) * @param ntp NTP time to convert from (input) */ void ntp2unix(struct timeval *tv, const struct ntp_time *ntp) { tv->tv_sec = ntp->hi - UNIX_NTP_OFFSET; tv->tv_usec = (uint32_t)(1.0e6 * (double) ntp->lo / (1LL<<32)); } int ntp_time_get(struct ntp_time *ntp) { struct timeval tv; #ifdef WIN32 union { long long ns100; FILETIME ft; } now; GetSystemTimeAsFileTime(&now.ft); tv.tv_usec = (long) ((now.ns100 / 10LL) % 1000000LL); tv.tv_sec = (long) ((now.ns100 - 116444736000000000LL) / 10000000LL); #else if (gettimeofday(&tv, NULL) != 0) return errno; #endif unix2ntp(ntp, &tv); return 0; } /** * Convert NTP time to middle 32-bits (compact representation) * * @param ntp NTP time * * @return NTP time in compact representation */ uint32_t ntp_compact(const struct ntp_time *ntp) { return ntp ? ((ntp->hi & 0xffff) << 16 | (ntp->lo >> 16)) : 0; } /** * Convert NTP compact representation to microseconds * * @param ntpc NTP time in compact representation * * @return NTP time in microseconds */ uint64_t ntp_compact2us(uint32_t ntpc) { const uint32_t hi = (ntpc >> 16) & 0xffff; const uint32_t lo = (ntpc & 0xffff) << 16; return (1000000ULL * hi) + ((1000000ULL * lo) >> 32); } re-1.1.0/src/rtp/pkt.c000066400000000000000000000222111373627245400144700ustar00rootroot00000000000000/** * @file pkt.c RTCP Packet handling * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "rtcp.h" #define DEBUG_MODULE "rtcp_pkt" #define DEBUG_LEVEL 5 #include static void rtcp_destructor(void *data) { struct rtcp_msg *msg = data; size_t i, j; switch (msg->hdr.pt) { case RTCP_SR: mem_deref(msg->r.sr.rrv); break; case RTCP_RR: mem_deref(msg->r.rr.rrv); break; case RTCP_SDES: if (!msg->r.sdesv) break; for (i=0; ihdr.count; i++) { struct rtcp_sdes *sdes = &msg->r.sdesv[i]; for (j=0; jn; j++) { mem_deref(sdes->itemv[j].data); } mem_deref(sdes->itemv); } mem_deref(msg->r.sdesv); break; case RTCP_BYE: mem_deref(msg->r.bye.srcv); mem_deref(msg->r.bye.reason); break; case RTCP_APP: mem_deref(msg->r.app.data); break; case RTCP_RTPFB: case RTCP_PSFB: mem_deref(msg->r.fb.fci.p); break; default: /* nothing allocated */ break; } } /** * Encode the RTCP Header * * @param mb Buffer to encode into * @param count Number of sub-elemements * @param type RTCP Packet type * @param length Packet length in words * * @return 0 for success, otherwise errorcode */ int rtcp_hdr_encode(struct mbuf *mb, uint8_t count, enum rtcp_type type, uint16_t length) { int err; if (!mb) return EINVAL; err = mbuf_write_u8(mb, RTCP_VERSION<<6 | count); err |= mbuf_write_u8(mb, type); err |= mbuf_write_u16(mb, htons(length)); return err; } /** * Decode the RTCP Header * * @param mb Buffer to decode from * @param hdr RTCP Header to decode into * * @return 0 for success, otherwise errorcode */ int rtcp_hdr_decode(struct mbuf *mb, struct rtcp_hdr *hdr) { uint8_t b; if (!hdr) return EINVAL; if (mbuf_get_left(mb) < RTCP_HDR_SIZE) return EBADMSG; b = mbuf_read_u8(mb); hdr->pt = mbuf_read_u8(mb); hdr->length = ntohs(mbuf_read_u16(mb)); hdr->version = (b >> 6) & 0x3; hdr->p = (b >> 5) & 0x1; hdr->count = (b >> 0) & 0x1f; return 0; } int rtcp_vencode(struct mbuf *mb, enum rtcp_type type, uint32_t count, va_list ap) { size_t i, pos; uint16_t len; const uint8_t *data; size_t data_len; const uint32_t *srcv; const char *reason; rtcp_encode_h *ench; void *arg; int err = 0; if (!mb) return EINVAL; pos = mb->pos; /* Skip header - encoded last */ mb->pos = mb->end = (mb->pos + RTCP_HDR_SIZE); switch (type) { case RTCP_SR: for (i=0; i<6; i++) err |= mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t))); ench = va_arg(ap, rtcp_encode_h *); arg = va_arg(ap, void *); if (ench) err |= ench(mb, arg); break; case RTCP_RR: err = mbuf_write_u32(mb, htonl(va_arg(ap, uint32_t))); ench = va_arg(ap, rtcp_encode_h *); arg = va_arg(ap, void *); if (ench) err |= ench(mb, arg); break; case RTCP_SDES: ench = va_arg(ap, rtcp_encode_h *); arg = va_arg(ap, void *); if (ench) err |= ench(mb, arg); break; case RTCP_BYE: srcv = va_arg(ap, uint32_t *); reason = va_arg(ap, char *); for (i=0; iend - pos) & 0x3) err |= mbuf_write_u8(mb, 0x00); if (err) return err; /* Encode RTCP Header */ mb->pos = pos; len = (mb->end - pos - RTCP_HDR_SIZE)/sizeof(uint32_t); err = rtcp_hdr_encode(mb, count, type, len); if (err) return err; mb->pos = mb->end; return 0; } /** * Encode an RTCP Packet into a buffer * * @param mb Buffer to encode into * @param type RTCP Packet type * @param count Packet-specific count * * @return 0 for success, otherwise errorcode */ int rtcp_encode(struct mbuf *mb, enum rtcp_type type, uint32_t count, ...) { va_list ap; int err; va_start(ap, count); err = rtcp_vencode(mb, type, count, ap); va_end(ap); return err; } /** * Decode one RTCP message from a buffer * * @param msgp Pointer to allocated RTCP Message * @param mb Buffer to decode from * * @return 0 for success, otherwise errorcode */ int rtcp_decode(struct rtcp_msg **msgp, struct mbuf *mb) { struct rtcp_msg *msg = NULL; size_t start, i, sz, count, rem; int err; if (!msgp) return EINVAL; if (mbuf_get_left(mb) < RTCP_HDR_SIZE) return EBADMSG; msg = mem_zalloc(sizeof(*msg), rtcp_destructor); if (!msg) return ENOMEM; start = mb->pos; /* decode and check header */ err = rtcp_hdr_decode(mb, &msg->hdr); if (err) goto out; if (msg->hdr.version != RTCP_VERSION) goto badmsg; /* check length and remaining */ rem = msg->hdr.length * sizeof(uint32_t); if (mbuf_get_left(mb) < rem) goto badmsg; count = msg->hdr.count; switch (msg->hdr.pt) { case RTCP_SR: if (mbuf_get_left(mb) < (RTCP_SRC_SIZE + RTCP_SR_SIZE)) goto badmsg; msg->r.sr.ssrc = ntohl(mbuf_read_u32(mb)); msg->r.sr.ntp_sec = ntohl(mbuf_read_u32(mb)); msg->r.sr.ntp_frac = ntohl(mbuf_read_u32(mb)); msg->r.sr.rtp_ts = ntohl(mbuf_read_u32(mb)); msg->r.sr.psent = ntohl(mbuf_read_u32(mb)); msg->r.sr.osent = ntohl(mbuf_read_u32(mb)); err = rtcp_rr_alloc(&msg->r.sr.rrv, count); if (err) goto out; for (i=0; ir.sr.rrv[i]); break; case RTCP_RR: if (mbuf_get_left(mb) < RTCP_SRC_SIZE) goto badmsg; msg->r.rr.ssrc = ntohl(mbuf_read_u32(mb)); err = rtcp_rr_alloc(&msg->r.rr.rrv, count); if (err) goto out; for (i=0; ir.rr.rrv[i]); break; case RTCP_SDES: if (count == 0) break; sz = count * sizeof(*msg->r.sdesv); msg->r.sdesv = mem_zalloc(sz, NULL); if (!msg->r.sdesv) { err = ENOMEM; goto out; } for (i=0; ihdr.count && !err; i++) err = rtcp_sdes_decode(mb, &msg->r.sdesv[i]); break; case RTCP_BYE: sz = count * sizeof(*msg->r.bye.srcv); msg->r.bye.srcv = mem_alloc(sz, NULL); if (!msg->r.bye.srcv) { err = ENOMEM; goto out; } if (mbuf_get_left(mb) < sz) goto badmsg; for (i=0; ir.bye.srcv[i] = ntohl(mbuf_read_u32(mb)); /* decode reason (optional) */ if (rem > count*sizeof(uint32_t)) { const size_t len = mbuf_read_u8(mb); if (mbuf_get_left(mb) < len) goto badmsg; err = mbuf_strdup(mb, &msg->r.bye.reason, len); } break; case RTCP_APP: if (mbuf_get_left(mb) < RTCP_APP_SIZE) goto badmsg; msg->r.app.src = ntohl(mbuf_read_u32(mb)); (void)mbuf_read_mem(mb, (uint8_t *)msg->r.app.name, sizeof(msg->r.app.name)); if (rem > RTCP_APP_SIZE) { msg->r.app.data_len = rem - RTCP_APP_SIZE; msg->r.app.data = mem_alloc(msg->r.app.data_len, NULL); if (!msg->r.app.data) { err = ENOMEM; goto out; } if (mbuf_get_left(mb) < msg->r.app.data_len) goto badmsg; (void)mbuf_read_mem(mb, msg->r.app.data, msg->r.app.data_len); } break; case RTCP_FIR: if (mbuf_get_left(mb) < RTCP_FIR_SIZE) goto badmsg; msg->r.fir.ssrc = ntohl(mbuf_read_u32(mb)); break; case RTCP_NACK: if (mbuf_get_left(mb) < RTCP_NACK_SIZE) goto badmsg; msg->r.nack.ssrc = ntohl(mbuf_read_u32(mb)); msg->r.nack.fsn = ntohs(mbuf_read_u16(mb)); msg->r.nack.blp = ntohs(mbuf_read_u16(mb)); break; case RTCP_RTPFB: if (mbuf_get_left(mb) < RTCP_FB_SIZE) goto badmsg; if (msg->hdr.length < 2) goto badmsg; msg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb)); msg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb)); msg->r.fb.n = msg->hdr.length - 2; err = rtcp_rtpfb_decode(mb, msg); break; case RTCP_PSFB: if (mbuf_get_left(mb) < RTCP_FB_SIZE) goto badmsg; if (msg->hdr.length < 2) goto badmsg; msg->r.fb.ssrc_packet = ntohl(mbuf_read_u32(mb)); msg->r.fb.ssrc_media = ntohl(mbuf_read_u32(mb)); msg->r.fb.n = msg->hdr.length - 2; err = rtcp_psfb_decode(mb, msg); break; default: /* unknown message type */ mbuf_advance(mb, rem); break; } if (err) goto out; /* slurp padding */ while ((mb->pos - start) & 0x3 && mbuf_get_left(mb)) ++mb->pos; out: if (err) mem_deref(msg); else *msgp = msg; return err; badmsg: mem_deref(msg); return EBADMSG; } re-1.1.0/src/rtp/rr.c000066400000000000000000000026141373627245400143220ustar00rootroot00000000000000/** * @file rtp/rr.c RTCP Reception report * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "rtcp.h" int rtcp_rr_alloc(struct rtcp_rr **rrp, size_t count) { struct rtcp_rr *rr; if (!rrp) return EINVAL; rr = mem_alloc(count * sizeof(*rr), NULL); if (!rr) return ENOMEM; *rrp = rr; return 0; } int rtcp_rr_encode(struct mbuf *mb, const struct rtcp_rr *rr) { int err; if (!mb || !rr) return EINVAL; err = mbuf_write_u32(mb, htonl(rr->ssrc)); err |= mbuf_write_u32(mb, htonl(rr->fraction<<24 | (rr->lost & 0x00ffffff))); err |= mbuf_write_u32(mb, htonl(rr->last_seq)); err |= mbuf_write_u32(mb, htonl(rr->jitter)); err |= mbuf_write_u32(mb, htonl(rr->lsr)); err |= mbuf_write_u32(mb, htonl(rr->dlsr)); return err; } int rtcp_rr_decode(struct mbuf *mb, struct rtcp_rr *rr) { uint32_t w; if (!rr) return EINVAL; if (mbuf_get_left(mb) < RTCP_RR_SIZE) return EBADMSG; rr->ssrc = ntohl(mbuf_read_u32(mb)); w = ntohl(mbuf_read_u32(mb)); rr->fraction = w>>24; rr->lost = w & 0x00ffffffU; rr->last_seq = ntohl(mbuf_read_u32(mb)); rr->jitter = ntohl(mbuf_read_u32(mb)); rr->lsr = ntohl(mbuf_read_u32(mb)); rr->dlsr = ntohl(mbuf_read_u32(mb)); return 0; } re-1.1.0/src/rtp/rtcp.c000066400000000000000000000145241373627245400146520ustar00rootroot00000000000000/** * @file rtcp.c Real-time Transport Control Protocol * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "rtcp.h" static int rtcp_quick_send(struct rtp_sock *rs, enum rtcp_type type, uint32_t count, ...) { struct mbuf *mb; va_list ap; int err; mb = mbuf_alloc(32); if (!mb) return ENOMEM; mb->pos = RTCP_HEADROOM; va_start(ap, count); err = rtcp_vencode(mb, type, count, ap); va_end(ap); mb->pos = RTCP_HEADROOM; if (!err) err = rtcp_send(rs, mb); mem_deref(mb); return err; } /** * Send an RTCP Application-Defined (APP) packet * * @param rs RTP Socket * @param name Ascii name (4 octets) * @param data Application-dependent data * @param len Number of bytes of data * * @return 0 for success, otherwise errorcode */ int rtcp_send_app(struct rtp_sock *rs, const char name[4], const uint8_t *data, size_t len) { return rtcp_quick_send(rs, RTCP_APP, 0, rtp_sess_ssrc(rs), name, data, len); } /** * Send a Full INTRA-frame Request (FIR) packet * * @param rs RTP Socket * @param ssrc Synchronization source identifier for the sender of this packet * * @return 0 for success, otherwise errorcode */ int rtcp_send_fir(struct rtp_sock *rs, uint32_t ssrc) { return rtcp_quick_send(rs, RTCP_FIR, 0, ssrc); } /** * Send an RTCP NACK packet * * @param rs RTP Socket * @param fsn First Sequence Number lost * @param blp Bitmask of lost packets * * @return 0 for success, otherwise errorcode */ int rtcp_send_nack(struct rtp_sock *rs, uint16_t fsn, uint16_t blp) { return rtcp_quick_send(rs, RTCP_NACK, 0, rtp_sess_ssrc(rs), fsn, blp); } /** * Send an RTCP Picture Loss Indication (PLI) packet * * @param rs RTP Socket * @param fb_ssrc Feedback SSRC * * @return 0 for success, otherwise errorcode */ int rtcp_send_pli(struct rtp_sock *rs, uint32_t fb_ssrc) { return rtcp_quick_send(rs, RTCP_PSFB, RTCP_PSFB_PLI, rtp_sess_ssrc(rs), fb_ssrc, NULL, NULL); } const char *rtcp_type_name(enum rtcp_type type) { switch (type) { case RTCP_FIR: return "FIR"; case RTCP_NACK: return "NACK"; case RTCP_SR: return "SR"; case RTCP_RR: return "RR"; case RTCP_SDES: return "SDES"; case RTCP_BYE: return "BYE"; case RTCP_APP: return "APP"; case RTCP_RTPFB: return "RTPFB"; case RTCP_PSFB: return "PSFB"; case RTCP_XR: return "XR"; case RTCP_AVB: return "AVB"; default: return "?"; } } const char *rtcp_sdes_name(enum rtcp_sdes_type sdes) { switch (sdes) { case RTCP_SDES_END: return "END"; case RTCP_SDES_CNAME: return "CNAME"; case RTCP_SDES_NAME: return "NAME"; case RTCP_SDES_EMAIL: return "EMAIL"; case RTCP_SDES_PHONE: return "PHONE"; case RTCP_SDES_LOC: return "LOC"; case RTCP_SDES_TOOL: return "TOOL"; case RTCP_SDES_NOTE: return "NOTE"; case RTCP_SDES_PRIV: return "PRIV"; default: return "?"; } } /** * Print an RTCP Message * * @param pf Print handler for debug output * @param msg RTCP Message * * @return 0 if success, otherwise errorcode */ int rtcp_msg_print(struct re_printf *pf, const struct rtcp_msg *msg) { size_t i, j; int err; if (!msg) return 0; err = re_hprintf(pf, "%8s pad=%d count=%-2d pt=%-3d len=%u ", rtcp_type_name((enum rtcp_type)msg->hdr.pt), msg->hdr.p, msg->hdr.count, msg->hdr.pt, msg->hdr.length); if (err) return err; switch (msg->hdr.pt) { case RTCP_SR: err = re_hprintf(pf, "%08x %u %u %u %u %u", msg->r.sr.ssrc, msg->r.sr.ntp_sec, msg->r.sr.ntp_frac, msg->r.sr.rtp_ts, msg->r.sr.psent, msg->r.sr.osent); for (i=0; ihdr.count && !err; i++) { const struct rtcp_rr *rr = &msg->r.sr.rrv[i]; err = re_hprintf(pf, " {%08x %u %d %u %u %u %u}", rr->ssrc, rr->fraction, rr->lost, rr->last_seq, rr->jitter, rr->lsr, rr->dlsr); } break; case RTCP_RR: err = re_hprintf(pf, "%08x", msg->r.rr.ssrc); for (i=0; ihdr.count && !err; i++) { const struct rtcp_rr *rr = &msg->r.rr.rrv[i]; err = re_hprintf(pf, " {0x%08x %u %d %u %u %u %u}", rr->ssrc, rr->fraction, rr->lost, rr->last_seq, rr->jitter, rr->lsr, rr->dlsr); } break; case RTCP_SDES: for (i=0; ihdr.count; i++) { const struct rtcp_sdes *sdes = &msg->r.sdesv[i]; err = re_hprintf(pf, " {0x%08x n=%u", sdes->src, sdes->n); for (j=0; jn && !err; j++) { const struct rtcp_sdes_item *item; item = &sdes->itemv[j]; err = re_hprintf(pf, " <%s:%b>", rtcp_sdes_name(item->type), item->data, (size_t)item->length); } err |= re_hprintf(pf, "}"); } break; case RTCP_BYE: err = re_hprintf(pf, "%u srcs:", msg->hdr.count); for (i=0; ihdr.count && !err; i++) { err = re_hprintf(pf, " %08x", msg->r.bye.srcv[i]); } err |= re_hprintf(pf, " '%s'", msg->r.bye.reason); break; case RTCP_APP: err = re_hprintf(pf, "src=%08x '%b' data=%zu", msg->r.app.src, msg->r.app.name, sizeof(msg->r.app.name), msg->r.app.data_len); break; case RTCP_FIR: err = re_hprintf(pf, "ssrc=%08x", msg->r.fir.ssrc); break; case RTCP_NACK: err = re_hprintf(pf, "ssrc=%08x fsn=%04x blp=%04x", msg->r.nack.ssrc, msg->r.nack.fsn, msg->r.nack.blp); break; case RTCP_RTPFB: err = re_hprintf(pf, "pkt=%08x med=%08x n=%u", msg->r.fb.ssrc_packet, msg->r.fb.ssrc_media, msg->r.fb.n); if (msg->hdr.count == RTCP_RTPFB_GNACK) { err |= re_hprintf(pf, " GNACK"); for (i=0; ir.fb.n; i++) { err |= re_hprintf(pf, " {%04x %04x}", msg->r.fb.fci.gnackv[i].pid, msg->r.fb.fci.gnackv[i].blp); } } break; case RTCP_PSFB: err = re_hprintf(pf, "pkt=%08x med=%08x n=%u", msg->r.fb.ssrc_packet, msg->r.fb.ssrc_media, msg->r.fb.n); if (msg->hdr.count == RTCP_PSFB_SLI) { err |= re_hprintf(pf, " SLI"); for (i=0; ir.fb.n; i++) { err |= re_hprintf(pf, " {%04x %04x %02x}", msg->r.fb.fci.sliv[i].first, msg->r.fb.fci.sliv[i].number, msg->r.fb.fci.sliv[i].picid); } } else if (msg->hdr.count == RTCP_PSFB_AFB) { err |= re_hprintf(pf, " AFB %u bytes", msg->r.fb.n * 4); } break; default: err = re_hprintf(pf, "", msg->hdr.length); break; } err |= re_hprintf(pf, "\n"); return err; } re-1.1.0/src/rtp/rtcp.h000066400000000000000000000115351373627245400146560ustar00rootroot00000000000000/** * @file rtcp.h Internal interface to RTCP * * Copyright (C) 2010 Creytiv.com */ /** RTCP protocol values */ enum { RTCP_HDR_SIZE = 4, /**< Size of common RTCP header */ RTCP_SRC_SIZE = 4, /**< Size of Source field */ RTCP_SR_SIZE = 20, /**< Size of Sender Information */ RTCP_RR_SIZE = 24, /**< Size of Report Block */ RTCP_APP_SIZE = 8, /**< Size of Application packet */ RTCP_FIR_SIZE = 4, /**< Size of FIR packet */ RTCP_NACK_SIZE = 8, /**< Size of NACK packet */ RTCP_FB_SIZE = 8, /**< Size of Feedback packets */ RTCP_MAX_SDES = 255, /**< Maximum text length for SDES */ RTCP_HEADROOM = 4, /**< Headroom in RTCP packets */ }; /** NTP Time */ struct ntp_time { uint32_t hi; /**< Seconds since 0h UTC on 1 January 1900 */ uint32_t lo; /**< Fraction of seconds */ }; struct hash; /** Per-source state information */ struct rtp_source { struct sa rtp_peer; /**< IP-address of the RTP source */ uint16_t max_seq; /**< Highest seq. number seen */ uint32_t cycles; /**< Shifted count of seq. number cycles */ uint32_t base_seq; /**< Base seq number */ uint32_t bad_seq; /**< Last 'bad' seq number + 1 */ uint32_t probation; /**< Sequ. packets till source is valid */ uint32_t received; /**< Packets received */ uint32_t expected_prior; /**< Packet expected at last interval */ uint32_t received_prior; /**< Packet received at last interval */ int transit; /**< Relative trans time for prev pkt */ uint32_t jitter; /**< Estimated jitter */ size_t rtp_rx_bytes; /**< Number of RTP bytes received */ uint64_t sr_recv; /**< When the last SR was received */ struct ntp_time last_sr; /**< NTP Timestamp from last SR received */ uint32_t rtp_ts; /**< RTP timestamp */ uint32_t psent; /**< RTP packets sent */ uint32_t osent; /**< RTP octets sent */ }; /** RTP Member */ struct rtp_member { struct le le; /**< Hash-table element */ struct rtp_source *s; /**< RTP source state */ uint32_t src; /**< Source - used for hash-table lookup */ int cum_lost; /**< Cumulative number of packets lost */ uint32_t jit; /**< Jitter in [us] */ uint32_t rtt; /**< Round-trip time in [us] */ }; /* Member */ struct rtp_member *member_add(struct hash *ht, uint32_t src); struct rtp_member *member_find(struct hash *ht, uint32_t src); /* Source */ void source_init_seq(struct rtp_source *s, uint16_t seq); int source_update_seq(struct rtp_source *s, uint16_t seq); void source_calc_jitter(struct rtp_source *s, uint32_t rtp_ts, uint32_t arrival); int source_calc_lost(const struct rtp_source *s); uint8_t source_calc_fraction_lost(struct rtp_source *s); /* RR (Reception report) */ int rtcp_rr_alloc(struct rtcp_rr **rrp, size_t count); int rtcp_rr_encode(struct mbuf *mb, const struct rtcp_rr *rr); int rtcp_rr_decode(struct mbuf *mb, struct rtcp_rr *rr); /* SDES (Source Description) */ int rtcp_sdes_decode(struct mbuf *mb, struct rtcp_sdes *sdes); /* RTCP Feedback */ int rtcp_rtpfb_gnack_encode(struct mbuf *mb, uint16_t pid, uint16_t blp); int rtcp_psfb_sli_encode(struct mbuf *mb, uint16_t first, uint16_t number, uint8_t picid); int rtcp_rtpfb_decode(struct mbuf *mb, struct rtcp_msg *msg); int rtcp_psfb_decode(struct mbuf *mb, struct rtcp_msg *msg); /** NTP Time */ struct timeval; void unix2ntp(struct ntp_time *ntp, const struct timeval *tv); void ntp2unix(struct timeval *tv, const struct ntp_time *ntp); int ntp_time_get(struct ntp_time *ntp); uint32_t ntp_compact(const struct ntp_time *ntp); uint64_t ntp_compact2us(uint32_t ntpc); /* RTP Socket */ struct rtcp_sess *rtp_rtcp_sess(const struct rtp_sock *rs); /* RTCP message */ typedef int (rtcp_encode_h)(struct mbuf *mb, void *arg); int rtcp_hdr_encode(struct mbuf *mb, uint8_t count, enum rtcp_type type, uint16_t length); int rtcp_hdr_decode(struct mbuf *mb, struct rtcp_hdr *hdr); int rtcp_vencode(struct mbuf *mb, enum rtcp_type type, uint32_t count, va_list ap); /* RTCP Session */ struct rtcp_sess; int rtcp_sess_alloc(struct rtcp_sess **sessp, struct rtp_sock *rs); int rtcp_enable(struct rtcp_sess *sess, bool enabled, const char *cname); int rtcp_send(struct rtp_sock *rs, struct mbuf *mb); void rtcp_handler(struct rtcp_sess *sess, struct rtcp_msg *msg); void rtcp_sess_tx_rtp(struct rtcp_sess *sess, uint32_t ts, size_t payload_size); void rtcp_sess_rx_rtp(struct rtcp_sess *sess, uint16_t seq, uint32_t ts, uint32_t src, size_t payload_size, const struct sa *peer); re-1.1.0/src/rtp/rtp.c000066400000000000000000000264551373627245400145150ustar00rootroot00000000000000/** * @file rtp.c Real-time Transport Protocol * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "rtcp.h" #define DEBUG_MODULE "rtp" #define DEBUG_LEVEL 5 #include /** Defines an RTP Socket */ struct rtp_sock { /** Encode data */ struct { uint16_t seq; /**< Sequence number */ uint32_t ssrc; /**< Synchronizing source */ } enc; int proto; /**< Transport Protocol */ void *sock_rtp; /**< RTP Socket */ void *sock_rtcp; /**< RTCP Socket */ struct sa local; /**< Local RTP Address */ struct sa rtcp_peer; /**< RTCP address of Peer */ rtp_recv_h *recvh; /**< RTP Receive handler */ rtcp_recv_h *rtcph; /**< RTCP Receive handler */ void *arg; /**< Handler argument */ struct rtcp_sess *rtcp; /**< RTCP Session */ bool rtcp_mux; /**< RTP/RTCP multiplexing */ }; /** * Encode the RTP header into a buffer * * @param mb Buffer to encode into * @param hdr RTP Header to be encoded * * @return 0 if success, otherwise errorcode */ int rtp_hdr_encode(struct mbuf *mb, const struct rtp_header *hdr) { uint8_t buf[2]; int err, i; if (!mb || !hdr) return EINVAL; buf[0] = (hdr->ver & 0x02) << 6; buf[0] |= (hdr->pad & 0x01) << 5; buf[0] |= (hdr->ext & 0x01) << 4; buf[0] |= (hdr->cc & 0x0f) << 0; buf[1] = (hdr->m & 0x01) << 7; buf[1] |= (hdr->pt & 0x7f) << 0; err = mbuf_write_mem(mb, buf, sizeof(buf)); err |= mbuf_write_u16(mb, htons(hdr->seq)); err |= mbuf_write_u32(mb, htonl(hdr->ts)); err |= mbuf_write_u32(mb, htonl(hdr->ssrc)); for (i=0; icc; i++) { err |= mbuf_write_u32(mb, htonl(hdr->csrc[i])); } return err; } /** * Decode an RTP header from a buffer * * @param hdr RTP Header to decode into * @param mb Buffer to decode from * * @return 0 if success, otherwise errorcode */ int rtp_hdr_decode(struct rtp_header *hdr, struct mbuf *mb) { uint8_t buf[2]; int err, i; size_t header_len; if (!hdr || !mb) return EINVAL; if (mbuf_get_left(mb) < RTP_HEADER_SIZE) return EBADMSG; err = mbuf_read_mem(mb, buf, sizeof(buf)); if (err) return err; hdr->ver = (buf[0] >> 6) & 0x03; hdr->pad = (buf[0] >> 5) & 0x01; hdr->ext = (buf[0] >> 4) & 0x01; hdr->cc = (buf[0] >> 0) & 0x0f; hdr->m = (buf[1] >> 7) & 0x01; hdr->pt = (buf[1] >> 0) & 0x7f; hdr->seq = ntohs(mbuf_read_u16(mb)); hdr->ts = ntohl(mbuf_read_u32(mb)); hdr->ssrc = ntohl(mbuf_read_u32(mb)); header_len = hdr->cc*sizeof(uint32_t); if (mbuf_get_left(mb) < header_len) return EBADMSG; for (i=0; icc; i++) { hdr->csrc[i] = ntohl(mbuf_read_u32(mb)); } if (hdr->ext) { if (mbuf_get_left(mb) < 4) return EBADMSG; hdr->x.type = ntohs(mbuf_read_u16(mb)); hdr->x.len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < hdr->x.len*sizeof(uint32_t)) return EBADMSG; mb->pos += hdr->x.len*sizeof(uint32_t); } return 0; } static void destructor(void *data) { struct rtp_sock *rs = data; switch (rs->proto) { case IPPROTO_UDP: udp_handler_set(rs->sock_rtp, NULL, NULL); udp_handler_set(rs->sock_rtcp, NULL, NULL); break; default: break; } /* Destroy RTCP Session now */ mem_deref(rs->rtcp); mem_deref(rs->sock_rtp); mem_deref(rs->sock_rtcp); } static void rtcp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct rtp_sock *rs = arg; struct rtcp_msg *msg; while (0 == rtcp_decode(&msg, mb)) { /* handle internally first */ rtcp_handler(rs->rtcp, msg); /* then relay to application */ if (rs->rtcph) rs->rtcph(src, msg, rs->arg); mem_deref(msg); } } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct rtp_sock *rs = arg; struct rtp_header hdr; int err; /* Handle RTCP multiplexed on RTP-port */ if (rs->rtcp_mux) { uint8_t pt; if (mbuf_get_left(mb) < 2) return; pt = mbuf_buf(mb)[1] & 0x7f; if (64 <= pt && pt <= 95) { rtcp_recv_handler(src, mb, arg); return; } } err = rtp_decode(rs, mb, &hdr); if (err) return; if (rs->rtcp) { rtcp_sess_rx_rtp(rs->rtcp, hdr.seq, hdr.ts, hdr.ssrc, mbuf_get_left(mb), src); } if (rs->recvh) rs->recvh(src, &hdr, mb, rs->arg); } static int udp_range_listen(struct rtp_sock *rs, const struct sa *ip, uint16_t min_port, uint16_t max_port) { struct sa rtcp; int tries = 64; int err = 0; rs->local = rtcp = *ip; /* try hard */ while (tries--) { struct udp_sock *us_rtp, *us_rtcp; uint16_t port; port = (min_port + (rand_u16() % (max_port - min_port))); port &= 0xfffe; sa_set_port(&rs->local, port); err = udp_listen(&us_rtp, &rs->local, udp_recv_handler, rs); if (err) continue; sa_set_port(&rtcp, port + 1); err = udp_listen(&us_rtcp, &rtcp, rtcp_recv_handler, rs); if (err) { mem_deref(us_rtp); continue; } /* OK */ rs->sock_rtp = us_rtp; rs->sock_rtcp = us_rtcp; break; } return err; } /** * Allocate a new RTP socket * * @param rsp Pointer to returned RTP socket * * @return 0 for success, otherwise errorcode */ int rtp_alloc(struct rtp_sock **rsp) { struct rtp_sock *rs; if (!rsp) return EINVAL; rs = mem_zalloc(sizeof(*rs), destructor); if (!rs) return ENOMEM; sa_init(&rs->rtcp_peer, AF_UNSPEC); rs->enc.seq = rand_u16() & 0x7fff; rs->enc.ssrc = rand_u32(); *rsp = rs; return 0; } /** * Listen on an RTP/RTCP Socket * * @param rsp Pointer to returned RTP socket * @param proto Transport protocol * @param ip Local IP address * @param min_port Minimum port range * @param max_port Maximum port range * @param enable_rtcp True to enable RTCP Session * @param recvh RTP Receive handler * @param rtcph RTCP Receive handler * @param arg Handler argument * * @return 0 for success, otherwise errorcode */ int rtp_listen(struct rtp_sock **rsp, int proto, const struct sa *ip, uint16_t min_port, uint16_t max_port, bool enable_rtcp, rtp_recv_h *recvh, rtcp_recv_h *rtcph, void *arg) { struct rtp_sock *rs; int err; if (!ip || min_port >= max_port || !recvh) return EINVAL; err = rtp_alloc(&rs); if (err) return err; rs->proto = proto; rs->recvh = recvh; rs->rtcph = rtcph; rs->arg = arg; /* Optional RTCP */ if (enable_rtcp) { err = rtcp_sess_alloc(&rs->rtcp, rs); if (err) goto out; } switch (proto) { case IPPROTO_UDP: err = udp_range_listen(rs, ip, min_port, max_port); break; default: err = EPROTONOSUPPORT; break; } out: if (err) mem_deref(rs); else *rsp = rs; return err; } /** * Encode a new RTP header into the beginning of the buffer * * @param rs RTP Socket * @param ext Extension bit * @param marker Marker bit * @param pt Payload type * @param ts Timestamp * @param mb Memory buffer * * @return 0 for success, otherwise errorcode * * @note The buffer must have enough space for the RTP header */ int rtp_encode(struct rtp_sock *rs, bool ext, bool marker, uint8_t pt, uint32_t ts, struct mbuf *mb) { struct rtp_header hdr; if (!rs || pt&~0x7f || !mb) return EINVAL; hdr.ver = RTP_VERSION; hdr.pad = false; hdr.ext = ext; hdr.cc = 0; hdr.m = marker ? 1 : 0; hdr.pt = pt; hdr.seq = rs->enc.seq++; hdr.ts = ts; hdr.ssrc = rs->enc.ssrc; return rtp_hdr_encode(mb, &hdr); } /** * Decode an RTP packet and return decoded RTP header and payload * * @param rs RTP Socket * @param mb Memory buffer containing RTP packet * @param hdr RTP header (set on return) * * @return 0 for success, otherwise errorcode */ int rtp_decode(struct rtp_sock *rs, struct mbuf *mb, struct rtp_header *hdr) { int err; if (!rs || !mb || !hdr) return EINVAL; memset(hdr, 0, sizeof(*hdr)); err = rtp_hdr_decode(hdr, mb); if (err) return err; if (RTP_VERSION != hdr->ver) return EBADMSG; return 0; } /** * Send an RTP packet to a peer * * @param rs RTP Socket * @param dst Destination address * @param ext Extension bit * @param marker Marker bit * @param pt Payload type * @param ts Timestamp * @param mb Payload buffer * * @return 0 for success, otherwise errorcode */ int rtp_send(struct rtp_sock *rs, const struct sa *dst, bool ext, bool marker, uint8_t pt, uint32_t ts, struct mbuf *mb) { size_t pos; int err; if (!rs || !mb) return EINVAL; if (mb->pos < RTP_HEADER_SIZE) { DEBUG_WARNING("rtp_send: buffer must have space for" " rtp header (pos=%u, end=%u)\n", mb->pos, mb->end); return EBADMSG; } mbuf_advance(mb, -RTP_HEADER_SIZE); pos = mb->pos; err = rtp_encode(rs, ext, marker, pt, ts, mb); if (err) return err; if (rs->rtcp) rtcp_sess_tx_rtp(rs->rtcp, ts, mbuf_get_left(mb)); mb->pos = pos; return udp_send(rs->sock_rtp, dst, mb); } /** * Get the RTP transport socket from an RTP/RTCP Socket * * @param rs RTP Socket * * @return Transport socket for RTP */ void *rtp_sock(const struct rtp_sock *rs) { return rs ? rs->sock_rtp : NULL; } /** * Get the RTCP transport socket from an RTP/RTCP Socket * * @param rs RTP Socket * * @return Transport socket for RTCP */ void *rtcp_sock(const struct rtp_sock *rs) { return rs ? rs->sock_rtcp : NULL; } /** * Get the local RTP address for an RTP/RTCP Socket * * @param rs RTP Socket * * @return Local RTP address */ const struct sa *rtp_local(const struct rtp_sock *rs) { return rs ? &rs->local : NULL; } /** * Get the Synchronizing source for an RTP/RTCP Socket * * @param rs RTP Socket * * @return Synchronizing source */ uint32_t rtp_sess_ssrc(const struct rtp_sock *rs) { return rs ? rs->enc.ssrc : 0; } /** * Get the RTCP-Session for an RTP/RTCP Socket * * @param rs RTP Socket * * @return RTCP-Session */ struct rtcp_sess *rtp_rtcp_sess(const struct rtp_sock *rs) { return rs ? rs->rtcp : NULL; } /** * Start the RTCP Session * * @param rs RTP Socket * @param cname Canonical Name * @param peer IP-Address of RTCP Peer */ void rtcp_start(struct rtp_sock *rs, const char *cname, const struct sa *peer) { if (!rs) return; if (peer) rs->rtcp_peer = *peer; (void)rtcp_enable(rs->rtcp, peer != NULL, cname); } /** * Enable RTCP-multiplexing on RTP-port * * @param rs RTP Socket * @param enabled True to enable, false to disable */ void rtcp_enable_mux(struct rtp_sock *rs, bool enabled) { if (!rs) return; rs->rtcp_mux = enabled; } /** * Send RTCP packet(s) to the Peer * * @param rs RTP Socket * @param mb Buffer containing the RTCP Packet(s) * * @return 0 for success, otherwise errorcode */ int rtcp_send(struct rtp_sock *rs, struct mbuf *mb) { if (!rs || !rs->sock_rtcp || !sa_isset(&rs->rtcp_peer, SA_ALL)) return EINVAL; return udp_send(rs->rtcp_mux ? rs->sock_rtp : rs->sock_rtcp, &rs->rtcp_peer, mb); } /** * RTP Debug handler, use with fmt %H * * @param pf Print function * @param rs RTP Socket * * @return 0 if success, otherwise errorcode */ int rtp_debug(struct re_printf *pf, const struct rtp_sock *rs) { int err; if (!rs || !pf) return EINVAL; err = re_hprintf(pf, "RTP debug:\n"); err |= re_hprintf(pf, " Encode: seq=%u ssrc=0x%lx\n", rs->enc.seq, rs->enc.ssrc); if (rs->rtcp) err |= rtcp_debug(pf, rs); return err; } re-1.1.0/src/rtp/sdes.c000066400000000000000000000053771373627245400146460ustar00rootroot00000000000000/** * @file sdes.c RTCP Source Description * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "rtcp.h" #define DEBUG_MODULE "rtcp_sdes" #define DEBUG_LEVEL 5 #include enum { RTCP_SDES_MIN_SIZE = 1, }; /** * Encode one SDES chunk into mbuffer * * @param mb Buffer to encode into * @param src First SSRC/CSRC * @param itemc Number of SDES items to encode * * @return 0 if success, otherwise errorcode */ int rtcp_sdes_encode(struct mbuf *mb, uint32_t src, uint32_t itemc, ...) { va_list ap; size_t start; int err = 0; if (!mb || !itemc) return EINVAL; va_start(ap, itemc); start = mb->pos; err = mbuf_write_u32(mb, htonl(src)); /* add all SDES items */ while (itemc-- && !err) { const uint8_t type = va_arg(ap, int); const char *v = va_arg(ap, const char *); size_t len; if (!v) continue; len = strlen(v); /* note: max 255 chars */ if (len > 255) { err = EINVAL; goto out; } err = mbuf_write_u8(mb, type); err |= mbuf_write_u8(mb, len & 0xff); err |= mbuf_write_mem(mb, (uint8_t *)v, len); } /* END padding */ err |= mbuf_write_u8(mb, RTCP_SDES_END); while ((mb->pos - start) & 0x3) err |= mbuf_write_u8(mb, RTCP_SDES_END); out: va_end(ap); return err; } /** * Decode SDES items from a buffer * * @param mb Buffer to decode from * @param sdes RTCP SDES to decode into * * @return 0 if success, otherwise errorcode */ int rtcp_sdes_decode(struct mbuf *mb, struct rtcp_sdes *sdes) { size_t start; if (!sdes) return EINVAL; if (mbuf_get_left(mb) < RTCP_SRC_SIZE) return EBADMSG; start = mb->pos; sdes->src = ntohl(mbuf_read_u32(mb)); /* Decode all SDES items */ while (mbuf_get_left(mb) >= RTCP_SDES_MIN_SIZE) { uint8_t type; struct rtcp_sdes_item *item; type = mbuf_read_u8(mb); if (type == RTCP_SDES_END) break; if (mbuf_get_left(mb) < 1) return EBADMSG; if (!sdes->itemv) { sdes->itemv = mem_alloc(sizeof(*sdes->itemv), NULL); if (!sdes->itemv) return ENOMEM; } else { const size_t sz = (sdes->n + 1) * sizeof(*sdes->itemv); struct rtcp_sdes_item *itemv; itemv = mem_realloc(sdes->itemv, sz); if (!itemv) return ENOMEM; sdes->itemv = itemv; } item = &sdes->itemv[sdes->n]; item->type = (enum rtcp_sdes_type)type; item->length = mbuf_read_u8(mb); if (mbuf_get_left(mb) < item->length) return EBADMSG; item->data = mem_alloc(item->length, NULL); if (!item->data) return ENOMEM; (void)mbuf_read_mem(mb, (uint8_t *)item->data, item->length); sdes->n++; } /* slurp padding */ while ((mb->pos - start) & 0x3 && mbuf_get_left(mb)) ++mb->pos; return 0; } re-1.1.0/src/rtp/sess.c000066400000000000000000000322761373627245400146630ustar00rootroot00000000000000/** * @file rtp/sess.c Real-time Transport Control Protocol Session * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_SYS_TIME_H #include #endif #include #ifdef WIN32 #include #endif #include #include #include #include #include #include #include #include #include #include #include #include "rtcp.h" #define DEBUG_MODULE "rtcp_sess" #define DEBUG_LEVEL 5 #include /** RTP protocol values */ enum { RTCP_INTERVAL = 5000, /**< Interval in [ms] between sending reports */ MAX_MEMBERS = 8, }; /** RTP Transmit stats */ struct txstat { uint32_t psent; /**< Total number of RTP packets sent */ uint32_t osent; /**< Total number of RTP octets sent */ uint64_t jfs_ref; /**< Timer ticks at RTP timestamp reference */ uint32_t ts_ref; /**< RTP timestamp reference (transmit) */ bool ts_synced; /**< RTP timestamp synchronization flag */ }; /** RTCP Session */ struct rtcp_sess { struct rtp_sock *rs; /**< RTP Socket */ struct hash *members; /**< Member table */ struct tmr tmr; /**< Event sender timer */ char *cname; /**< Canonical Name */ uint32_t memberc; /**< Number of members */ uint32_t senderc; /**< Number of senders */ uint32_t srate_tx; /**< Transmit sampling rate */ uint32_t srate_rx; /**< Receive sampling rate */ /* stats */ struct lock *lock; /**< Lock for txstat */ struct txstat txstat; /**< Local transmit statistics */ }; /* Prototypes */ static void schedule(struct rtcp_sess *sess); static int send_bye_packet(struct rtcp_sess *sess); static void sess_destructor(void *data) { struct rtcp_sess *sess = data; if (sess->cname) (void)send_bye_packet(sess); tmr_cancel(&sess->tmr); mem_deref(sess->cname); hash_flush(sess->members); mem_deref(sess->members); mem_deref(sess->lock); } static struct rtp_member *get_member(struct rtcp_sess *sess, uint32_t src) { struct rtp_member *mbr; mbr = member_find(sess->members, src); if (mbr) return mbr; if (sess->memberc >= MAX_MEMBERS) return NULL; mbr = member_add(sess->members, src); if (!mbr) return NULL; ++sess->memberc; return mbr; } /** Calculate Round-Trip Time in [microseconds] */ static void calc_rtt(uint32_t *rtt, uint32_t lsr, uint32_t dlsr) { struct ntp_time ntp_time; uint64_t a_us, lsr_us, dlsr_us; int err; err = ntp_time_get(&ntp_time); if (err) return; a_us = ntp_compact2us(ntp_compact(&ntp_time)); lsr_us = ntp_compact2us(lsr); dlsr_us = 1000000ULL * dlsr / 65536; /* RTT delay is (A - LSR - DLSR) */ *rtt = MAX((int)(a_us - lsr_us - dlsr_us), 0); } /** Decode Reception Report block */ static void handle_rr_block(struct rtcp_sess *sess, struct rtp_member *mbr, const struct rtcp_rr *rr) { /* Lost */ mbr->cum_lost = rr->lost; /* Interarrival jitter */ if (sess->srate_tx) mbr->jit = 1000000 * rr->jitter / sess->srate_tx; /* round-trip propagation delay as (A - LSR - DLSR) */ if (rr->lsr && rr->dlsr) calc_rtt(&mbr->rtt, rr->lsr, rr->dlsr); } /** Handle incoming RR (Receiver Report) packet */ static void handle_incoming_rr(struct rtcp_sess *sess, const struct rtcp_msg *msg) { struct rtp_member *mbr; uint32_t i; mbr = get_member(sess, msg->r.rr.ssrc); if (!mbr) return; for (i=0; ihdr.count; i++) handle_rr_block(sess, mbr, &msg->r.rr.rrv[i]); } /** Handle incoming SR (Sender Report) packet */ static void handle_incoming_sr(struct rtcp_sess *sess, const struct rtcp_msg *msg) { struct rtp_member *mbr; uint32_t i; mbr = get_member(sess, msg->r.sr.ssrc); if (!mbr) { DEBUG_WARNING("0x%08x: could not add member\n", msg->r.sr.ssrc); return; } if (mbr->s) { /* Save time when SR was received */ mbr->s->sr_recv = tmr_jiffies(); /* Save NTP timestamp from SR */ mbr->s->last_sr.hi = msg->r.sr.ntp_sec; mbr->s->last_sr.lo = msg->r.sr.ntp_frac; mbr->s->rtp_ts = msg->r.sr.rtp_ts; mbr->s->psent = msg->r.sr.psent; mbr->s->osent = msg->r.sr.osent; } for (i=0; ihdr.count; i++) handle_rr_block(sess, mbr, &msg->r.sr.rrv[i]); } static void handle_incoming_bye(struct rtcp_sess *sess, const struct rtcp_msg *msg) { uint32_t i; for (i=0; ihdr.count; i++) { struct rtp_member *mbr; mbr = member_find(sess->members, msg->r.bye.srcv[i]); if (mbr) { if (mbr->s) --sess->senderc; --sess->memberc; mem_deref(mbr); } } } void rtcp_handler(struct rtcp_sess *sess, struct rtcp_msg *msg) { if (!sess || !msg) return; switch (msg->hdr.pt) { case RTCP_SR: handle_incoming_sr(sess, msg); break; case RTCP_RR: handle_incoming_rr(sess, msg); break; case RTCP_BYE: handle_incoming_bye(sess, msg); break; default: break; } } int rtcp_sess_alloc(struct rtcp_sess **sessp, struct rtp_sock *rs) { struct rtcp_sess *sess; int err; if (!sessp) return EINVAL; sess = mem_zalloc(sizeof(*sess), sess_destructor); if (!sess) return ENOMEM; sess->rs = rs; tmr_init(&sess->tmr); err = lock_alloc(&sess->lock); if (err) goto out; err = hash_alloc(&sess->members, MAX_MEMBERS); if (err) goto out; out: if (err) mem_deref(sess); else *sessp = sess; return err; } /** * Set the Sampling-rate on an RTCP Session * * @param rs RTP Socket * @param srate_tx Transmit samplerate * @param srate_rx Receive samplerate */ void rtcp_set_srate(struct rtp_sock *rs, uint32_t srate_tx, uint32_t srate_rx) { struct rtcp_sess *sess = rtp_rtcp_sess(rs); if (!sess) return; sess->srate_tx = srate_tx; sess->srate_rx = srate_rx; } /** * Set the transmit Sampling-rate on an RTCP Session * * @param rs RTP Socket * @param srate_tx Transmit samplerate */ void rtcp_set_srate_tx(struct rtp_sock *rs, uint32_t srate_tx) { struct rtcp_sess *sess = rtp_rtcp_sess(rs); if (!sess) return; sess->srate_tx = srate_tx; } /** * Set the receive Sampling-rate on an RTCP Session * * @param rs RTP Socket * @param srate_rx Receive samplerate */ void rtcp_set_srate_rx(struct rtp_sock *rs, uint32_t srate_rx) { struct rtcp_sess *sess = rtp_rtcp_sess(rs); if (!sess) return; sess->srate_rx = srate_rx; } int rtcp_enable(struct rtcp_sess *sess, bool enabled, const char *cname) { int err; if (!sess) return EINVAL; sess->cname = mem_deref(sess->cname); err = str_dup(&sess->cname, cname); if (err) return err; if (enabled) schedule(sess); else tmr_cancel(&sess->tmr); return 0; } /** Calculate LSR (middle 32 bits out of 64 in the NTP timestamp) */ static uint32_t calc_lsr(const struct ntp_time *last_sr) { return last_sr->hi ? ntp_compact(last_sr) : 0; } static uint32_t calc_dlsr(uint64_t sr_recv) { if (sr_recv) { const uint64_t diff = tmr_jiffies() - sr_recv; return (uint32_t)((65536 * diff) / 1000); } else { return 0; } } static bool sender_apply_handler(struct le *le, void *arg) { struct rtp_member *mbr = le->data; struct rtp_source *s = mbr->s; struct mbuf *mb = arg; struct rtcp_rr rr; if (!s) return false; /* Initialise the members */ rr.ssrc = mbr->src; rr.fraction = source_calc_fraction_lost(s); rr.lost = source_calc_lost(s); rr.last_seq = s->cycles | s->max_seq; rr.jitter = s->jitter >> 4; rr.lsr = calc_lsr(&s->last_sr); rr.dlsr = calc_dlsr(s->sr_recv); return 0 != rtcp_rr_encode(mb, &rr); } static int encode_handler(struct mbuf *mb, void *arg) { struct hash *members = arg; /* copy all report blocks */ if (hash_apply(members, sender_apply_handler, mb)) return ENOMEM; return 0; } /** Create a Sender Report */ static int mk_sr(struct rtcp_sess *sess, struct mbuf *mb) { struct ntp_time ntp = {0, 0}; struct txstat txstat; uint32_t dur, rtp_ts = 0; int err; err = ntp_time_get(&ntp); if (err) return err; lock_write_get(sess->lock); txstat = sess->txstat; sess->txstat.ts_synced = false; lock_rel(sess->lock); if (txstat.jfs_ref) { dur = (uint32_t)(tmr_jiffies() - txstat.jfs_ref); rtp_ts = txstat.ts_ref + dur * sess->srate_tx / 1000; } err = rtcp_encode(mb, RTCP_SR, sess->senderc, rtp_sess_ssrc(sess->rs), ntp.hi, ntp.lo, rtp_ts, txstat.psent, txstat.osent, encode_handler, sess->members); if (err) return err; return err; } static int sdes_encode_handler(struct mbuf *mb, void *arg) { struct rtcp_sess *sess = arg; return rtcp_sdes_encode(mb, rtp_sess_ssrc(sess->rs), 1, RTCP_SDES_CNAME, sess->cname); } static int mk_sdes(struct rtcp_sess *sess, struct mbuf *mb) { return rtcp_encode(mb, RTCP_SDES, 1, sdes_encode_handler, sess); } static int send_rtcp_report(struct rtcp_sess *sess) { struct mbuf *mb; int err; mb = mbuf_alloc(512); if (!mb) return ENOMEM; mb->pos = RTCP_HEADROOM; err = mk_sr(sess, mb); err |= mk_sdes(sess, mb); if (err) goto out; mb->pos = RTCP_HEADROOM; err = rtcp_send(sess->rs, mb); out: mem_deref(mb); return err; } static int send_bye_packet(struct rtcp_sess *sess) { const uint32_t ssrc = rtp_sess_ssrc(sess->rs); struct mbuf *mb; int err; mb = mbuf_alloc(512); if (!mb) return ENOMEM; mb->pos = RTCP_HEADROOM; err = rtcp_encode(mb, RTCP_BYE, 1, &ssrc, "Adjo"); err |= mk_sdes(sess, mb); if (err) goto out; mb->pos = RTCP_HEADROOM; err = rtcp_send(sess->rs, mb); out: mem_deref(mb); return err; } static void timeout(void *arg) { struct rtcp_sess *sess = arg; int err; err = send_rtcp_report(sess); if (err) { DEBUG_WARNING("Send RTCP report failed: %m\n", err); } schedule(sess); } static void schedule(struct rtcp_sess *sess) { tmr_start(&sess->tmr, RTCP_INTERVAL, timeout, sess); } void rtcp_sess_tx_rtp(struct rtcp_sess *sess, uint32_t ts, size_t payload_size) { if (!sess) return; lock_write_get(sess->lock); sess->txstat.osent += (uint32_t)payload_size; sess->txstat.psent += 1; if (!sess->txstat.ts_synced) { sess->txstat.jfs_ref = tmr_jiffies(); sess->txstat.ts_ref = ts; sess->txstat.ts_synced = true; } lock_rel(sess->lock); } void rtcp_sess_rx_rtp(struct rtcp_sess *sess, uint16_t seq, uint32_t ts, uint32_t ssrc, size_t payload_size, const struct sa *peer) { struct rtp_member *mbr; if (!sess) return; mbr = get_member(sess, ssrc); if (!mbr) { DEBUG_NOTICE("could not add member: 0x%08x\n", ssrc); return; } if (!mbr->s) { mbr->s = mem_zalloc(sizeof(*mbr->s), NULL); if (!mbr->s) { DEBUG_NOTICE("could not add sender: 0x%08x\n", ssrc); return; } /* first packet - init sequence number */ source_init_seq(mbr->s, seq); /* probation not used */ sa_cpy(&mbr->s->rtp_peer, peer); ++sess->senderc; } if (!source_update_seq(mbr->s, seq)) { DEBUG_WARNING("rtp_update_seq() returned 0\n"); } if (sess->srate_rx) { uint64_t ts_arrive; /* Convert from wall-clock time to timestamp units */ ts_arrive = tmr_jiffies() * sess->srate_rx / 1000; source_calc_jitter(mbr->s, ts, (uint32_t)ts_arrive); } mbr->s->rtp_rx_bytes += payload_size; } /** * Get the RTCP Statistics for a source * * @param rs RTP Socket * @param ssrc Synchronization source * @param stats RTCP Statistics, set on return * * @return 0 if success, otherwise errorcode */ int rtcp_stats(struct rtp_sock *rs, uint32_t ssrc, struct rtcp_stats *stats) { const struct rtcp_sess *sess = rtp_rtcp_sess(rs); struct rtp_member *mbr; if (!sess || !stats) return EINVAL; mbr = member_find(sess->members, ssrc); if (!mbr) return ENOENT; lock_read_get(sess->lock); stats->tx.sent = sess->txstat.psent; lock_rel(sess->lock); stats->tx.lost = mbr->cum_lost; stats->tx.jit = mbr->jit; stats->rtt = mbr->rtt; if (!mbr->s) { memset(&stats->rx, 0, sizeof(stats->rx)); return 0; } stats->rx.sent = mbr->s->received; stats->rx.lost = source_calc_lost(mbr->s); stats->rx.jit = sess->srate_rx ? 1000000 * (mbr->s->jitter>>4) / sess->srate_rx : 0; return 0; } static bool debug_handler(struct le *le, void *arg) { const struct rtp_member *mbr = le->data; struct re_printf *pf = arg; int err; err = re_hprintf(pf, " member 0x%08x: lost=%d Jitter=%.1fms" " RTT=%.1fms\n", mbr->src, mbr->cum_lost, (double)mbr->jit/1000, (double)mbr->rtt/1000); if (mbr->s) { err |= re_hprintf(pf, " IP=%J psent=%u rcvd=%u\n", &mbr->s->rtp_peer, mbr->s->psent, mbr->s->received); } return err != 0; } /** * RTCP Debug handler, use with fmt %H * * @param pf Print function * @param rs RTP Socket * * @return 0 if success, otherwise errorcode */ int rtcp_debug(struct re_printf *pf, const struct rtp_sock *rs) { const struct rtcp_sess *sess = rtp_rtcp_sess(rs); int err = 0; if (!sess) return 0; err |= re_hprintf(pf, "----- RTCP Session: -----\n"); err |= re_hprintf(pf, " cname=%s SSRC=0x%08x/%u rx=%uHz\n", sess->cname, rtp_sess_ssrc(sess->rs), rtp_sess_ssrc(sess->rs), sess->srate_rx); hash_apply(sess->members, debug_handler, pf); lock_read_get(sess->lock); err |= re_hprintf(pf, " TX: packets=%u, octets=%u\n", sess->txstat.psent, sess->txstat.osent); lock_rel(sess->lock); return err; } re-1.1.0/src/rtp/source.c000066400000000000000000000065571373627245400152110ustar00rootroot00000000000000/** * @file source.c Real-time Transport Control Protocol source * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "rtcp.h" enum { RTP_SEQ_MOD = 1<<16, }; void source_init_seq(struct rtp_source *s, uint16_t seq) { if (!s) return; s->base_seq = seq; s->max_seq = seq; s->bad_seq = RTP_SEQ_MOD + 1; /* so seq == bad_seq is false */ s->cycles = 0; s->received = 0; s->received_prior = 0; s->expected_prior = 0; /* other initialization */ } /* * See RFC 3550 - A.1 RTP Data Header Validity Checks */ int source_update_seq(struct rtp_source *s, uint16_t seq) { uint16_t udelta = seq - s->max_seq; const int MAX_DROPOUT = 3000; const int MAX_MISORDER = 100; const int MIN_SEQUENTIAL = 2; /* * Source is not valid until MIN_SEQUENTIAL packets with * sequential sequence numbers have been received. */ if (s->probation) { /* packet is in sequence */ if (seq == s->max_seq + 1) { s->probation--; s->max_seq = seq; if (s->probation == 0) { source_init_seq(s, seq); s->received++; return 1; } } else { s->probation = MIN_SEQUENTIAL - 1; s->max_seq = seq; } return 0; } else if (udelta < MAX_DROPOUT) { /* in order, with permissible gap */ if (seq < s->max_seq) { /* * Sequence number wrapped - count another 64K cycle. */ s->cycles += RTP_SEQ_MOD; } s->max_seq = seq; } else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER) { /* the sequence number made a very large jump */ if (seq == s->bad_seq) { /* * Two sequential packets -- assume that the other side * restarted without telling us so just re-sync * (i.e., pretend this was the first packet). */ source_init_seq(s, seq); } else { s->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1); return 0; } } else { /* duplicate or reordered packet */ } s->received++; return 1; } /* RFC 3550 A.8 * * The inputs are: * * rtp_ts: the timestamp from the incoming RTP packet * arrival: the current time in the same units. */ void source_calc_jitter(struct rtp_source *s, uint32_t rtp_ts, uint32_t arrival) { const int transit = arrival - rtp_ts; int d = transit - s->transit; if (!s->transit) { s->transit = transit; return; } s->transit = transit; if (d < 0) d = -d; s->jitter += d - ((s->jitter + 8) >> 4); } /* A.3 */ int source_calc_lost(const struct rtp_source *s) { int extended_max = s->cycles + s->max_seq; int expected = extended_max - s->base_seq + 1; int lost; lost = expected - s->received; /* Clamp at 24 bits */ if (lost > 0x7fffff) lost = 0x7fffff; else if (lost < -0x7fffff) lost = -0x7fffff; return lost; } /* A.3 */ uint8_t source_calc_fraction_lost(struct rtp_source *s) { int extended_max = s->cycles + s->max_seq; int expected = extended_max - s->base_seq + 1; int expected_interval = expected - s->expected_prior; int received_interval; int lost_interval; uint8_t fraction; s->expected_prior = expected; received_interval = s->received - s->received_prior; s->received_prior = s->received; lost_interval = expected_interval - received_interval; if (expected_interval == 0 || lost_interval <= 0) fraction = 0; else fraction = (lost_interval << 8) / expected_interval; return fraction; } re-1.1.0/src/sa/000077500000000000000000000000001373627245400133265ustar00rootroot00000000000000re-1.1.0/src/sa/mod.mk000066400000000000000000000001741373627245400144400ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sa/ntop.c SRCS += sa/printaddr.c SRCS += sa/pton.c SRCS += sa/sa.c re-1.1.0/src/sa/ntop.c000066400000000000000000000111771373627245400144610ustar00rootroot00000000000000/** * @file ntop.c Network address structure functions * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #ifdef HAVE_INET_NTOP #ifdef WIN32 #ifdef _MSC_VER #pragma warning (disable: 4090) #endif #include #else #define __USE_BSD 1 /**< Use BSD code */ #include #include #include #define __USE_POSIX 1 /**< Use POSIX code */ #include #endif /* WIN32 */ #endif #include #include #include #include #include #include "sa.h" #define DEBUG_MODULE "net_ntop" #define DEBUG_LEVEL 5 #include #ifndef HAVE_INET_NTOP #define NS_IN6ADDRSZ 16 /**< IPv6 T_AAAA */ #define NS_INT16SZ 2 /**< #/bytes of data in a u_int16_t */ static const char* inet_ntop4(const u_char *src, char *dst, size_t size) { if (re_snprintf(dst, size, "%u.%u.%u.%u", src[0], src[1], src[2], src[3]) < 0) { errno = ENOSPC; dst[size-1] = 0; return NULL; } return dst; } #ifdef HAVE_INET6 /* const char * * inet_ntop6(src, dst, size) * convert IPv6 binary address into presentation (printable) format * author: * Paul Vixie, 1996. */ static const char * inet_ntop6(const u_char *src, char *dst, size_t size) { /* * Note that int32_t and int16_t need only be "at least" large enough * to contain a value of the specified size. On some systems, like * Crays, there is no such thing as an integer variable with 16 bits. * Keep this in mind if you think this function should have been coded * to use pointer overlays. All the world's not a VAX. */ char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp; struct { int base, len; } best, cur; u_int words[NS_IN6ADDRSZ / NS_INT16SZ]; int i; /* * Preprocess: * Copy the input (bytewise) array into a wordwise array. * Find the longest run of 0x00's in src[] for :: shorthanding. */ memset(words, '\0', sizeof words); for (i = 0; i < NS_IN6ADDRSZ; i++) words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3)); best.base = -1; best.len = 0; cur.base = -1; cur.len = 0; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { if (words[i] == 0) { if (cur.base == -1) cur.base = i, cur.len = 1; else cur.len++; } else { if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; cur.base = -1; } } } if (cur.base != -1) { if (best.base == -1 || cur.len > best.len) best = cur; } if (best.base != -1 && best.len < 2) best.base = -1; /* * Format the result. */ tp = tmp; for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) { /* Are we inside the best run of 0x00's? */ if (best.base != -1 && i >= best.base && i < (best.base + best.len)) { if (i == best.base) *tp++ = ':'; continue; } /* Are we following an initial run of 0x00s or any real hex?*/ if (i != 0) *tp++ = ':'; /* Is this address an encapsulated IPv4? */ if (i == 6 && best.base == 0 && (best.len == 6 || (best.len == 5 && words[5] == 0xffff))) { if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp))) return NULL; tp += strlen(tp); break; } tp += sprintf(tp, "%x", words[i]); } /* Was it a trailing run of 0x00's? */ if (best.base != -1 && (best.base + best.len) == (NS_IN6ADDRSZ / NS_INT16SZ)) *tp++ = ':'; *tp++ = '\0'; /* * Check for overflow, copy, and we're done. */ if ((size_t)(tp - tmp) > size) { errno = ENOSPC; return NULL; } strcpy(dst, tmp); return dst; } #endif /* * Implementation of inet_ntop() */ const char* inet_ntop(int af, const void *src, char *dst, size_t size); const char* inet_ntop(int af, const void *src, char *dst, size_t size) { switch (af) { case AF_INET: return inet_ntop4(src, dst, size); #ifdef HAVE_INET6 case AF_INET6: return inet_ntop6(src, dst, size); #endif default: DEBUG_WARNING("inet_ntop: unknown address family %d\n", af); return NULL; } } #endif /* * POSIX 1003.1-2001 marks gethostbyaddr() and gethostbyname() obsolescent. * See getaddrinfo(3), getnameinfo(3), gai_strerror(3). */ /** * Convert network address structure to a character string * * @param sa Socket address * @param buf Buffer to return IP address * @param size Size of buffer * * @return 0 if success, otherwise errorcode */ int net_inet_ntop(const struct sa *sa, char *buf, int size) { if (!sa || !buf || !size) return EINVAL; switch (sa->u.sa.sa_family) { case AF_INET: inet_ntop(AF_INET, &sa->u.in.sin_addr, buf, size); break; #ifdef HAVE_INET6 case AF_INET6: inet_ntop(AF_INET6, &sa->u.in6.sin6_addr, buf, size); break; #endif default: return EAFNOSUPPORT; } return 0; } re-1.1.0/src/sa/printaddr.c000066400000000000000000000015611373627245400154640ustar00rootroot00000000000000/** * @file sa/printaddr.c Socket Address printing * * Copyright (C) 2010 Creytiv.com */ #ifdef HAVE_GETIFADDRS #include #include #define __USE_MISC 1 /**< Use MISC code */ #include #endif #include #include #include /** * Print a Socket Address including IPv6 scope identifier * * @param pf Print function * @param sa Socket Address * * @return 0 if success, otherwise errorcode */ int sa_print_addr(struct re_printf *pf, const struct sa *sa) { int err; if (!sa) return 0; err = re_hprintf(pf, "%j", sa); #if defined (HAVE_GETIFADDRS) && defined (HAVE_INET6) if (sa_af(sa) == AF_INET6 && sa_is_linklocal(sa)) { char ifname[IF_NAMESIZE]; if (!if_indextoname(sa->u.in6.sin6_scope_id, ifname)) return errno; err |= re_hprintf(pf, "%%%s", ifname); } #endif return err; } re-1.1.0/src/sa/pton.c000066400000000000000000000116321373627245400144550ustar00rootroot00000000000000/** * @file pton.c Network address structure functions * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #ifdef HAVE_INET_PTON #ifdef WIN32 #include #else #include #include #include #define __USE_POSIX 1 /**< Use POSIX code */ #include #endif /* WIN32 */ #endif #include #include #include #include #include #include "sa.h" #define DEBUG_MODULE "net_pton" #define DEBUG_LEVEL 5 #include #ifndef HAVE_INET_PTON #define NS_INADDRSZ 4 /**< IPv4 T_A */ #define NS_IN6ADDRSZ 16 /**< IPv6 T_AAAA */ #define NS_INT16SZ 2 /**< #/bytes of data in a u_int16_t */ /* int * inet_pton4(src, dst) * like inet_aton() but without all the hexadecimal and shorthand. * return: * 1 if `src' is a valid dotted quad, else 0. * notice: * does not touch `dst' unless it's returning 1. * author: * Paul Vixie, 1996. */ static int inet_pton4(const char *src, u_char *dst) { static const char digits[] = "0123456789"; int saw_digit, octets, ch; u_char tmp[NS_INADDRSZ], *tp; saw_digit = 0; octets = 0; *(tp = tmp) = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr(digits, ch)) != NULL) { u_int newVal = (u_int) (*tp * 10 + (pch - digits)); if (newVal > 255) return 0; *tp = newVal; if (! saw_digit) { if (++octets > 4) return 0; saw_digit = 1; } } else if (ch == '.' && saw_digit) { if (octets == 4) return 0; *++tp = 0; saw_digit = 0; } else return 0; } if (octets < 4) return 0; memcpy(dst, tmp, NS_INADDRSZ); return 1; } #ifdef HAVE_INET6 /* int * inet_pton6(src, dst) * convert presentation level address to network order binary form. * return: * 1 if `src' is a valid [RFC1884 2.2] address, else 0. * notice: * (1) does not touch `dst' unless it's returning 1. * (2) :: in a full address is silently ignored. * credit: * inspired by Mark Andrews. * author: * Paul Vixie, 1996. */ static int inet_pton6(const char *src, u_char *dst) { static const char xdigits_l[] = "0123456789abcdef", xdigits_u[] = "0123456789ABCDEF"; u_char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; const char *xdigits, *curtok; int ch, saw_xdigit; u_int val; memset((tp = tmp), '\0', NS_IN6ADDRSZ); endp = tp + NS_IN6ADDRSZ; colonp = NULL; /* Leading :: requires some special handling. */ if (*src == ':') if (*++src != ':') return 0; curtok = src; saw_xdigit = 0; val = 0; while ((ch = *src++) != '\0') { const char *pch; if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL) pch = strchr((xdigits = xdigits_u), ch); if (pch != NULL) { val <<= 4; val |= (u_int)(pch - xdigits); if (val > 0xffff) return 0; saw_xdigit = 1; continue; } if (ch == ':') { curtok = src; if (!saw_xdigit) { if (colonp) return 0; colonp = tp; continue; } if (tp + NS_INT16SZ > endp) return 0; *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; saw_xdigit = 0; val = 0; continue; } if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && inet_pton4(curtok, tp) > 0) { tp += NS_INADDRSZ; saw_xdigit = 0; break; /* '\0' was seen by inet_pton4(). */ } return 0; } if (saw_xdigit) { if (tp + NS_INT16SZ > endp) return 0; *tp++ = (u_char) (val >> 8) & 0xff; *tp++ = (u_char) val & 0xff; } if (colonp != NULL) { /* * Since some memmove()'s erroneously fail to handle * overlapping regions, we'll do the shift by hand. */ const int n = (int)(tp - colonp); int i; for (i = 1; i <= n; i++) { endp[- i] = colonp[n - i]; colonp[n - i] = 0; } tp = endp; } if (tp != endp) return 0; memcpy(dst, tmp, NS_IN6ADDRSZ); return 1; } #endif /** * Implementation of inet_pton() */ static int inet_pton(int af, const char *src, void *dst) { if (!src || !dst) return 0; switch (af) { case AF_INET: return inet_pton4(src, (u_char*) dst); #ifdef HAVE_INET6 case AF_INET6: return inet_pton6(src, (u_char*) dst); #endif default: DEBUG_INFO("inet_pton: unknown address family %d\n", af); errno = EAFNOSUPPORT; return -1; } } #endif /** * Convert character string to a network address structure * * @param addr IP address string * @param sa Returned socket address * * @return 0 if success, otherwise errorcode */ int net_inet_pton(const char *addr, struct sa *sa) { if (!addr) return EINVAL; if (inet_pton(AF_INET, addr, &sa->u.in.sin_addr) > 0) { sa->u.in.sin_family = AF_INET; } #ifdef HAVE_INET6 else if (inet_pton(AF_INET6, addr, &sa->u.in6.sin6_addr) > 0) { if (IN6_IS_ADDR_V4MAPPED(&sa->u.in6.sin6_addr)) { const uint8_t *a = &sa->u.in6.sin6_addr.s6_addr[12]; sa->u.in.sin_family = AF_INET; memcpy(&sa->u.in.sin_addr.s_addr, a, 4); } else { sa->u.in6.sin6_family = AF_INET6; } } #endif else { return EINVAL; } return 0; } re-1.1.0/src/sa/sa.c000066400000000000000000000234751373627245400141100ustar00rootroot00000000000000/** * @file sa.c Socket Address * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #include #include #include #include "sa.h" #define DEBUG_MODULE "sa" #define DEBUG_LEVEL 5 #include /** * Initialize a Socket Address * * @param sa Socket Address * @param af Address Family */ void sa_init(struct sa *sa, int af) { if (!sa) return; memset(sa, 0, sizeof(*sa)); sa->u.sa.sa_family = af; sa->len = sizeof(sa->u); } /** * Set a Socket Address from a PL string * * @param sa Socket Address * @param addr IP-address * @param port Port number * * @return 0 if success, otherwise errorcode */ int sa_set(struct sa *sa, const struct pl *addr, uint16_t port) { char buf[64]; (void)pl_strcpy(addr, buf, sizeof(buf)); return sa_set_str(sa, buf, port); } /** * Set a Socket Address from a string * * @param sa Socket Address * @param addr IP-address * @param port Port number * * @return 0 if success, otherwise errorcode */ int sa_set_str(struct sa *sa, const char *addr, uint16_t port) { int err; if (!sa || !addr) return EINVAL; err = net_inet_pton(addr, sa); if (err) return err; switch (sa->u.sa.sa_family) { case AF_INET: sa->u.in.sin_port = htons(port); sa->len = sizeof(struct sockaddr_in); break; #ifdef HAVE_INET6 case AF_INET6: sa->u.in6.sin6_port = htons(port); sa->len = sizeof(struct sockaddr_in6); break; #endif default: return EAFNOSUPPORT; } return 0; } /** * Set a Socket Address from an IPv4 address * * @param sa Socket Address * @param addr IPv4 address in host order * @param port Port number * * @return 0 if success, otherwise errorcode */ void sa_set_in(struct sa *sa, uint32_t addr, uint16_t port) { if (!sa) return; sa->u.in.sin_family = AF_INET; sa->u.in.sin_addr.s_addr = htonl(addr); sa->u.in.sin_port = htons(port); sa->len = sizeof(struct sockaddr_in); } /** * Set a Socket Address from an IPv6 address * * @param sa Socket Address * @param addr IPv6 address * @param port Port number * * @return 0 if success, otherwise errorcode */ void sa_set_in6(struct sa *sa, const uint8_t *addr, uint16_t port) { if (!sa) return; #ifdef HAVE_INET6 sa->u.in6.sin6_family = AF_INET6; memcpy(&sa->u.in6.sin6_addr, addr, 16); sa->u.in6.sin6_port = htons(port); sa->len = sizeof(struct sockaddr_in6); #else (void)addr; (void)port; #endif } /** * Set a Socket Address from a sockaddr * * @param sa Socket Address * @param s Sockaddr * * @return 0 if success, otherwise errorcode */ int sa_set_sa(struct sa *sa, const struct sockaddr *s) { if (!sa || !s) return EINVAL; switch (s->sa_family) { case AF_INET: memcpy(&sa->u.in, s, sizeof(struct sockaddr_in)); sa->len = sizeof(struct sockaddr_in); break; #ifdef HAVE_INET6 case AF_INET6: memcpy(&sa->u.in6, s, sizeof(struct sockaddr_in6)); sa->len = sizeof(struct sockaddr_in6); break; #endif default: return EAFNOSUPPORT; } sa->u.sa.sa_family = s->sa_family; return 0; } /** * Set the port number on a Socket Address * * @param sa Socket Address * @param port Port number */ void sa_set_port(struct sa *sa, uint16_t port) { if (!sa) return; switch (sa->u.sa.sa_family) { case AF_INET: sa->u.in.sin_port = htons(port); break; #ifdef HAVE_INET6 case AF_INET6: sa->u.in6.sin6_port = htons(port); break; #endif default: DEBUG_WARNING("sa_set_port: no af %d (port %u)\n", sa->u.sa.sa_family, port); break; } } /** * Set a socket address from a string of type "address:port" * IPv6 addresses must be encapsulated in square brackets. * * @param sa Socket Address * @param str Address and port string * @param len Length of string * * @return 0 if success, otherwise errorcode * * Example strings: * *
 *   1.2.3.4:1234
 *   [::1]:1234
 *   [::]:5060
 * 
*/ int sa_decode(struct sa *sa, const char *str, size_t len) { struct pl addr, port, pl; const char *c; if (!sa || !str || !len) return EINVAL; pl.p = str; pl.l = len; if ('[' == str[0] && (c = pl_strchr(&pl, ']'))) { addr.p = str + 1; addr.l = c - str - 1; ++c; } else if (NULL != (c = pl_strchr(&pl, ':'))) { addr.p = str; addr.l = c - str; } else { return EINVAL; } if (len < (size_t)(c - str + 2)) return EINVAL; if (':' != *c) return EINVAL; port.p = ++c; port.l = len + str - c; return sa_set(sa, &addr, pl_u32(&port)); } /** * Get the Address Family of a Socket Address * * @param sa Socket Address * * @return Address Family */ int sa_af(const struct sa *sa) { return sa ? sa->u.sa.sa_family : AF_UNSPEC; } /** * Get the IPv4-address of a Socket Address * * @param sa Socket Address * * @return IPv4 address in host order */ uint32_t sa_in(const struct sa *sa) { return sa ? ntohl(sa->u.in.sin_addr.s_addr) : 0; } /** * Get the IPv6-address of a Socket Address * * @param sa Socket Address * @param addr On return, contains the IPv6-address */ void sa_in6(const struct sa *sa, uint8_t *addr) { if (!sa || !addr) return; #ifdef HAVE_INET6 memcpy(addr, &sa->u.in6.sin6_addr, 16); #endif } /** * Convert a Socket Address to Presentation format * * @param sa Socket Address * @param buf Buffer to store presentation format * @param size Buffer size * * @return 0 if success, otherwise errorcode */ int sa_ntop(const struct sa *sa, char *buf, int size) { return net_inet_ntop(sa, buf, size); } /** * Get the port number from a Socket Address * * @param sa Socket Address * * @return Port number in host order */ uint16_t sa_port(const struct sa *sa) { if (!sa) return 0; switch (sa->u.sa.sa_family) { case AF_INET: return ntohs(sa->u.in.sin_port); #ifdef HAVE_INET6 case AF_INET6: return ntohs(sa->u.in6.sin6_port); #endif default: return 0; } } /** * Check if a Socket Address is set * * @param sa Socket Address * @param flag Flags specifying which fields to check * * @return true if set, false if not set */ bool sa_isset(const struct sa *sa, int flag) { if (!sa) return false; switch (sa->u.sa.sa_family) { case AF_INET: if (flag & SA_ADDR) if (INADDR_ANY == sa->u.in.sin_addr.s_addr) return false; if (flag & SA_PORT) if (0 == sa->u.in.sin_port) return false; break; #ifdef HAVE_INET6 case AF_INET6: if (flag & SA_ADDR) if (IN6_IS_ADDR_UNSPECIFIED(&sa->u.in6.sin6_addr)) return false; if (flag & SA_PORT) if (0 == sa->u.in6.sin6_port) return false; break; #endif default: return false; } return true; } /** * Calculate the hash value of a Socket Address * * @param sa Socket Address * @param flag Flags specifying which fields to use * * @return Hash value */ uint32_t sa_hash(const struct sa *sa, int flag) { uint32_t v = 0; if (!sa) return 0; switch (sa->u.sa.sa_family) { case AF_INET: if (flag & SA_ADDR) v += ntohl(sa->u.in.sin_addr.s_addr); if (flag & SA_PORT) v += ntohs(sa->u.in.sin_port); break; #ifdef HAVE_INET6 case AF_INET6: if (flag & SA_ADDR) { uint32_t *a = (uint32_t *)&sa->u.in6.sin6_addr; v += a[0] ^ a[1] ^ a[2] ^ a[3]; } if (flag & SA_PORT) v += ntohs(sa->u.in6.sin6_port); break; #endif default: DEBUG_WARNING("sa_hash: unknown af %d\n", sa->u.sa.sa_family); return 0; } return v; } /** * Copy a Socket Address * * @param dst Socket Address to be written * @param src Socket Address to be copied */ void sa_cpy(struct sa *dst, const struct sa *src) { if (!dst || !src) return; memcpy(dst, src, sizeof(*dst)); } /** * Compare two Socket Address objects * * @param l Socket Address number one * @param r Socket Address number two * @param flag Flags specifying which fields to use * * @return true if match, false if no match */ bool sa_cmp(const struct sa *l, const struct sa *r, int flag) { if (!l || !r) return false; if (l == r) return true; if (l->u.sa.sa_family != r->u.sa.sa_family) return false; switch (l->u.sa.sa_family) { case AF_INET: if (flag & SA_ADDR) if (l->u.in.sin_addr.s_addr != r->u.in.sin_addr.s_addr) return false; if (flag & SA_PORT) if (l->u.in.sin_port != r->u.in.sin_port) return false; break; #ifdef HAVE_INET6 case AF_INET6: if (flag & SA_ADDR) if (memcmp(&l->u.in6.sin6_addr, &r->u.in6.sin6_addr, 16)) return false; if (flag & SA_PORT) if (l->u.in6.sin6_port != r->u.in6.sin6_port) return false; break; #endif default: return false; } return true; } /** IPv4 Link-local test */ #define IN_IS_ADDR_LINKLOCAL(a) \ (((a) & htonl(0xffff0000)) == htonl (0xa9fe0000)) /** * Check if socket address is a link-local address * * @param sa Socket address * * @return true if link-local address, otherwise false */ bool sa_is_linklocal(const struct sa *sa) { if (!sa) return false; switch (sa_af(sa)) { case AF_INET: return IN_IS_ADDR_LINKLOCAL(sa->u.in.sin_addr.s_addr); #ifdef HAVE_INET6 case AF_INET6: return IN6_IS_ADDR_LINKLOCAL(&sa->u.in6.sin6_addr); #endif default: return false; } } /** * Check if socket address is a loopback address * * @param sa Socket address * * @return true if loopback address, otherwise false */ bool sa_is_loopback(const struct sa *sa) { if (!sa) return false; switch (sa_af(sa)) { case AF_INET: return INADDR_LOOPBACK == ntohl(sa->u.in.sin_addr.s_addr); #ifdef HAVE_INET6 case AF_INET6: return IN6_IS_ADDR_LOOPBACK(&sa->u.in6.sin6_addr); #endif default: return false; } } /** * Check if socket address is any/unspecified address * * @param sa Socket address * * @return true if any address, otherwise false */ bool sa_is_any(const struct sa *sa) { if (!sa) return false; switch (sa_af(sa)) { case AF_INET: return INADDR_ANY == ntohl(sa->u.in.sin_addr.s_addr); #ifdef HAVE_INET6 case AF_INET6: return IN6_IS_ADDR_UNSPECIFIED(&sa->u.in6.sin6_addr); #endif default: return false; } } re-1.1.0/src/sa/sa.h000066400000000000000000000003501373627245400141000ustar00rootroot00000000000000/** * @file sa.h Internal interface to Socket Address * * Copyright (C) 2010 Creytiv.com */ /* Net ntop/pton */ int net_inet_ntop(const struct sa *sa, char *buf, int size); int net_inet_pton(const char *addr, struct sa *sa); re-1.1.0/src/sdp/000077500000000000000000000000001373627245400135115ustar00rootroot00000000000000re-1.1.0/src/sdp/attr.c000066400000000000000000000044251373627245400146340ustar00rootroot00000000000000/** * @file sdp/attr.c SDP Attributes * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "sdp.h" struct sdp_attr { struct le le; char *name; char *val; }; static void destructor(void *arg) { struct sdp_attr *attr = arg; list_unlink(&attr->le); mem_deref(attr->name); mem_deref(attr->val); } int sdp_attr_add(struct list *lst, struct pl *name, struct pl *val) { struct sdp_attr *attr; int err; attr = mem_zalloc(sizeof(*attr), destructor); if (!attr) return ENOMEM; list_append(lst, &attr->le, attr); err = pl_strdup(&attr->name, name); if (pl_isset(val)) err |= pl_strdup(&attr->val, val); if (err) mem_deref(attr); return err; } int sdp_attr_addv(struct list *lst, const char *name, const char *val, va_list ap) { struct sdp_attr *attr; int err; attr = mem_zalloc(sizeof(*attr), destructor); if (!attr) return ENOMEM; list_append(lst, &attr->le, attr); err = str_dup(&attr->name, name); if (str_isset(val)) err |= re_vsdprintf(&attr->val, val, ap); if (err) mem_deref(attr); return err; } void sdp_attr_del(const struct list *lst, const char *name) { struct le *le = list_head(lst); while (le) { struct sdp_attr *attr = le->data; le = le->next; if (0 == str_casecmp(name, attr->name)) mem_deref(attr); } } const char *sdp_attr_apply(const struct list *lst, const char *name, sdp_attr_h *attrh, void *arg) { struct le *le = list_head(lst); while (le) { const struct sdp_attr *attr = le->data; le = le->next; if (name && (!attr->name || strcmp(name, attr->name))) continue; if (!attrh || attrh(attr->name, attr->val?attr->val : "", arg)) return attr->val ? attr->val : ""; } return NULL; } int sdp_attr_print(struct re_printf *pf, const struct sdp_attr *attr) { if (!attr) return 0; if (attr->val) return re_hprintf(pf, "a=%s:%s\r\n", attr->name, attr->val); else return re_hprintf(pf, "a=%s\r\n", attr->name); } int sdp_attr_debug(struct re_printf *pf, const struct sdp_attr *attr) { if (!attr) return 0; if (attr->val) return re_hprintf(pf, "%s='%s'", attr->name, attr->val); else return re_hprintf(pf, "%s", attr->name); } re-1.1.0/src/sdp/format.c000066400000000000000000000112011373627245400151400ustar00rootroot00000000000000/** * @file sdp/format.c SDP format * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "sdp.h" static void destructor(void *arg) { struct sdp_format *fmt = arg; list_unlink(&fmt->le); if (fmt->ref) mem_deref(fmt->data); mem_deref(fmt->id); mem_deref(fmt->params); mem_deref(fmt->rparams); mem_deref(fmt->name); } /** * Add an SDP Format to an SDP Media line * * @param fmtp Pointer to allocated SDP Format * @param m SDP Media line * @param prepend True to prepend, False to append * @param id Format identifier * @param name Format name * @param srate Sampling rate * @param ch Number of channels * @param ench Optional format encode handler * @param cmph Optional format comparison handler * @param data Opaque data for handler * @param ref True to mem_ref() data * @param params Formatted parameters * * @return 0 if success, otherwise errorcode */ int sdp_format_add(struct sdp_format **fmtp, struct sdp_media *m, bool prepend, const char *id, const char *name, uint32_t srate, uint8_t ch, sdp_fmtp_enc_h *ench, sdp_fmtp_cmp_h *cmph, void *data, bool ref, const char *params, ...) { struct sdp_format *fmt; int err; if (!m) return EINVAL; if (!id && (m->dynpt > RTP_DYNPT_END)) return ERANGE; fmt = mem_zalloc(sizeof(*fmt), destructor); if (!fmt) return ENOMEM; if (prepend) list_prepend(&m->lfmtl, &fmt->le, fmt); else list_append(&m->lfmtl, &fmt->le, fmt); if (id) err = str_dup(&fmt->id, id); else err = re_sdprintf(&fmt->id, "%i", m->dynpt++); if (err) goto out; if (name) { err = str_dup(&fmt->name, name); if (err) goto out; } if (params) { va_list ap; va_start(ap, params); err = re_vsdprintf(&fmt->params, params, ap); va_end(ap); if (err) goto out; } fmt->pt = atoi(fmt->id); fmt->srate = srate; fmt->ch = ch; fmt->ench = ench; fmt->cmph = cmph; fmt->data = ref ? mem_ref(data) : data; fmt->ref = ref; fmt->sup = true; out: if (err) mem_deref(fmt); else if (fmtp) *fmtp = fmt; return err; } int sdp_format_radd(struct sdp_media *m, const struct pl *id) { struct sdp_format *fmt; int err; if (!m || !id) return EINVAL; fmt = mem_zalloc(sizeof(*fmt), destructor); if (!fmt) return ENOMEM; list_append(&m->rfmtl, &fmt->le, fmt); err = pl_strdup(&fmt->id, id); if (err) goto out; fmt->pt = atoi(fmt->id); out: if (err) mem_deref(fmt); return err; } struct sdp_format *sdp_format_find(const struct list *lst, const struct pl *id) { struct le *le; if (!lst || !id) return NULL; for (le=lst->head; le; le=le->next) { struct sdp_format *fmt = le->data; if (pl_strcmp(id, fmt->id)) continue; return fmt; } return NULL; } /** * Set the parameters of an SDP format * * @param fmt SDP Format * @param params Formatted parameters * * @return 0 if success, otherwise errorcode */ int sdp_format_set_params(struct sdp_format *fmt, const char *params, ...) { int err = 0; if (!fmt) return EINVAL; fmt->params = mem_deref(fmt->params); if (params) { va_list ap; va_start(ap, params); err = re_vsdprintf(&fmt->params, params, ap); va_end(ap); } return err; } /** * Compare two SDP Formats * * @param fmt1 First SDP format * @param fmt2 Second SDP format * * @return True if matching, False if not */ bool sdp_format_cmp(const struct sdp_format *fmt1, const struct sdp_format *fmt2) { if (!fmt1 || !fmt2) return false; if (fmt1->pt < RTP_DYNPT_START && fmt2->pt < RTP_DYNPT_START) { if (!fmt1->id || !fmt2->id) return false; return strcmp(fmt1->id, fmt2->id) ? false : true; } if (str_casecmp(fmt1->name, fmt2->name)) return false; if (fmt1->srate != fmt2->srate) return false; if (fmt1->ch != fmt2->ch) return false; if (fmt1->cmph && !fmt1->cmph(fmt1->params, fmt2->params, fmt1->data)) return false; if (fmt2->cmph && !fmt2->cmph(fmt2->params, fmt1->params, fmt2->data)) return false; return true; } /** * Print SDP Format debug information * * @param pf Print function for output * @param fmt SDP Format * * @return 0 if success, otherwise errorcode */ int sdp_format_debug(struct re_printf *pf, const struct sdp_format *fmt) { int err; if (!fmt) return 0; err = re_hprintf(pf, "%3s", fmt->id); if (fmt->name) err |= re_hprintf(pf, " %s/%u/%u", fmt->name, fmt->srate, fmt->ch); if (fmt->params) err |= re_hprintf(pf, " (%s)", fmt->params); if (fmt->sup) err |= re_hprintf(pf, " *"); return err; } re-1.1.0/src/sdp/media.c000066400000000000000000000425711373627245400147450ustar00rootroot00000000000000/** * @file sdp/media.c SDP Media * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "sdp.h" static void destructor(void *arg) { struct sdp_media *m = arg; unsigned i; list_flush(&m->lfmtl); list_flush(&m->rfmtl); list_flush(&m->rattrl); list_flush(&m->lattrl); if (m->le.list) { m->disabled = true; m->ench = NULL; mem_ref(m); return; } for (i=0; iprotov); i++) mem_deref(m->protov[i]); list_unlink(&m->le); mem_deref(m->name); mem_deref(m->proto); mem_deref(m->uproto); } static int media_alloc(struct sdp_media **mp, struct list *list) { struct sdp_media *m; int i; m = mem_zalloc(sizeof(*m), destructor); if (!m) return ENOMEM; list_append(list, &m->le, m); m->ldir = SDP_SENDRECV; m->rdir = SDP_SENDRECV; m->dynpt = RTP_DYNPT_START; sa_init(&m->laddr, AF_INET); sa_init(&m->raddr, AF_INET); sa_init(&m->laddr_rtcp, AF_INET); sa_init(&m->raddr_rtcp, AF_INET); for (i=0; ilbwv[i] = -1; m->rbwv[i] = -1; } *mp = m; return 0; } /** * Add a media line to an SDP Session * * @param mp Pointer to allocated SDP Media line object * @param sess SDP Session * @param name Media name * @param port Port number * @param proto Transport protocol * * @return 0 if success, otherwise errorcode */ int sdp_media_add(struct sdp_media **mp, struct sdp_session *sess, const char *name, uint16_t port, const char *proto) { struct sdp_media *m; int err; if (!sess || !name || !proto) return EINVAL; err = media_alloc(&m, &sess->lmedial); if (err) return err; err = str_dup(&m->name, name); err |= str_dup(&m->proto, proto); if (err) goto out; sa_set_port(&m->laddr, port); out: if (err) mem_deref(m); else if (mp) *mp = m; return err; } /** * Add a remote SDP media line to an SDP Session * * @param mp Pointer to allocated SDP Media line object * @param sess SDP Session * @param name Media name * @param proto Transport protocol * * @return 0 if success, otherwise errorcode */ int sdp_media_radd(struct sdp_media **mp, struct sdp_session *sess, const struct pl *name, const struct pl *proto) { struct sdp_media *m; int err; if (!mp || !sess || !name || !proto) return EINVAL; err = media_alloc(&m, &sess->medial); if (err) return err; m->disabled = true; err = pl_strdup(&m->name, name); err |= pl_strdup(&m->proto, proto); if (err) mem_deref(m); else *mp = m; return err; } /** * Reset the remote part of an SDP Media line * * @param m SDP Media line */ void sdp_media_rreset(struct sdp_media *m) { int i; if (!m) return; sa_init(&m->raddr, AF_INET); sa_init(&m->raddr_rtcp, AF_INET); list_flush(&m->rfmtl); list_flush(&m->rattrl); m->rdir = SDP_SENDRECV; for (i=0; irbwv[i] = -1; } /** * Compare media line protocols * * @param m SDP Media line * @param proto Transport protocol * @param update Update media protocol if match is found in alternate set * * @return True if matching, False if not */ bool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto, bool update) { unsigned i; if (!m || !proto) return false; if (!pl_strcmp(proto, m->proto)) return true; for (i=0; iprotov); i++) { if (!m->protov[i] || pl_strcmp(proto, m->protov[i])) continue; if (update) { mem_deref(m->proto); m->proto = mem_ref(m->protov[i]); } return true; } return false; } /** * Find an SDP Media line from name and transport protocol * * @param sess SDP Session * @param name Media name * @param proto Transport protocol * @param update_proto Update media transport protocol * * @return Matching media line if found, NULL if not found */ struct sdp_media *sdp_media_find(const struct sdp_session *sess, const struct pl *name, const struct pl *proto, bool update_proto) { struct le *le; if (!sess || !name || !proto) return NULL; for (le=sess->lmedial.head; le; le=le->next) { struct sdp_media *m = le->data; if (pl_strcmp(name, m->name)) continue; if (!sdp_media_proto_cmp(m, proto, update_proto)) continue; return m; } return NULL; } /** * Align the locate/remote formats of an SDP Media line * * @param m SDP Media line * @param offer True if SDP Offer, False if SDP Answer */ void sdp_media_align_formats(struct sdp_media *m, bool offer) { struct sdp_format *rfmt, *lfmt; struct le *rle, *lle; if (!m || m->disabled || !sa_port(&m->raddr) || m->fmt_ignore) return; for (lle=m->lfmtl.head; lle; lle=lle->next) { lfmt = lle->data; lfmt->rparams = mem_deref(lfmt->rparams); lfmt->sup = false; } for (rle=m->rfmtl.head; rle; rle=rle->next) { rfmt = rle->data; for (lle=m->lfmtl.head; lle; lle=lle->next) { lfmt = lle->data; if (sdp_format_cmp(lfmt, rfmt)) break; } if (!lle) { rfmt->sup = false; continue; } mem_deref(lfmt->rparams); lfmt->rparams = mem_ref(rfmt->params); lfmt->sup = true; rfmt->sup = true; if (rfmt->ref) rfmt->data = mem_deref(rfmt->data); else rfmt->data = NULL; if (lfmt->ref) rfmt->data = mem_ref(lfmt->data); else rfmt->data = lfmt->data; rfmt->ref = lfmt->ref; if (offer) { mem_deref(lfmt->id); lfmt->id = mem_ref(rfmt->id); lfmt->pt = atoi(lfmt->id ? lfmt->id : ""); list_unlink(&lfmt->le); list_append(&m->lfmtl, &lfmt->le, lfmt); } } if (offer) { for (lle=m->lfmtl.tail; lle; ) { lfmt = lle->data; lle = lle->prev; if (!lfmt->sup) { list_unlink(&lfmt->le); list_append(&m->lfmtl, &lfmt->le, lfmt); } } } } /** * Set alternative protocols for an SDP Media line * * @param m SDP Media line * @param protoc Number of alternative protocols * * @return 0 if success, otherwise errorcode */ int sdp_media_set_alt_protos(struct sdp_media *m, unsigned protoc, ...) { const char *proto; int err = 0; unsigned i; va_list ap; if (!m) return EINVAL; va_start(ap, protoc); for (i=0; iprotov); i++) { m->protov[i] = mem_deref(m->protov[i]); if (i >= protoc) continue; proto = va_arg(ap, const char *); if (proto) err |= str_dup(&m->protov[i], proto); } va_end(ap); return err; } /** * Set SDP Media line encode handler * * @param m SDP Media line * @param ench Encode handler * @param arg Encode handler argument */ void sdp_media_set_encode_handler(struct sdp_media *m, sdp_media_enc_h *ench, void *arg) { if (!m) return; m->ench = ench; m->arg = arg; } /** * Set an SDP Media line to ignore formats * * @param m SDP Media line * @param fmt_ignore True for ignore formats, otherwise false */ void sdp_media_set_fmt_ignore(struct sdp_media *m, bool fmt_ignore) { if (!m) return; m->fmt_ignore = fmt_ignore; } /** * Set an SDP Media line to enabled/disabled * * @param m SDP Media line * @param disabled True for disabled, False for enabled */ void sdp_media_set_disabled(struct sdp_media *m, bool disabled) { if (!m) return; m->disabled = disabled; } /** * Check if an SDP Media line is disabled * * @param m SDP Media line * @return True if disabled, otherwise false */ bool sdp_media_disabled(struct sdp_media *m) { if (!m) return true; return m->disabled; } /** * Set the local port number of an SDP Media line * * @param m SDP Media line * @param port Port number */ void sdp_media_set_lport(struct sdp_media *m, uint16_t port) { if (!m) return; sa_set_port(&m->laddr, port); } /** * Set the local network address of an SDP media line * * @param m SDP Media line * @param laddr Local network address */ void sdp_media_set_laddr(struct sdp_media *m, const struct sa *laddr) { if (!m || !laddr) return; m->laddr = *laddr; } /** * Set a local bandwidth of an SDP Media line * * @param m SDP Media line * @param type Bandwidth type * @param bw Bandwidth value */ void sdp_media_set_lbandwidth(struct sdp_media *m, enum sdp_bandwidth type, int32_t bw) { if (!m || type >= SDP_BANDWIDTH_MAX) return; m->lbwv[type] = bw; } /** * Set the local RTCP port number of an SDP Media line * * @param m SDP Media line * @param port RTCP Port number */ void sdp_media_set_lport_rtcp(struct sdp_media *m, uint16_t port) { if (!m) return; sa_set_port(&m->laddr_rtcp, port); } /** * Set the local RTCP network address of an SDP media line * * @param m SDP Media line * @param laddr Local RTCP network address */ void sdp_media_set_laddr_rtcp(struct sdp_media *m, const struct sa *laddr) { if (!m || !laddr) return; m->laddr_rtcp = *laddr; } /** * Set the local direction flag of an SDP Media line * * @param m SDP Media line * @param dir Media direction flag */ void sdp_media_set_ldir(struct sdp_media *m, enum sdp_dir dir) { if (!m) return; m->ldir = dir; } /** * Set a local attribute of an SDP Media line * * @param m SDP Media line * @param replace True to replace attribute, False to append * @param name Attribute name * @param value Formatted attribute value * * @return 0 if success, otherwise errorcode */ int sdp_media_set_lattr(struct sdp_media *m, bool replace, const char *name, const char *value, ...) { va_list ap; int err; if (!m || !name) return EINVAL; if (replace) sdp_attr_del(&m->lattrl, name); va_start(ap, value); err = sdp_attr_addv(&m->lattrl, name, value, ap); va_end(ap); return err; } /** * Delete a local attribute of an SDP Media line * * @param m SDP Media line * @param name Attribute name */ void sdp_media_del_lattr(struct sdp_media *m, const char *name) { if (!m || !name) return; sdp_attr_del(&m->lattrl, name); } const char *sdp_media_proto(const struct sdp_media *m) { return m ? m->proto : NULL; } /** * Get the remote port number of an SDP Media line * * @param m SDP Media line * * @return Remote port number */ uint16_t sdp_media_rport(const struct sdp_media *m) { return m ? sa_port(&m->raddr) : 0; } /** * Get the remote network address of an SDP Media line * * @param m SDP Media line * * @return Remote network address */ const struct sa *sdp_media_raddr(const struct sdp_media *m) { return m ? &m->raddr : NULL; } /** * Get the local network address of an SDP Media line * * @param m SDP Media line * * @return Local network address */ const struct sa *sdp_media_laddr(const struct sdp_media *m) { return m ? &m->laddr : NULL; } /** * Get the remote RTCP network address of an SDP Media line * * @param m SDP Media line * @param raddr On return, contains remote RTCP network address */ void sdp_media_raddr_rtcp(const struct sdp_media *m, struct sa *raddr) { if (!m || !raddr) return; if (sa_isset(&m->raddr_rtcp, SA_ALL)) { *raddr = m->raddr_rtcp; } else if (sa_isset(&m->raddr_rtcp, SA_PORT)) { *raddr = m->raddr; sa_set_port(raddr, sa_port(&m->raddr_rtcp)); } else { uint16_t port = sa_port(&m->raddr); *raddr = m->raddr; sa_set_port(raddr, port ? port + 1 : 0); } } /** * Get a remote bandwidth of an SDP Media line * * @param m SDP Media line * @param type Bandwidth type * * @return Remote bandwidth value */ int32_t sdp_media_rbandwidth(const struct sdp_media *m, enum sdp_bandwidth type) { if (!m || type >= SDP_BANDWIDTH_MAX) return 0; return m->rbwv[type]; } /** * Get the local media direction of an SDP Media line * * @param m SDP Media line * * @return Local media direction */ enum sdp_dir sdp_media_ldir(const struct sdp_media *m) { return m ? m->ldir : SDP_INACTIVE; } /** * Get the remote media direction of an SDP Media line * * @param m SDP Media line * * @return Remote media direction */ enum sdp_dir sdp_media_rdir(const struct sdp_media *m) { return m ? m->rdir : SDP_INACTIVE; } /** * Get the combined media direction of an SDP Media line * * @param m SDP Media line * * @return Combined media direction */ enum sdp_dir sdp_media_dir(const struct sdp_media *m) { return m ? (enum sdp_dir)(m->ldir & m->rdir) : SDP_INACTIVE; } /** * Find a local SDP format from a payload type * * @param m SDP Media line * @param pt Payload type * * @return Local SDP format if found, NULL if not found */ const struct sdp_format *sdp_media_lformat(const struct sdp_media *m, int pt) { struct le *le; if (!m) return NULL; for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (pt == fmt->pt) return fmt; } return NULL; } /** * Find a remote SDP format from a format name * * @param m SDP Media line * @param name Format name * * @return Remote SDP format if found, NULL if not found */ const struct sdp_format *sdp_media_rformat(const struct sdp_media *m, const char *name) { struct le *le; if (!m || !sa_port(&m->raddr)) return NULL; for (le=m->rfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (!fmt->sup) continue; if (name && str_casecmp(name, fmt->name)) continue; return fmt; } return NULL; } /** * Find an SDP Format of an SDP Media line * * @param m SDP Media line * @param local True if local media, False if remote * @param id SDP format id * @param pt Payload type * @param name Format name * @param srate Sampling rate * @param ch Number of channels * * @return SDP Format if found, NULL if not found */ struct sdp_format *sdp_media_format(const struct sdp_media *m, bool local, const char *id, int pt, const char *name, int32_t srate, int8_t ch) { return sdp_media_format_apply(m, local, id, pt, name, srate, ch, NULL, NULL); } /** * Apply a function handler to all matching SDP formats * * @param m SDP Media line * @param local True if local media, False if remote * @param id SDP format id * @param pt Payload type * @param name Format name * @param srate Sampling rate * @param ch Number of channels * @param fmth SDP Format handler * @param arg Handler argument * * @return SDP Format if found, NULL if not found */ struct sdp_format *sdp_media_format_apply(const struct sdp_media *m, bool local, const char *id, int pt, const char *name, int32_t srate, int8_t ch, sdp_format_h *fmth, void *arg) { struct le *le; if (!m) return NULL; le = local ? m->lfmtl.head : m->rfmtl.head; while (le) { struct sdp_format *fmt = le->data; le = le->next; if (id && (!fmt->id || strcmp(id, fmt->id))) continue; if (pt >= 0 && pt != fmt->pt) continue; if (name && str_casecmp(name, fmt->name)) continue; if (srate >= 0 && (uint32_t)srate != fmt->srate) continue; if (ch >= 0 && (uint8_t)ch != fmt->ch) continue; if (!fmth || fmth(fmt, arg)) return fmt; } return NULL; } /** * Get the list of SDP Formats * * @param m SDP Media line * @param local True if local, False if remote * * @return List of SDP Formats */ const struct list *sdp_media_format_lst(const struct sdp_media *m, bool local) { if (!m) return NULL; return local ? &m->lfmtl : &m->rfmtl; } /** * Get a remote attribute from an SDP Media line * * @param m SDP Media line * @param name Attribute name * * @return Attribute value, NULL if not found */ const char *sdp_media_rattr(const struct sdp_media *m, const char *name) { if (!m || !name) return NULL; return sdp_attr_apply(&m->rattrl, name, NULL, NULL); } /** * Get a remote attribute from an SDP Media line or the SDP session * * @param m SDP Media line * @param sess SDP Session * @param name Attribute name * * @return Attribute value, NULL if not found */ const char *sdp_media_session_rattr(const struct sdp_media *m, const struct sdp_session *sess, const char *name) { const char *val; val = sdp_media_rattr(m, name); if (!val) val = sdp_session_rattr(sess, name); return val; } /** * Apply a function handler to all matching remote attributes * * @param m SDP Media line * @param name Attribute name * @param attrh Attribute handler * @param arg Handler argument * * @return Attribute value if match */ const char *sdp_media_rattr_apply(const struct sdp_media *m, const char *name, sdp_attr_h *attrh, void *arg) { if (!m) return NULL; return sdp_attr_apply(&m->rattrl, name, attrh, arg); } /** * Get the name of an SDP Media line * * @param m SDP Media line * * @return SDP Media line name */ const char *sdp_media_name(const struct sdp_media *m) { return m ? m->name : NULL; } /** * Print SDP Media line debug information * * @param pf Print function for output * @param m SDP Media line * * @return 0 if success, otherwise errorcode */ int sdp_media_debug(struct re_printf *pf, const struct sdp_media *m) { struct le *le; int err; if (!m) return 0; err = re_hprintf(pf, "%s %s\n", m->name, m->proto); err |= re_hprintf(pf, " local formats:\n"); for (le=m->lfmtl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_format_debug, le->data); err |= re_hprintf(pf, " remote formats:\n"); for (le=m->rfmtl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_format_debug, le->data); err |= re_hprintf(pf, " local attributes:\n"); for (le=m->lattrl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data); err |= re_hprintf(pf, " remote attributes:\n"); for (le=m->rattrl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data); return err; } re-1.1.0/src/sdp/mod.mk000066400000000000000000000002721373627245400146220ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sdp/attr.c SRCS += sdp/format.c SRCS += sdp/media.c SRCS += sdp/msg.c SRCS += sdp/session.c SRCS += sdp/str.c SRCS += sdp/util.c re-1.1.0/src/sdp/msg.c000066400000000000000000000234561373627245400144550ustar00rootroot00000000000000/** * @file sdp/msg.c SDP Message processing * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include "sdp.h" static int attr_decode_fmtp(struct sdp_media *m, const struct pl *pl) { struct sdp_format *fmt; struct pl id, params; if (!m) return 0; if (re_regex(pl->p, pl->l, "[^ ]+ [^]*", &id, ¶ms)) return EBADMSG; fmt = sdp_format_find(&m->rfmtl, &id); if (!fmt) return 0; fmt->params = mem_deref(fmt->params); return pl_strdup(&fmt->params, ¶ms); } static int attr_decode_rtcp(struct sdp_media *m, const struct pl *pl) { struct pl port, addr; int err = 0; if (!m) return 0; if (!re_regex(pl->p, pl->l, "[0-9]+ IN IP[46]1 [^ ]+", &port, NULL, &addr)) { (void)sa_set(&m->raddr_rtcp, &addr, pl_u32(&port)); } else if (!re_regex(pl->p, pl->l, "[0-9]+", &port)) { sa_set_port(&m->raddr_rtcp, pl_u32(&port)); } else err = EBADMSG; return err; } static int attr_decode_rtpmap(struct sdp_media *m, const struct pl *pl) { struct pl id, name, srate, ch; struct sdp_format *fmt; int err; if (!m) return 0; if (re_regex(pl->p, pl->l, "[^ ]+ [^/]+/[0-9]+[/]*[^]*", &id, &name, &srate, NULL, &ch)) return EBADMSG; fmt = sdp_format_find(&m->rfmtl, &id); if (!fmt) return 0; fmt->name = mem_deref(fmt->name); err = pl_strdup(&fmt->name, &name); if (err) return err; fmt->srate = pl_u32(&srate); fmt->ch = ch.l ? pl_u32(&ch) : 1; return 0; } static int attr_decode(struct sdp_session *sess, struct sdp_media *m, enum sdp_dir *dir, const struct pl *pl) { struct pl name, val; int err = 0; if (re_regex(pl->p, pl->l, "[^:]+:[^]+", &name, &val)) { name = *pl; val = pl_null; } if (!pl_strcmp(&name, "fmtp")) err = attr_decode_fmtp(m, &val); else if (!pl_strcmp(&name, "inactive")) *dir = SDP_INACTIVE; else if (!pl_strcmp(&name, "recvonly")) *dir = SDP_SENDONLY; else if (!pl_strcmp(&name, "rtcp")) err = attr_decode_rtcp(m, &val); else if (!pl_strcmp(&name, "rtpmap")) err = attr_decode_rtpmap(m, &val); else if (!pl_strcmp(&name, "sendonly")) *dir = SDP_RECVONLY; else if (!pl_strcmp(&name, "sendrecv")) *dir = SDP_SENDRECV; else err = sdp_attr_add(m ? &m->rattrl : &sess->rattrl, &name, &val); return err; } static int bandwidth_decode(int32_t *bwv, const struct pl *pl) { struct pl type, bw; if (re_regex(pl->p, pl->l, "[^:]+:[0-9]+", &type, &bw)) return EBADMSG; if (!pl_strcmp(&type, "CT")) bwv[SDP_BANDWIDTH_CT] = pl_u32(&bw); else if (!pl_strcmp(&type, "AS")) bwv[SDP_BANDWIDTH_AS] = pl_u32(&bw); else if (!pl_strcmp(&type, "RS")) bwv[SDP_BANDWIDTH_RS] = pl_u32(&bw); else if (!pl_strcmp(&type, "RR")) bwv[SDP_BANDWIDTH_RR] = pl_u32(&bw); else if (!pl_strcmp(&type, "TIAS")) bwv[SDP_BANDWIDTH_TIAS] = pl_u32(&bw); return 0; } static int conn_decode(struct sa *sa, const struct pl *pl) { struct pl v; if (re_regex(pl->p, pl->l, "IN IP[46]1 [^ ]+", NULL, &v)) return EBADMSG; (void)sa_set(sa, &v, sa_port(sa)); return 0; } static int media_decode(struct sdp_media **mp, struct sdp_session *sess, bool offer, const struct pl *pl) { struct pl name, port, proto, fmtv, fmt; struct sdp_media *m; int err; if (re_regex(pl->p, pl->l, "[a-z]+ [^ ]+ [^ ]+[^]*", &name, &port, &proto, &fmtv)) return EBADMSG; m = list_ledata(*mp ? (*mp)->le.next : sess->medial.head); if (!m) { if (!offer) return EPROTO; m = sdp_media_find(sess, &name, &proto, true); if (!m) { err = sdp_media_radd(&m, sess, &name, &proto); if (err) return err; } else { list_unlink(&m->le); list_append(&sess->medial, &m->le, m); } m->uproto = mem_deref(m->uproto); } else { if (pl_strcmp(&name, m->name)) return offer ? ENOTSUP : EPROTO; m->uproto = mem_deref(m->uproto); if (!sdp_media_proto_cmp(m, &proto, offer)) { err = pl_strdup(&m->uproto, &proto); if (err) return err; } } while (!re_regex(fmtv.p, fmtv.l, " [^ ]+", &fmt)) { pl_advance(&fmtv, fmt.p + fmt.l - fmtv.p); err = sdp_format_radd(m, &fmt); if (err) return err; } m->raddr = sess->raddr; sa_set_port(&m->raddr, m->uproto ? 0 : pl_u32(&port)); m->rdir = sess->rdir; *mp = m; return 0; } static int version_decode(const struct pl *pl) { return pl_strcmp(pl, "0") ? ENOSYS : 0; } /** * Decode an SDP message into an SDP Session * * @param sess SDP Session * @param mb Memory buffer containing SDP message * @param offer True if SDP offer, False if SDP answer * * @return 0 if success, otherwise errorcode */ int sdp_decode(struct sdp_session *sess, struct mbuf *mb, bool offer) { struct sdp_media *m; struct pl pl, val; struct le *le; char type = 0; int err = 0; if (!sess || !mb) return EINVAL; sdp_session_rreset(sess); for (le=sess->medial.head; le; le=le->next) { m = le->data; sdp_media_rreset(m); } pl.p = (const char *)mbuf_buf(mb); pl.l = mbuf_get_left(mb); m = NULL; for (;pl.l && !err; pl.p++, pl.l--) { switch (*pl.p) { case '\r': case '\n': if (!type) break; switch (type) { case 'a': err = attr_decode(sess, m, m ? &m->rdir : &sess->rdir, &val); break; case 'b': err = bandwidth_decode(m? m->rbwv : sess->rbwv, &val); break; case 'c': err = conn_decode(m ? &m->raddr : &sess->raddr, &val); break; case 'm': err = media_decode(&m, sess, offer, &val); break; case 'v': err = version_decode(&val); break; } #if 0 if (err) re_printf("** %c='%r': %m\n", type, &val, err); #endif type = 0; break; default: if (type) { val.l++; break; } if (pl.l < 2 || *(pl.p + 1) != '=') { err = EBADMSG; break; } type = *pl.p; val.p = pl.p + 2; val.l = 0; pl.p += 1; pl.l -= 1; break; } } if (err) return err; if (type) return EBADMSG; for (le=sess->medial.head; le; le=le->next) sdp_media_align_formats(le->data, offer); return 0; } static int media_encode(const struct sdp_media *m, struct mbuf *mb, bool offer) { enum sdp_bandwidth i; const char *proto; int err, supc = 0; bool disabled; struct le *le; uint16_t port; for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (fmt->sup) ++supc; } if (m->uproto && !offer) { disabled = true; port = 0; proto = m->uproto; } else if (m->disabled || supc == 0 || (!offer && !sa_port(&m->raddr))) { disabled = true; port = 0; proto = m->proto; } else { disabled = false; port = sa_port(&m->laddr); proto = m->proto; } err = mbuf_printf(mb, "m=%s %u %s", m->name, port, proto); if (disabled) { err |= mbuf_write_str(mb, " 0\r\n"); return err; } for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (!fmt->sup) continue; err |= mbuf_printf(mb, " %s", fmt->id); } err |= mbuf_write_str(mb, "\r\n"); if (sa_isset(&m->laddr, SA_ADDR)) { const int ipver = sa_af(&m->laddr) == AF_INET ? 4 : 6; err |= mbuf_printf(mb, "c=IN IP%d %j\r\n", ipver, &m->laddr); } for (i=SDP_BANDWIDTH_MIN; ilbwv[i] < 0) continue; err |= mbuf_printf(mb, "b=%s:%i\r\n", sdp_bandwidth_name(i), m->lbwv[i]); } for (le=m->lfmtl.head; le; le=le->next) { const struct sdp_format *fmt = le->data; if (!fmt->sup || !str_isset(fmt->name)) continue; err |= mbuf_printf(mb, "a=rtpmap:%s %s/%u", fmt->id, fmt->name, fmt->srate); if (fmt->ch > 1) err |= mbuf_printf(mb, "/%u", fmt->ch); err |= mbuf_printf(mb, "\r\n"); if (str_isset(fmt->params)) err |= mbuf_printf(mb, "a=fmtp:%s %s\r\n", fmt->id, fmt->params); if (fmt->ench) err |= fmt->ench(mb, fmt, offer, fmt->data); } if (sa_isset(&m->laddr_rtcp, SA_ALL)) err |= mbuf_printf(mb, "a=rtcp:%u IN IP%d %j\r\n", sa_port(&m->laddr_rtcp), (AF_INET == sa_af(&m->laddr_rtcp)) ? 4 : 6, &m->laddr_rtcp); else if (sa_isset(&m->laddr_rtcp, SA_PORT)) err |= mbuf_printf(mb, "a=rtcp:%u\r\n", sa_port(&m->laddr_rtcp)); err |= mbuf_printf(mb, "a=%s\r\n", sdp_dir_name(offer ? m->ldir : m->ldir & m->rdir)); for (le = m->lattrl.head; le; le = le->next) err |= mbuf_printf(mb, "%H", sdp_attr_print, le->data); if (m->ench) err |= m->ench(mb, offer, m->arg); return err; } /** * Encode an SDP Session into a memory buffer * * @param mbp Pointer to allocated memory buffer * @param sess SDP Session * @param offer True if SDP Offer, False if SDP Answer * * @return 0 if success, otherwise errorcode */ int sdp_encode(struct mbuf **mbp, struct sdp_session *sess, bool offer) { const int ipver = sa_af(&sess->laddr) == AF_INET ? 4 : 6; enum sdp_bandwidth i; struct mbuf *mb; struct le *le; int err; if (!mbp || !sess) return EINVAL; mb = mbuf_alloc(512); if (!mb) return ENOMEM; err = mbuf_printf(mb, "v=%u\r\n", SDP_VERSION); err |= mbuf_printf(mb, "o=- %u %u IN IP%d %j\r\n", sess->id, sess->ver++, ipver, &sess->laddr); err |= mbuf_write_str(mb, "s=-\r\n"); err |= mbuf_printf(mb, "c=IN IP%d %j\r\n", ipver, &sess->laddr); for (i=SDP_BANDWIDTH_MIN; ilbwv[i] < 0) continue; err |= mbuf_printf(mb, "b=%s:%i\r\n", sdp_bandwidth_name(i), sess->lbwv[i]); } err |= mbuf_write_str(mb, "t=0 0\r\n"); for (le = sess->lattrl.head; le; le = le->next) err |= mbuf_printf(mb, "%H", sdp_attr_print, le->data); for (le=sess->lmedial.head; offer && le;) { struct sdp_media *m = le->data; le = le->next; if (m->disabled) continue; list_unlink(&m->le); list_append(&sess->medial, &m->le, m); } for (le=sess->medial.head; le; le=le->next) { struct sdp_media *m = le->data; err |= media_encode(m, mb, offer); } mb->pos = 0; if (err) mem_deref(mb); else *mbp = mb; return err; } re-1.1.0/src/sdp/sdp.h000066400000000000000000000041031373627245400144460ustar00rootroot00000000000000/** * @file sdp.h Internal SDP interface * * Copyright (C) 2010 Creytiv.com */ enum { RTP_DYNPT_START = 96, RTP_DYNPT_END = 127, }; struct sdp_session { struct list lmedial; struct list medial; struct list lattrl; struct list rattrl; struct sa laddr; struct sa raddr; int32_t lbwv[SDP_BANDWIDTH_MAX]; int32_t rbwv[SDP_BANDWIDTH_MAX]; uint32_t id; uint32_t ver; enum sdp_dir rdir; }; struct sdp_media { struct le le; struct list lfmtl; struct list rfmtl; struct list lattrl; struct list rattrl; struct sa laddr; struct sa raddr; struct sa laddr_rtcp; struct sa raddr_rtcp; int32_t lbwv[SDP_BANDWIDTH_MAX]; int32_t rbwv[SDP_BANDWIDTH_MAX]; char *name; char *proto; char *protov[8]; char *uproto; /* unsupported protocol */ sdp_media_enc_h *ench; void *arg; enum sdp_dir ldir; enum sdp_dir rdir; bool fmt_ignore; bool disabled; int dynpt; }; /* session */ void sdp_session_rreset(struct sdp_session *sess); /* media */ int sdp_media_radd(struct sdp_media **mp, struct sdp_session *sess, const struct pl *name, const struct pl *proto); void sdp_media_rreset(struct sdp_media *m); bool sdp_media_proto_cmp(struct sdp_media *m, const struct pl *proto, bool update); struct sdp_media *sdp_media_find(const struct sdp_session *sess, const struct pl *name, const struct pl *proto, bool update_proto); void sdp_media_align_formats(struct sdp_media *m, bool offer); /* format */ int sdp_format_radd(struct sdp_media *m, const struct pl *id); struct sdp_format *sdp_format_find(const struct list *lst, const struct pl *id); /* attribute */ struct sdp_attr; int sdp_attr_add(struct list *lst, struct pl *name, struct pl *val); int sdp_attr_addv(struct list *lst, const char *name, const char *val, va_list ap); void sdp_attr_del(const struct list *lst, const char *name); const char *sdp_attr_apply(const struct list *lst, const char *name, sdp_attr_h *attrh, void *arg); int sdp_attr_print(struct re_printf *pf, const struct sdp_attr *attr); int sdp_attr_debug(struct re_printf *pf, const struct sdp_attr *attr); re-1.1.0/src/sdp/session.c000066400000000000000000000131461373627245400153450ustar00rootroot00000000000000/** * @file sdp/session.c SDP Session * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include "sdp.h" static void destructor(void *arg) { struct sdp_session *sess = arg; list_flush(&sess->lmedial); list_flush(&sess->medial); list_flush(&sess->rattrl); list_flush(&sess->lattrl); } /** * Allocate a new SDP Session * * @param sessp Pointer to allocated SDP Session object * @param laddr Local network address * * @return 0 if success, otherwise errorcode */ int sdp_session_alloc(struct sdp_session **sessp, const struct sa *laddr) { struct sdp_session *sess; int err = 0, i; if (!sessp || !laddr) return EINVAL; sess = mem_zalloc(sizeof(*sess), destructor); if (!sess) return ENOMEM; sess->laddr = *laddr; sess->id = rand_u32(); sess->ver = rand_u32() & 0x7fffffff; sess->rdir = SDP_SENDRECV; sa_init(&sess->raddr, AF_INET); for (i=0; ilbwv[i] = -1; sess->rbwv[i] = -1; } if (err) mem_deref(sess); else *sessp = sess; return err; } /** * Reset the remote side of an SDP Session * * @param sess SDP Session */ void sdp_session_rreset(struct sdp_session *sess) { int i; if (!sess) return; sa_init(&sess->raddr, AF_INET); list_flush(&sess->rattrl); sess->rdir = SDP_SENDRECV; for (i=0; irbwv[i] = -1; } /** * Set the local network address of an SDP Session * * @param sess SDP Session * @param laddr Local network address */ void sdp_session_set_laddr(struct sdp_session *sess, const struct sa *laddr) { if (!sess || !laddr) return; sess->laddr = *laddr; } /** * Set the local bandwidth of an SDP Session * * @param sess SDP Session * @param type Bandwidth type * @param bw Bandwidth value */ void sdp_session_set_lbandwidth(struct sdp_session *sess, enum sdp_bandwidth type, int32_t bw) { if (!sess || type >= SDP_BANDWIDTH_MAX) return; sess->lbwv[type] = bw; } /** * Set a local attribute of an SDP Session * * @param sess SDP Session * @param replace True to replace any existing attributes, false to append * @param name Attribute name * @param value Formatted attribute value * * @return 0 if success, otherwise errorcode */ int sdp_session_set_lattr(struct sdp_session *sess, bool replace, const char *name, const char *value, ...) { va_list ap; int err; if (!sess || !name) return EINVAL; if (replace) sdp_attr_del(&sess->lattrl, name); va_start(ap, value); err = sdp_attr_addv(&sess->lattrl, name, value, ap); va_end(ap); return err; } /** * Delete a local attribute of an SDP Session * * @param sess SDP Session * @param name Attribute name */ void sdp_session_del_lattr(struct sdp_session *sess, const char *name) { if (!sess || !name) return; sdp_attr_del(&sess->lattrl, name); } /** * Get the local bandwidth of an SDP Session * * @param sess SDP Session * @param type Bandwidth type * * @return Bandwidth value */ int32_t sdp_session_lbandwidth(const struct sdp_session *sess, enum sdp_bandwidth type) { if (!sess || type >= SDP_BANDWIDTH_MAX) return 0; return sess->lbwv[type]; } /** * Get the remote bandwidth of an SDP Session * * @param sess SDP Session * @param type Bandwidth type * * @return Bandwidth value */ int32_t sdp_session_rbandwidth(const struct sdp_session *sess, enum sdp_bandwidth type) { if (!sess || type >= SDP_BANDWIDTH_MAX) return 0; return sess->rbwv[type]; } /** * Get a remote attribute of an SDP Session * * @param sess SDP Session * @param name Attribute name * * @return Attribute value if exist, NULL if not exist */ const char *sdp_session_rattr(const struct sdp_session *sess, const char *name) { if (!sess || !name) return NULL; return sdp_attr_apply(&sess->rattrl, name, NULL, NULL); } /** * Apply a function handler of all matching remote attributes * * @param sess SDP Session * @param name Attribute name * @param attrh Attribute handler * @param arg Handler argument * * @return Attribute value if match */ const char *sdp_session_rattr_apply(const struct sdp_session *sess, const char *name, sdp_attr_h *attrh, void *arg) { if (!sess) return NULL; return sdp_attr_apply(&sess->rattrl, name, attrh, arg); } /** * Get the list of media-lines from an SDP Session * * @param sess SDP Session * @param local True for local, False for remote * * @return List of media-lines */ const struct list *sdp_session_medial(const struct sdp_session *sess, bool local) { if (!sess) return NULL; return local ? &sess->lmedial : &sess->medial; } /** * Print SDP Session debug information * * @param pf Print function for output * @param sess SDP Session * * @return 0 if success, otherwise errorcode */ int sdp_session_debug(struct re_printf *pf, const struct sdp_session *sess) { struct le *le; int err; if (!sess) return 0; err = re_hprintf(pf, "SDP session\n"); err |= re_hprintf(pf, " local attributes:\n"); for (le=sess->lattrl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data); err |= re_hprintf(pf, " remote attributes:\n"); for (le=sess->rattrl.head; le; le=le->next) err |= re_hprintf(pf, " %H\n", sdp_attr_debug, le->data); err |= re_hprintf(pf, "session media:\n"); for (le=sess->medial.head; le; le=le->next) err |= sdp_media_debug(pf, le->data); err |= re_hprintf(pf, "local media:\n"); for (le=sess->lmedial.head; le; le=le->next) err |= sdp_media_debug(pf, le->data); return err; } re-1.1.0/src/sdp/str.c000066400000000000000000000032751373627245400144740ustar00rootroot00000000000000/** * @file sdp/str.c SDP strings * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include const char sdp_attr_fmtp[] = "fmtp"; /**< fmtp */ const char sdp_attr_maxptime[] = "maxptime"; /**< maxptime */ const char sdp_attr_ptime[] = "ptime"; /**< ptime */ const char sdp_attr_rtcp[] = "rtcp"; /**< rtcp */ const char sdp_attr_rtpmap[] = "rtpmap"; /**< rtpmap */ const char sdp_media_audio[] = "audio"; /**< Media type 'audio' */ const char sdp_media_video[] = "video"; /**< Media type 'video' */ const char sdp_media_text[] = "text"; /**< Media type 'text' */ const char sdp_proto_rtpavp[] = "RTP/AVP"; /**< RTP Profile */ const char sdp_proto_rtpsavp[] = "RTP/SAVP"; /**< Secure RTP Profile */ /** * Get the SDP media direction name * * @param dir Media direction * * @return Name of media direction */ const char *sdp_dir_name(enum sdp_dir dir) { switch (dir) { case SDP_INACTIVE: return "inactive"; case SDP_RECVONLY: return "recvonly"; case SDP_SENDONLY: return "sendonly"; case SDP_SENDRECV: return "sendrecv"; default: return "??"; } } /** * Get the SDP bandwidth name * * @param type Bandwidth type * * @return Bandwidth name */ const char *sdp_bandwidth_name(enum sdp_bandwidth type) { switch (type) { case SDP_BANDWIDTH_CT: return "CT"; case SDP_BANDWIDTH_AS: return "AS"; case SDP_BANDWIDTH_RS: return "RS"; case SDP_BANDWIDTH_RR: return "RR"; case SDP_BANDWIDTH_TIAS: return "TIAS"; default: return "??"; } } re-1.1.0/src/sdp/util.c000066400000000000000000000021021373627245400146250ustar00rootroot00000000000000/** * @file sdp/util.c SDP utility functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include /** * Decode RTP Header Extension SDP attribute value * * @param ext Extension-map object * @param val SDP attribute value * * @return 0 for success, otherwise errorcode */ int sdp_extmap_decode(struct sdp_extmap *ext, const char *val) { struct pl id, dir; if (!ext || !val) return EINVAL; if (re_regex(val, strlen(val), "[0-9]+[/]*[a-z]* [^ ]+[ ]*[^ ]*", &id, NULL, &dir, &ext->name, NULL, &ext->attrs)) return EBADMSG; ext->dir_set = false; ext->dir = SDP_SENDRECV; if (pl_isset(&dir)) { ext->dir_set = true; if (!pl_strcmp(&dir, "sendonly")) ext->dir = SDP_SENDONLY; else if (!pl_strcmp(&dir, "sendrecv")) ext->dir = SDP_SENDRECV; else if (!pl_strcmp(&dir, "recvonly")) ext->dir = SDP_RECVONLY; else if (!pl_strcmp(&dir, "inactive")) ext->dir = SDP_INACTIVE; else ext->dir_set = false; } ext->id = pl_u32(&id); return 0; } re-1.1.0/src/sha/000077500000000000000000000000001373627245400134765ustar00rootroot00000000000000re-1.1.0/src/sha/mod.mk000066400000000000000000000001411373627245400146020ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # ifeq ($(USE_OPENSSL),) SRCS += sha/sha1.c endif re-1.1.0/src/sha/sha1.c000066400000000000000000000177451373627245400145140ustar00rootroot00000000000000/** * @file sha1.c SHA-1 in C */ /* By Steve Reid 100% Public Domain ----------------- Modified 7/98 By James H. Brown Still 100% Public Domain Corrected a problem which generated improper hash values on 16 bit machines Routine SHA1Update changed from void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int len) to void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned long len) The 'len' parameter was declared an int which works fine on 32 bit machines. However, on 16 bit machines an int is too small for the shifts being done against it. This caused the hash function to generate incorrect values if len was greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). Since the file IO in main() reads 16K at a time, any file 8K or larger would be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million "a"s). I also changed the declaration of variables i & j in SHA1Update to unsigned long from unsigned int for the same reason. These changes should make no difference to any 32 bit implementations since an int and a long are the same size in those environments. -- I also corrected a few compiler warnings generated by Borland C. 1. Added #include for exit() prototype 2. Removed unused variable 'j' in SHA1Final 3. Changed exit(0) to return(0) at end of main. ALL changes I made can be located by searching for comments containing 'JHB' ----------------- Modified 8/98 By Steve Reid Still 100% public domain 1- Removed #include and used return() instead of exit() 2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) 3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net ----------------- Modified 4/01 By Saul Kravitz Still 100% PD Modified to run on Compaq Alpha hardware. ----------------- Modified 07/2002 By Ralph Giles Still 100% public domain modified for use with stdint types, autoconf code cleanup, removed attribution comments switched SHA1Final() argument order for consistency use SHA1_ prefix for public api move public api to sha1.h */ /* Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ #define SHA1HANDSOFF 1 #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]); #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) #if defined (BYTE_ORDER) && defined(BIG_ENDIAN) && (BYTE_ORDER == BIG_ENDIAN) #define WORDS_BIGENDIAN 1 #endif #ifdef _BIG_ENDIAN #define WORDS_BIGENDIAN 1 #endif /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ /* FIXME: can we do this in an endian-proof way? */ #ifdef WORDS_BIGENDIAN #define blk0(i) block->l[i] #else #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xff00ff00) \ |(rol(block->l[i],8)&0x00ff00ff)) #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk0(i)+0x5a827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) \ z+=((w&(x^y))^y)+blk(i)+0x5a827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0x6ed9eba1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) \ z+=(((w|x)&y)|(w&x))+blk(i)+0x8f1bbcdc+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) \ z+=(w^x^y)+blk(i)+0xca62c1d6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1_Transform(uint32_t state[5], const uint8_t buffer[64]) { uint32_t a, b, c, d, e; typedef union { uint8_t c[64]; uint32_t l[16]; } CHAR64LONG16; CHAR64LONG16* block; #ifdef SHA1HANDSOFF CHAR64LONG16 workspace; block = &workspace; memcpy(block, buffer, 64); #else block = (CHAR64LONG16*)buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2);R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6);R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10);R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14);R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18);R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22);R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26);R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30);R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34);R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38);R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42);R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46);R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50);R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54);R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58);R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62);R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66);R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70);R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74);R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78);R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; } /** * Initialize new context * * @param context SHA1-Context */ void SHA1_Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xefcdab89; context->state[2] = 0x98badcfe; context->state[3] = 0x10325476; context->state[4] = 0xc3d2e1f0; context->count[0] = context->count[1] = 0; } /** * Run your data through this * * @param context SHA1-Context * @param p Buffer to run SHA1 on * @param len Number of bytes */ void SHA1_Update(SHA1_CTX* context, const void *p, size_t len) { const uint8_t* data = p; size_t i, j; j = (context->count[0] >> 3) & 63; if ((context->count[0] += (uint32_t)(len << 3)) < (len << 3)) context->count[1]++; context->count[1] += (uint32_t)(len >> 29); if ((j + len) > 63) { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1_Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1_Transform(context->state, data + i); } j = 0; } else i = 0; memcpy(&context->buffer[j], &data[i], len - i); } /** * Add padding and return the message digest * * @param digest Generated message digest * @param context SHA1-Context */ void SHA1_Final(uint8_t digest[SHA1_DIGEST_SIZE], SHA1_CTX* context) { uint32_t i; uint8_t finalcount[8]; for (i = 0; i < 8; i++) { finalcount[i] = (uint8_t)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); } SHA1_Update(context, (uint8_t *)"\200", 1); while ((context->count[0] & 504) != 448) { SHA1_Update(context, (uint8_t *)"\0", 1); } SHA1_Update(context, finalcount, 8); /* Should cause SHA1_Transform */ for (i = 0; i < SHA1_DIGEST_SIZE; i++) { digest[i] = (uint8_t) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ i = 0; memset(context->buffer, 0, 64); memset(context->state, 0, 20); memset(context->count, 0, 8); memset(finalcount, 0, 8); /* SWR */ #ifdef SHA1HANDSOFF /* make SHA1Transform overwrite its own static vars */ SHA1_Transform(context->state, context->buffer); #endif } re-1.1.0/src/sip/000077500000000000000000000000001373627245400135165ustar00rootroot00000000000000re-1.1.0/src/sip/addr.c000066400000000000000000000020751373627245400146000ustar00rootroot00000000000000/** * @file sip/addr.c SIP Address decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include /** * Decode a pointer-length string into a SIP Address object * * @param addr SIP Address object * @param pl Pointer-length string * * @return 0 for success, otherwise errorcode */ int sip_addr_decode(struct sip_addr *addr, const struct pl *pl) { int err; if (!addr || !pl) return EINVAL; memset(addr, 0, sizeof(*addr)); if (0 == re_regex(pl->p, pl->l, "[~ \t\r\n<]*[ \t\r\n]*<[^>]+>[^]*", &addr->dname, NULL, &addr->auri, &addr->params)) { if (!addr->dname.l) addr->dname.p = NULL; if (!addr->params.l) addr->params.p = NULL; } else { memset(addr, 0, sizeof(*addr)); if (re_regex(pl->p, pl->l, "[^;]+[^]*", &addr->auri, &addr->params)) return EBADMSG; } err = uri_decode(&addr->uri, &addr->auri); if (err) memset(addr, 0, sizeof(*addr)); return err; } re-1.1.0/src/sip/auth.c000066400000000000000000000140771373627245400146340ustar00rootroot00000000000000/** * @file sip/auth.c SIP Authentication * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" struct sip_auth { struct list realml; sip_auth_h *authh; void *arg; bool ref; int err; }; struct realm { struct le le; char *realm; char *nonce; char *qop; char *opaque; char *user; char *pass; uint32_t nc; enum sip_hdrid hdr; }; static int dummy_handler(char **user, char **pass, const char *rlm, void *arg) { (void)user; (void)pass; (void)rlm; (void)arg; return EAUTH; } static void realm_destructor(void *arg) { struct realm *realm = arg; list_unlink(&realm->le); mem_deref(realm->realm); mem_deref(realm->nonce); mem_deref(realm->qop); mem_deref(realm->opaque); mem_deref(realm->user); mem_deref(realm->pass); } static void auth_destructor(void *arg) { struct sip_auth *auth = arg; if (auth->ref) mem_deref(auth->arg); list_flush(&auth->realml); } static int mkdigest(uint8_t *digest, const struct realm *realm, const char *met, const char *uri, uint64_t cnonce) { uint8_t ha1[MD5_SIZE], ha2[MD5_SIZE]; int err; err = md5_printf(ha1, "%s:%s:%s", realm->user, realm->realm, realm->pass); if (err) return err; err = md5_printf(ha2, "%s:%s", met, uri); if (err) return err; if (realm->qop) return md5_printf(digest, "%w:%s:%08x:%016llx:auth:%w", ha1, sizeof(ha1), realm->nonce, realm->nc, cnonce, ha2, sizeof(ha2)); else return md5_printf(digest, "%w:%s:%w", ha1, sizeof(ha1), realm->nonce, ha2, sizeof(ha2)); } static bool cmp_handler(struct le *le, void *arg) { struct realm *realm = le->data; struct pl *chrealm = arg; /* handle multiple authenticate headers with equal realm value */ if (realm->nc == 1) return false; return 0 == pl_strcasecmp(chrealm, realm->realm); } static bool auth_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { struct httpauth_digest_chall ch; struct sip_auth *auth = arg; struct realm *realm = NULL; int err; (void)msg; if (httpauth_digest_challenge_decode(&ch, &hdr->val)) { err = EBADMSG; goto out; } if (pl_isset(&ch.algorithm) && pl_strcasecmp(&ch.algorithm, "md5")) { err = ENOSYS; goto out; } realm = list_ledata(list_apply(&auth->realml, true, cmp_handler, &ch.realm)); if (!realm) { realm = mem_zalloc(sizeof(*realm), realm_destructor); if (!realm) { err = ENOMEM; goto out; } list_append(&auth->realml, &realm->le, realm); err = pl_strdup(&realm->realm, &ch.realm); if (err) goto out; err = auth->authh(&realm->user, &realm->pass, realm->realm, auth->arg); if (err) goto out; } else { if (!pl_isset(&ch.stale) || pl_strcasecmp(&ch.stale, "true")) { err = EAUTH; goto out; } realm->nonce = mem_deref(realm->nonce); realm->qop = mem_deref(realm->qop); realm->opaque = mem_deref(realm->opaque); } realm->hdr = hdr->id; realm->nc = 1; err = pl_strdup(&realm->nonce, &ch.nonce); if (pl_isset(&ch.qop)) err |= pl_strdup(&realm->qop, &ch.qop); if (pl_isset(&ch.opaque)) err |= pl_strdup(&realm->opaque, &ch.opaque); out: if (err) { mem_deref(realm); auth->err = err; return true; } return false; } /** * Update a SIP authentication state from a SIP message * * @param auth SIP Authentication state * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_auth_authenticate(struct sip_auth *auth, const struct sip_msg *msg) { if (!auth || !msg) return EINVAL; if (sip_msg_hdr_apply(msg, true, SIP_HDR_WWW_AUTHENTICATE, auth_handler, auth)) return auth->err; if (sip_msg_hdr_apply(msg, true, SIP_HDR_PROXY_AUTHENTICATE, auth_handler, auth)) return auth->err; return 0; } int sip_auth_encode(struct mbuf *mb, struct sip_auth *auth, const char *met, const char *uri) { struct le *le; int err = 0; if (!mb || !auth || !met || !uri) return EINVAL; for (le = auth->realml.head; le; le = le->next) { const uint64_t cnonce = rand_u64(); struct realm *realm = le->data; uint8_t digest[MD5_SIZE]; err = mkdigest(digest, realm, met, uri, cnonce); if (err) break; switch (realm->hdr) { case SIP_HDR_WWW_AUTHENTICATE: err = mbuf_write_str(mb, "Authorization: "); break; case SIP_HDR_PROXY_AUTHENTICATE: err = mbuf_write_str(mb, "Proxy-Authorization: "); break; default: continue; } err |= mbuf_printf(mb, "Digest username=\"%s\"", realm->user); err |= mbuf_printf(mb, ", realm=\"%s\"", realm->realm); err |= mbuf_printf(mb, ", nonce=\"%s\"", realm->nonce); err |= mbuf_printf(mb, ", uri=\"%s\"", uri); err |= mbuf_printf(mb, ", response=\"%w\"", digest, sizeof(digest)); if (realm->opaque) err |= mbuf_printf(mb, ", opaque=\"%s\"", realm->opaque); if (realm->qop) { err |= mbuf_printf(mb, ", cnonce=\"%016llx\"", cnonce); err |= mbuf_write_str(mb, ", qop=auth"); err |= mbuf_printf(mb, ", nc=%08x", realm->nc); } ++realm->nc; err |= mbuf_write_str(mb, "\r\n"); if (err) break; } return err; } /** * Allocate a SIP authentication state * * @param authp Pointer to allocated SIP authentication state * @param authh Authentication handler * @param arg Handler argument * @param ref True to mem_ref() argument * * @return 0 if success, otherwise errorcode */ int sip_auth_alloc(struct sip_auth **authp, sip_auth_h *authh, void *arg, bool ref) { struct sip_auth *auth; if (!authp) return EINVAL; auth = mem_zalloc(sizeof(*auth), auth_destructor); if (!auth) return ENOMEM; auth->authh = authh ? authh : dummy_handler; auth->arg = ref ? mem_ref(arg) : arg; auth->ref = ref; *authp = auth; return 0; } /** * Reset a SIP authentication state * * @param auth SIP Authentication state */ void sip_auth_reset(struct sip_auth *auth) { if (!auth) return; list_flush(&auth->realml); } re-1.1.0/src/sip/contact.c000066400000000000000000000022351373627245400153170ustar00rootroot00000000000000/** * @file sip/contact.c SIP contact functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include /** * Set contact parameters * * @param contact SIP Contact object * @param uri Username or URI * @param addr IP-address and port * @param tp SIP Transport */ void sip_contact_set(struct sip_contact *contact, const char *uri, const struct sa *addr, enum sip_transp tp) { if (!contact) return; contact->uri = uri; contact->addr = addr; contact->tp = tp; } /** * Print contact header * * @param pf Print function * @param contact SIP Contact object * * @return 0 for success, otherwise errorcode */ int sip_contact_print(struct re_printf *pf, const struct sip_contact *contact) { if (!contact) return 0; if (contact->uri && strchr(contact->uri, ':')) return re_hprintf(pf, "Contact: <%s>\r\n", contact->uri); else return re_hprintf(pf, "Contact: \r\n", contact->uri, contact->addr, sip_transp_param(contact->tp)); } re-1.1.0/src/sip/cseq.c000066400000000000000000000013171373627245400146170ustar00rootroot00000000000000/** * @file cseq.c SIP CSeq decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include /** * Decode a pointer-length string into a SIP CSeq header * * @param cseq SIP CSeq header * @param pl Pointer-length string * * @return 0 for success, otherwise errorcode */ int sip_cseq_decode(struct sip_cseq *cseq, const struct pl *pl) { struct pl num; int err; if (!cseq || !pl) return EINVAL; err = re_regex(pl->p, pl->l, "[0-9]+[ \t\r\n]+[^ \t\r\n]+", &num, NULL, &cseq->met); if (err) return err; cseq->num = pl_u32(&num); return 0; } re-1.1.0/src/sip/ctrans.c000066400000000000000000000173531373627245400151650ustar00rootroot00000000000000/** * @file sip/ctrans.c SIP Client Transaction * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum state { TRYING, CALLING, PROCEEDING, COMPLETED, }; enum { COMPLETE_WAIT = 32000, }; struct sip_ctrans { struct le he; struct sa dst; struct tmr tmr; struct tmr tmre; struct sip *sip; struct mbuf *mb; struct mbuf *mb_ack; struct sip_msg *req; struct sip_connqent *qent; char *met; char *branch; sip_resp_h *resph; void *arg; enum sip_transp tp; enum state state; uint32_t txc; bool invite; }; static bool route_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { (void)msg; return 0 != mbuf_printf(arg, "Route: %r\r\n", &hdr->val); } static int request_copy(struct mbuf **mbp, struct sip_ctrans *ct, const char *met, const struct sip_msg *resp) { struct mbuf *mb; int err; if (!ct->req) { err = sip_msg_decode(&ct->req, ct->mb); if (err) return err; } mb = mbuf_alloc(1024); if (!mb) return ENOMEM; err = mbuf_printf(mb, "%s %r SIP/2.0\r\n", met, &ct->req->ruri); err |= mbuf_printf(mb, "Via: %r\r\n", &ct->req->via.val); err |= mbuf_write_str(mb, "Max-Forwards: 70\r\n"); err |= sip_msg_hdr_apply(ct->req, true, SIP_HDR_ROUTE, route_handler, mb) ? ENOMEM : 0; err |= mbuf_printf(mb, "To: %r\r\n", resp ? &resp->to.val : &ct->req->to.val); err |= mbuf_printf(mb, "From: %r\r\n", &ct->req->from.val); err |= mbuf_printf(mb, "Call-ID: %r\r\n", &ct->req->callid); err |= mbuf_printf(mb, "CSeq: %u %s\r\n", ct->req->cseq.num, met); if (ct->sip->software) err |= mbuf_printf(mb, "User-Agent: %s\r\n",ct->sip->software); err |= mbuf_write_str(mb, "Content-Length: 0\r\n\r\n"); mb->pos = 0; if (err) mem_deref(mb); else *mbp = mb; return err; } static void destructor(void *arg) { struct sip_ctrans *ct = arg; hash_unlink(&ct->he); tmr_cancel(&ct->tmr); tmr_cancel(&ct->tmre); mem_deref(ct->met); mem_deref(ct->branch); mem_deref(ct->qent); mem_deref(ct->req); mem_deref(ct->mb); mem_deref(ct->mb_ack); } static bool cmp_handler(struct le *le, void *arg) { struct sip_ctrans *ct = le->data; const struct sip_msg *msg = arg; if (pl_strcmp(&msg->via.branch, ct->branch)) return false; if (pl_strcmp(&msg->cseq.met, ct->met)) return false; return true; } static void dummy_handler(int err, const struct sip_msg *msg, void *arg) { (void)err; (void)msg; (void)arg; } static void terminate(struct sip_ctrans *ct, int err) { switch (ct->state) { case TRYING: case CALLING: case PROCEEDING: ct->resph(err, NULL, ct->arg); break; default: break; } } static void transport_handler(int err, void *arg) { struct sip_ctrans *ct = arg; terminate(ct, err); mem_deref(ct); } static void tmr_handler(void *arg) { struct sip_ctrans *ct = arg; terminate(ct, ETIMEDOUT); mem_deref(ct); } static void retransmit_handler(void *arg) { struct sip_ctrans *ct = arg; uint32_t timeout; int err; ct->txc++; switch (ct->state) { case TRYING: timeout = MIN(SIP_T1<txc, SIP_T2); break; case CALLING: timeout = SIP_T1<txc; break; case PROCEEDING: timeout = SIP_T2; break; default: return; } tmr_start(&ct->tmre, timeout, retransmit_handler, ct); err = sip_transp_send(&ct->qent, ct->sip, NULL, ct->tp, &ct->dst, ct->mb, transport_handler, ct); if (err) { terminate(ct, err); mem_deref(ct); } } static void invite_response(struct sip_ctrans *ct, const struct sip_msg *msg) { switch (ct->state) { case CALLING: tmr_cancel(&ct->tmr); tmr_cancel(&ct->tmre); /*@fallthrough@*/ case PROCEEDING: if (msg->scode < 200) { ct->state = PROCEEDING; ct->resph(0, msg, ct->arg); } else if (msg->scode < 300) { ct->resph(0, msg, ct->arg); mem_deref(ct); } else { ct->state = COMPLETED; (void)request_copy(&ct->mb_ack, ct, "ACK", msg); (void)sip_send(ct->sip, NULL, ct->tp, &ct->dst, ct->mb_ack); ct->resph(0, msg, ct->arg); if (sip_transp_reliable(ct->tp)) { mem_deref(ct); break; } tmr_start(&ct->tmr, COMPLETE_WAIT, tmr_handler, ct); } break; case COMPLETED: if (msg->scode < 300) break; (void)sip_send(ct->sip, NULL, ct->tp, &ct->dst, ct->mb_ack); break; default: break; } } static bool response_handler(const struct sip_msg *msg, void *arg) { struct sip_ctrans *ct; struct sip *sip = arg; ct = list_ledata(hash_lookup(sip->ht_ctrans, hash_joaat_pl(&msg->via.branch), cmp_handler, (void *)msg)); if (!ct) return false; if (ct->invite) { invite_response(ct, msg); return true; } switch (ct->state) { case TRYING: case PROCEEDING: if (msg->scode < 200) { ct->state = PROCEEDING; ct->resph(0, msg, ct->arg); } else { ct->state = COMPLETED; ct->resph(0, msg, ct->arg); if (sip_transp_reliable(ct->tp)) { mem_deref(ct); break; } tmr_start(&ct->tmr, SIP_T4, tmr_handler, ct); tmr_cancel(&ct->tmre); } break; default: break; } return true; } int sip_ctrans_request(struct sip_ctrans **ctp, struct sip *sip, enum sip_transp tp, const struct sa *dst, char *met, char *branch, struct mbuf *mb, sip_resp_h *resph, void *arg) { struct sip_ctrans *ct; int err; if (!sip || !dst || !met || !branch || !mb) return EINVAL; ct = mem_zalloc(sizeof(*ct), destructor); if (!ct) return ENOMEM; hash_append(sip->ht_ctrans, hash_joaat_str(branch), &ct->he, ct); ct->invite = !strcmp(met, "INVITE"); ct->branch = mem_ref(branch); ct->met = mem_ref(met); ct->mb = mem_ref(mb); ct->dst = *dst; ct->tp = tp; ct->sip = sip; ct->state = ct->invite ? CALLING : TRYING; ct->resph = resph ? resph : dummy_handler; ct->arg = arg; err = sip_transp_send(&ct->qent, sip, NULL, tp, dst, mb, transport_handler, ct); if (err) goto out; tmr_start(&ct->tmr, 64 * SIP_T1, tmr_handler, ct); if (!sip_transp_reliable(ct->tp)) tmr_start(&ct->tmre, SIP_T1, retransmit_handler, ct); out: if (err) mem_deref(ct); else if (ctp) *ctp = ct; return err; } int sip_ctrans_cancel(struct sip_ctrans *ct) { struct mbuf *mb = NULL; char *cancel = NULL; int err; if (!ct) return EINVAL; if (!ct->invite) return 0; switch (ct->state) { case PROCEEDING: tmr_start(&ct->tmr, 64 * SIP_T1, tmr_handler, ct); break; default: return EPROTO; } err = str_dup(&cancel, "CANCEL"); if (err) goto out; err = request_copy(&mb, ct, cancel, NULL); if (err) goto out; err = sip_ctrans_request(NULL, ct->sip, ct->tp, &ct->dst, cancel, ct->branch, mb, NULL, NULL); if (err) goto out; out: mem_deref(cancel); mem_deref(mb); return err; } int sip_ctrans_init(struct sip *sip, uint32_t sz) { int err; err = sip_listen(NULL, sip, false, response_handler, sip); if (err) return err; return hash_alloc(&sip->ht_ctrans, sz); } static const char *statename(enum state state) { switch (state) { case TRYING: return "TRYING"; case CALLING: return "CALLING"; case PROCEEDING: return "PROCEEDING"; case COMPLETED: return "COMPLETED"; default: return "???"; } } static bool debug_handler(struct le *le, void *arg) { struct sip_ctrans *ct = le->data; struct re_printf *pf = arg; (void)re_hprintf(pf, " %-10s %-10s %2llus (%s)\n", ct->met, statename(ct->state), tmr_get_expire(&ct->tmr)/1000, ct->branch); return false; } int sip_ctrans_debug(struct re_printf *pf, const struct sip *sip) { int err; err = re_hprintf(pf, "client transactions:\n"); hash_apply(sip->ht_ctrans, debug_handler, pf); return err; } re-1.1.0/src/sip/dialog.c000066400000000000000000000276701373627245400151350ustar00rootroot00000000000000/** * @file dialog.c SIP Dialog * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum { ROUTE_OFFSET = 7, X64_STRSIZE = 17, }; struct sip_dialog { struct uri route; struct mbuf *mb; char *callid; char *ltag; char *rtag; char *uri; uint32_t hash; uint32_t lseq; uint32_t rseq; size_t cpos; }; struct route_enc { struct mbuf *mb; size_t end; }; static int x64_strdup(char **strp, uint64_t val) { char *str; str = mem_alloc(X64_STRSIZE, NULL); if (!str) return ENOMEM; (void)re_snprintf(str, X64_STRSIZE, "%016llx", val); *strp = str; return 0; } static void destructor(void *arg) { struct sip_dialog *dlg = arg; mem_deref(dlg->callid); mem_deref(dlg->ltag); mem_deref(dlg->rtag); mem_deref(dlg->uri); mem_deref(dlg->mb); } /** * Allocate a SIP Dialog * * @param dlgp Pointer to allocated SIP Dialog * @param uri Target URI * @param to_uri To URI * @param from_name From displayname (optional) * @param from_uri From URI * @param routev Route vector * @param routec Route count * * @return 0 if success, otherwise errorcode */ int sip_dialog_alloc(struct sip_dialog **dlgp, const char *uri, const char *to_uri, const char *from_name, const char *from_uri, const char *routev[], uint32_t routec) { const uint64_t ltag = rand_u64(); struct sip_dialog *dlg; struct sip_addr addr; size_t rend = 0; struct pl pl; uint32_t i; int err; if (!dlgp || !uri || !to_uri || !from_uri) return EINVAL; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->hash = hash_fast_str(from_uri); dlg->lseq = rand_u16(); err = str_dup(&dlg->uri, uri); if (err) goto out; err = x64_strdup(&dlg->callid, rand_u64()); if (err) goto out; err = x64_strdup(&dlg->ltag, ltag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } for (i=0; imb, "Route: <%s;lr>\r\n", routev[i]); if (i == 0) rend = dlg->mb->pos - 2; } err |= mbuf_printf(dlg->mb, "To: <%s>\r\n", to_uri); dlg->cpos = dlg->mb->pos; err |= mbuf_printf(dlg->mb, "From: %s%s%s<%s>;tag=%016llx\r\n", from_name ? "\"" : "", from_name, from_name ? "\" " : "", from_uri, ltag); if (err) goto out; dlg->mb->pos = 0; if (rend) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = rend - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; } static bool record_route_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { struct route_enc *renc = arg; (void)msg; if (mbuf_printf(renc->mb, "Route: %r\r\n", &hdr->val)) return true; if (!renc->end) renc->end = renc->mb->pos - 2; return false; } /** * Accept and create a SIP Dialog from an incoming SIP Message * * @param dlgp Pointer to allocated SIP Dialog * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_dialog_accept(struct sip_dialog **dlgp, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_dialog *dlg; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlgp || !msg || !msg->req) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact || !msg->callid.p) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->hash = rand_u32(); dlg->lseq = rand_u16(); dlg->rseq = msg->cseq.num; err = pl_strdup(&dlg->uri, &addr.auri); if (err) goto out; err = pl_strdup(&dlg->callid, &msg->callid); if (err) goto out; err = x64_strdup(&dlg->ltag, msg->tag); if (err) goto out; err = pl_strdup(&dlg->rtag, &msg->from.tag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } renc.mb = dlg->mb; renc.end = 0; err |= sip_msg_hdr_apply(msg, true, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(dlg->mb, "To: %r\r\n", &msg->from.val); err |= mbuf_printf(dlg->mb, "From: %r;tag=%016llx\r\n", &msg->to.val, msg->tag); if (err) goto out; dlg->mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; } /** * Initialize a SIP Dialog from an incoming SIP Message * * @param dlg SIP Dialog to initialize * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_dialog_create(struct sip_dialog *dlg, const struct sip_msg *msg) { char *uri = NULL, *rtag = NULL; const struct sip_hdr *contact; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlg || dlg->rtag || !dlg->cpos || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; renc.mb = mbuf_alloc(512); if (!renc.mb) return ENOMEM; err = pl_strdup(&uri, &addr.auri); if (err) goto out; err = pl_strdup(&rtag, msg->req ? &msg->from.tag : &msg->to.tag); if (err) goto out; renc.end = 0; err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(renc.mb, "To: %r\r\n", msg->req ? &msg->from.val : &msg->to.val); dlg->mb->pos = dlg->cpos; err |= mbuf_write_mem(renc.mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb)); dlg->mb->pos = 0; if (err) goto out; renc.mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(renc.mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); if (err) goto out; dlg->route = addr.uri; } else { struct uri tmp; pl_set_str(&pl, uri); err = uri_decode(&tmp, &pl); if (err) goto out; dlg->route = tmp; } mem_deref(dlg->mb); mem_deref(dlg->uri); dlg->mb = mem_ref(renc.mb); dlg->rtag = mem_ref(rtag); dlg->uri = mem_ref(uri); dlg->rseq = msg->req ? msg->cseq.num : 0; dlg->cpos = 0; out: mem_deref(renc.mb); mem_deref(rtag); mem_deref(uri); return err; } /** * Fork a SIP Dialog from an incoming SIP Message * * @param dlgp Pointer to allocated SIP Dialog * @param odlg Original SIP Dialog * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_dialog_fork(struct sip_dialog **dlgp, struct sip_dialog *odlg, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_dialog *dlg; struct route_enc renc; struct sip_addr addr; struct pl pl; int err; if (!dlgp || !odlg || !odlg->cpos || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact || !msg->callid.p) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; dlg = mem_zalloc(sizeof(*dlg), destructor); if (!dlg) return ENOMEM; dlg->callid = mem_ref(odlg->callid); dlg->ltag = mem_ref(odlg->ltag); dlg->hash = odlg->hash; dlg->lseq = odlg->lseq; dlg->rseq = msg->req ? msg->cseq.num : 0; err = pl_strdup(&dlg->uri, &addr.auri); if (err) goto out; err = pl_strdup(&dlg->rtag, msg->req ? &msg->from.tag : &msg->to.tag); if (err) goto out; dlg->mb = mbuf_alloc(512); if (!dlg->mb) { err = ENOMEM; goto out; } renc.mb = dlg->mb; renc.end = 0; err |= sip_msg_hdr_apply(msg, msg->req, SIP_HDR_RECORD_ROUTE, record_route_handler, &renc) ? ENOMEM : 0; err |= mbuf_printf(dlg->mb, "To: %r\r\n", msg->req ? &msg->from.val : &msg->to.val); odlg->mb->pos = odlg->cpos; err |= mbuf_write_mem(dlg->mb, mbuf_buf(odlg->mb), mbuf_get_left(odlg->mb)); odlg->mb->pos = 0; if (err) goto out; dlg->mb->pos = 0; if (renc.end) { pl.p = (const char *)mbuf_buf(dlg->mb) + ROUTE_OFFSET; pl.l = renc.end - ROUTE_OFFSET; err = sip_addr_decode(&addr, &pl); dlg->route = addr.uri; } else { pl_set_str(&pl, dlg->uri); err = uri_decode(&dlg->route, &pl); } out: if (err) mem_deref(dlg); else *dlgp = dlg; return err; } /** * Update an existing SIP Dialog from a SIP Message * * @param dlg SIP Dialog to update * @param msg SIP Message * * @return 0 if success, otherwise errorcode */ int sip_dialog_update(struct sip_dialog *dlg, const struct sip_msg *msg) { const struct sip_hdr *contact; struct sip_addr addr; char *uri; int err; if (!dlg || !msg) return EINVAL; contact = sip_msg_hdr(msg, SIP_HDR_CONTACT); if (!contact) return EBADMSG; if (sip_addr_decode(&addr, &contact->val)) return EBADMSG; err = pl_strdup(&uri, &addr.auri); if (err) return err; if (dlg->route.scheme.p == dlg->uri) { struct uri tmp; struct pl pl; pl_set_str(&pl, uri); err = uri_decode(&tmp, &pl); if (err) goto out; dlg->route = tmp; } mem_deref(dlg->uri); dlg->uri = mem_ref(uri); out: mem_deref(uri); return err; } /** * Check if a remote sequence number is valid * * @param dlg SIP Dialog * @param msg SIP Message * * @return True if valid, False if invalid */ bool sip_dialog_rseq_valid(struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg || !msg->req) return false; if (msg->cseq.num < dlg->rseq) return false; dlg->rseq = msg->cseq.num; return true; } int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq, const char *met) { int err = 0; if (!mb || !dlg || !met) return EINVAL; err |= mbuf_write_mem(mb, mbuf_buf(dlg->mb), mbuf_get_left(dlg->mb)); err |= mbuf_printf(mb, "Call-ID: %s\r\n", dlg->callid); err |= mbuf_printf(mb, "CSeq: %u %s\r\n", strcmp(met, "ACK") ? dlg->lseq++ : cseq, met); return err; } const char *sip_dialog_uri(const struct sip_dialog *dlg) { return dlg ? dlg->uri : NULL; } const struct uri *sip_dialog_route(const struct sip_dialog *dlg) { return dlg ? &dlg->route : NULL; } uint32_t sip_dialog_hash(const struct sip_dialog *dlg) { return dlg ? dlg->hash : 0; } /** * Get the Call-ID from a SIP Dialog * * @param dlg SIP Dialog * * @return Call-ID string */ const char *sip_dialog_callid(const struct sip_dialog *dlg) { return dlg ? dlg->callid : NULL; } /** * Get the local sequence number from a SIP Dialog * * @param dlg SIP Dialog * * @return Local sequence number */ uint32_t sip_dialog_lseq(const struct sip_dialog *dlg) { return dlg ? dlg->lseq : 0; } /** * Check if a SIP Dialog is established * * @param dlg SIP Dialog * * @return True if established, False if not */ bool sip_dialog_established(const struct sip_dialog *dlg) { return dlg && dlg->rtag; } /** * Compare a SIP Dialog against a SIP Message * * @param dlg SIP Dialog * @param msg SIP Message * * @return True if match, False if no match */ bool sip_dialog_cmp(const struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg) return false; if (pl_strcmp(&msg->callid, dlg->callid)) return false; if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag)) return false; if (pl_strcmp(msg->req ? &msg->from.tag : &msg->to.tag, dlg->rtag)) return false; return true; } /** * Compare a half SIP Dialog against a SIP Message * * @param dlg SIP Dialog * @param msg SIP Message * * @return True if match, False if no match */ bool sip_dialog_cmp_half(const struct sip_dialog *dlg, const struct sip_msg *msg) { if (!dlg || !msg) return false; if (pl_strcmp(&msg->callid, dlg->callid)) return false; if (pl_strcmp(msg->req ? &msg->to.tag : &msg->from.tag, dlg->ltag)) return false; return true; } re-1.1.0/src/sip/keepalive.c000066400000000000000000000036741373627245400156410ustar00rootroot00000000000000/** * @file sip/keepalive.c SIP Keepalive * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" static void destructor(void *arg) { struct sip_keepalive *ka = arg; if (ka->kap) *ka->kap = NULL; list_unlink(&ka->le); } void sip_keepalive_signal(struct list *kal, int err) { struct le *le = list_head(kal); while (le) { struct sip_keepalive *ka = le->data; sip_keepalive_h *kah = ka->kah; void *arg = ka->arg; le = le->next; list_unlink(&ka->le); mem_deref(ka); kah(err, arg); } } uint64_t sip_keepalive_wait(uint32_t interval) { return interval * (800 + rand_u16() % 201); } /** * Start a keepalive handler on a SIP transport * * @param kap Pointer to allocated keepalive object * @param sip SIP Stack instance * @param msg SIP Message * @param interval Keepalive interval in seconds * @param kah Keepalive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sip_keepalive_start(struct sip_keepalive **kap, struct sip *sip, const struct sip_msg *msg, uint32_t interval, sip_keepalive_h *kah, void *arg) { struct sip_keepalive *ka; int err; if (!kap || !sip || !msg || !kah) return EINVAL; ka = mem_zalloc(sizeof(*ka), destructor); if (!ka) return ENOMEM; ka->kah = kah; ka->arg = arg; switch (msg->tp) { case SIP_TRANSP_UDP: err = sip_keepalive_udp(ka, sip, (struct udp_sock *)msg->sock, &msg->src, interval); break; case SIP_TRANSP_TCP: case SIP_TRANSP_TLS: err = sip_keepalive_tcp(ka, (struct sip_conn *)msg->sock, interval); break; default: err = EPROTONOSUPPORT; break; } if (err) { mem_deref(ka); } else { ka->kap = kap; *kap = ka; } return err; } re-1.1.0/src/sip/keepalive_udp.c000066400000000000000000000066661373627245400165150ustar00rootroot00000000000000/** * @file keepalive_udp.c SIP UDP Keepalive * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum { UDP_KEEPALIVE_INTVAL = 29, }; struct sip_udpconn { struct le he; struct list kal; struct tmr tmr_ka; struct sa maddr; struct sa paddr; struct udp_sock *us; struct stun_ctrans *ct; struct stun *stun; uint32_t ka_interval; }; static void udpconn_keepalive_handler(void *arg); static void destructor(void *arg) { struct sip_udpconn *uc = arg; list_flush(&uc->kal); hash_unlink(&uc->he); tmr_cancel(&uc->tmr_ka); mem_deref(uc->ct); mem_deref(uc->us); mem_deref(uc->stun); } static void udpconn_close(struct sip_udpconn *uc, int err) { sip_keepalive_signal(&uc->kal, err); hash_unlink(&uc->he); tmr_cancel(&uc->tmr_ka); uc->ct = mem_deref(uc->ct); uc->us = mem_deref(uc->us); uc->stun = mem_deref(uc->stun); } static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct sip_udpconn *uc = arg; struct stun_attr *attr; (void)reason; if (err || scode) { err = err ? err : EPROTO; goto out; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) { attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!attr) { err = EPROTO; goto out; } } if (!sa_isset(&uc->maddr, SA_ALL)) { uc->maddr = attr->v.sa; } else if (!sa_cmp(&uc->maddr, &attr->v.sa, SA_ALL)) { err = ENOTCONN; goto out; } out: if (err) { udpconn_close(uc, err); mem_deref(uc); } else { tmr_start(&uc->tmr_ka, sip_keepalive_wait(uc->ka_interval), udpconn_keepalive_handler, uc); } } static void udpconn_keepalive_handler(void *arg) { struct sip_udpconn *uc = arg; int err; if (!uc->kal.head) { /* no need for us anymore */ udpconn_close(uc, 0); mem_deref(uc); return; } err = stun_request(&uc->ct, uc->stun, IPPROTO_UDP, uc->us, &uc->paddr, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, uc, 1, STUN_ATTR_SOFTWARE, stun_software); if (err) { udpconn_close(uc, err); mem_deref(uc); } } static struct sip_udpconn *udpconn_find(struct sip *sip, struct udp_sock *us, const struct sa *paddr) { struct le *le; le = list_head(hash_list(sip->ht_udpconn, sa_hash(paddr, SA_ALL))); for (; le; le = le->next) { struct sip_udpconn *uc = le->data; if (!sa_cmp(&uc->paddr, paddr, SA_ALL)) continue; if (uc->us != us) continue; return uc; } return NULL; } int sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip, struct udp_sock *us, const struct sa *paddr, uint32_t interval) { struct sip_udpconn *uc; if (!ka || !sip || !us || !paddr) return EINVAL; uc = udpconn_find(sip, us, paddr); if (!uc) { uc = mem_zalloc(sizeof(*uc), destructor); if (!uc) return ENOMEM; hash_append(sip->ht_udpconn, sa_hash(paddr, SA_ALL), &uc->he, uc); uc->paddr = *paddr; uc->stun = mem_ref(sip->stun); uc->us = mem_ref(us); uc->ka_interval = interval ? interval : UDP_KEEPALIVE_INTVAL; /* learn mapped address immediately */ tmr_start(&uc->tmr_ka, 0, udpconn_keepalive_handler, uc); } list_append(&uc->kal, &ka->le, ka); return 0; } re-1.1.0/src/sip/mod.mk000066400000000000000000000005501373627245400146260ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sip/addr.c SRCS += sip/auth.c SRCS += sip/contact.c SRCS += sip/cseq.c SRCS += sip/ctrans.c SRCS += sip/dialog.c SRCS += sip/keepalive.c SRCS += sip/keepalive_udp.c SRCS += sip/msg.c SRCS += sip/reply.c SRCS += sip/request.c SRCS += sip/sip.c SRCS += sip/strans.c SRCS += sip/transp.c SRCS += sip/via.c re-1.1.0/src/sip/msg.c000066400000000000000000000303371373627245400144560ustar00rootroot00000000000000/** * @file sip/msg.c SIP Message decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum { HDR_HASH_SIZE = 32, STARTLINE_MAX = 8192, }; static void hdr_destructor(void *arg) { struct sip_hdr *hdr = arg; list_unlink(&hdr->le); hash_unlink(&hdr->he); } static void destructor(void *arg) { struct sip_msg *msg = arg; list_flush(&msg->hdrl); hash_flush(msg->hdrht); mem_deref(msg->hdrht); mem_deref(msg->sock); mem_deref(msg->mb); } static enum sip_hdrid hdr_hash(const struct pl *name) { if (!name->l) return SIP_HDR_NONE; if (name->l > 1) { switch (name->p[0]) { case 'x': case 'X': if (name->p[1] == '-') return SIP_HDR_NONE; /*@fallthrough@*/ default: return (enum sip_hdrid) (hash_joaat_ci(name->p, name->l) & 0xfff); } } /* compact headers */ switch (tolower(name->p[0])) { case 'a': return SIP_HDR_ACCEPT_CONTACT; case 'b': return SIP_HDR_REFERRED_BY; case 'c': return SIP_HDR_CONTENT_TYPE; case 'd': return SIP_HDR_REQUEST_DISPOSITION; case 'e': return SIP_HDR_CONTENT_ENCODING; case 'f': return SIP_HDR_FROM; case 'i': return SIP_HDR_CALL_ID; case 'j': return SIP_HDR_REJECT_CONTACT; case 'k': return SIP_HDR_SUPPORTED; case 'l': return SIP_HDR_CONTENT_LENGTH; case 'm': return SIP_HDR_CONTACT; case 'n': return SIP_HDR_IDENTITY_INFO; case 'o': return SIP_HDR_EVENT; case 'r': return SIP_HDR_REFER_TO; case 's': return SIP_HDR_SUBJECT; case 't': return SIP_HDR_TO; case 'u': return SIP_HDR_ALLOW_EVENTS; case 'v': return SIP_HDR_VIA; case 'x': return SIP_HDR_SESSION_EXPIRES; case 'y': return SIP_HDR_IDENTITY; default: return SIP_HDR_NONE; } } static inline bool hdr_comma_separated(enum sip_hdrid id) { switch (id) { case SIP_HDR_ACCEPT: case SIP_HDR_ACCEPT_CONTACT: case SIP_HDR_ACCEPT_ENCODING: case SIP_HDR_ACCEPT_LANGUAGE: case SIP_HDR_ACCEPT_RESOURCE_PRIORITY: case SIP_HDR_ALERT_INFO: case SIP_HDR_ALLOW: case SIP_HDR_ALLOW_EVENTS: case SIP_HDR_AUTHENTICATION_INFO: case SIP_HDR_CALL_INFO: case SIP_HDR_CONTACT: case SIP_HDR_CONTENT_ENCODING: case SIP_HDR_CONTENT_LANGUAGE: case SIP_HDR_ERROR_INFO: case SIP_HDR_HISTORY_INFO: case SIP_HDR_IN_REPLY_TO: case SIP_HDR_P_ASSERTED_IDENTITY: case SIP_HDR_P_ASSOCIATED_URI: case SIP_HDR_P_EARLY_MEDIA: case SIP_HDR_P_MEDIA_AUTHORIZATION: case SIP_HDR_P_PREFERRED_IDENTITY: case SIP_HDR_P_REFUSED_URI_LIST: case SIP_HDR_P_VISITED_NETWORK_ID: case SIP_HDR_PATH: case SIP_HDR_PERMISSION_MISSING: case SIP_HDR_PROXY_REQUIRE: case SIP_HDR_REASON: case SIP_HDR_RECORD_ROUTE: case SIP_HDR_REJECT_CONTACT: case SIP_HDR_REQUEST_DISPOSITION: case SIP_HDR_REQUIRE: case SIP_HDR_RESOURCE_PRIORITY: case SIP_HDR_ROUTE: case SIP_HDR_SECURITY_CLIENT: case SIP_HDR_SECURITY_SERVER: case SIP_HDR_SECURITY_VERIFY: case SIP_HDR_SERVICE_ROUTE: case SIP_HDR_SUPPORTED: case SIP_HDR_TRIGGER_CONSENT: case SIP_HDR_UNSUPPORTED: case SIP_HDR_VIA: case SIP_HDR_WARNING: return true; default: return false; } } static inline int hdr_add(struct sip_msg *msg, const struct pl *name, enum sip_hdrid id, const char *p, ssize_t l, bool atomic, bool line) { struct sip_hdr *hdr; int err = 0; hdr = mem_zalloc(sizeof(*hdr), hdr_destructor); if (!hdr) return ENOMEM; hdr->name = *name; hdr->val.p = p; hdr->val.l = MAX(l, 0); hdr->id = id; switch (id) { case SIP_HDR_VIA: case SIP_HDR_ROUTE: if (!atomic) break; hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr)); list_append(&msg->hdrl, &hdr->le, mem_ref(hdr)); break; default: if (atomic) hash_append(msg->hdrht, id, &hdr->he, mem_ref(hdr)); if (line) list_append(&msg->hdrl, &hdr->le, mem_ref(hdr)); break; } /* parse common headers */ switch (id) { case SIP_HDR_VIA: if (!atomic || pl_isset(&msg->via.sentby)) break; err = sip_via_decode(&msg->via, &hdr->val); break; case SIP_HDR_TO: err = sip_addr_decode((struct sip_addr *)&msg->to, &hdr->val); if (err) break; (void)msg_param_decode(&msg->to.params, "tag", &msg->to.tag); msg->to.val = hdr->val; break; case SIP_HDR_FROM: err = sip_addr_decode((struct sip_addr *)&msg->from, &hdr->val); if (err) break; (void)msg_param_decode(&msg->from.params, "tag", &msg->from.tag); msg->from.val = hdr->val; break; case SIP_HDR_CALL_ID: msg->callid = hdr->val; break; case SIP_HDR_CSEQ: err = sip_cseq_decode(&msg->cseq, &hdr->val); break; case SIP_HDR_MAX_FORWARDS: msg->maxfwd = hdr->val; break; case SIP_HDR_CONTENT_TYPE: err = msg_ctype_decode(&msg->ctyp, &hdr->val); break; case SIP_HDR_CONTENT_LENGTH: msg->clen = hdr->val; break; case SIP_HDR_EXPIRES: msg->expires = hdr->val; break; default: /* re_printf("%r = %u\n", &hdr->name, id); */ break; } mem_deref(hdr); return err; } /** * Decode a SIP message * * @param msgp Pointer to allocated SIP Message * @param mb Buffer containing SIP Message * * @return 0 if success, otherwise errorcode */ int sip_msg_decode(struct sip_msg **msgp, struct mbuf *mb) { struct pl x, y, z, e, name; const char *p, *v, *cv; struct sip_msg *msg; bool comsep, quote; enum sip_hdrid id = SIP_HDR_NONE; uint32_t ws, lf; size_t l; int err; if (!msgp || !mb) return EINVAL; p = (const char *)mbuf_buf(mb); l = mbuf_get_left(mb); if (re_regex(p, l, "[^ \t\r\n]+ [^ \t\r\n]+ [^\r\n]*[\r]*[\n]1", &x, &y, &z, NULL, &e) || x.p != (char *)mbuf_buf(mb)) return (l > STARTLINE_MAX) ? EBADMSG : ENODATA; msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) return ENOMEM; err = hash_alloc(&msg->hdrht, HDR_HASH_SIZE); if (err) goto out; msg->tag = rand_u64(); msg->mb = mem_ref(mb); msg->req = (0 == pl_strcmp(&z, "SIP/2.0")); if (msg->req) { msg->met = x; msg->ruri = y; msg->ver = z; if (uri_decode(&msg->uri, &y)) { err = EBADMSG; goto out; } } else { msg->ver = x; msg->scode = pl_u32(&y); msg->reason = z; if (!msg->scode) { err = EBADMSG; goto out; } } l -= e.p + e.l - p; p = e.p + e.l; name.p = v = cv = NULL; name.l = ws = lf = 0; comsep = false; quote = false; for (; l > 0; p++, l--) { switch (*p) { case ' ': case '\t': lf = 0; /* folding */ ++ws; break; case '\r': ++ws; break; case '\n': ++ws; if (!lf++) break; ++p; --l; /* eoh */ /*@fallthrough@*/ default: if (lf || (*p == ',' && comsep && !quote)) { if (!name.l) { err = EBADMSG; goto out; } err = hdr_add(msg, &name, id, cv ? cv : p, cv ? p - cv - ws : 0, true, cv == v && lf); if (err) goto out; if (!lf) { /* comma separated */ cv = NULL; break; } if (cv != v) { err = hdr_add(msg, &name, id, v ? v : p, v ? p - v - ws : 0, false, true); if (err) goto out; } if (lf > 1) { /* eoh */ err = 0; goto out; } comsep = false; name.p = NULL; cv = v = NULL; lf = 0; } if (!name.p) { name.p = p; name.l = 0; ws = 0; } if (!name.l) { if (*p != ':') { ws = 0; break; } name.l = MAX((int)(p - name.p - ws), 0); if (!name.l) { err = EBADMSG; goto out; } id = hdr_hash(&name); comsep = hdr_comma_separated(id); break; } if (!cv) { quote = false; cv = p; } if (!v) { v = p; } if (*p == '"') quote = !quote; ws = 0; break; } } err = ENODATA; out: if (err) mem_deref(msg); else { *msgp = msg; mb->pos = mb->end - l; } return err; } /** * Get a SIP Header from a SIP Message * * @param msg SIP Message * @param id SIP Header ID * * @return SIP Header if found, NULL if not found */ const struct sip_hdr *sip_msg_hdr(const struct sip_msg *msg, enum sip_hdrid id) { return sip_msg_hdr_apply(msg, true, id, NULL, NULL); } /** * Apply a function handler to certain SIP Headers * * @param msg SIP Message * @param fwd True to traverse forwards, false to traverse backwards * @param id SIP Header ID * @param h Function handler * @param arg Handler argument * * @return SIP Header if handler returns true, otherwise NULL */ const struct sip_hdr *sip_msg_hdr_apply(const struct sip_msg *msg, bool fwd, enum sip_hdrid id, sip_hdr_h *h, void *arg) { struct list *lst; struct le *le; if (!msg) return NULL; lst = hash_list(msg->hdrht, id); le = fwd ? list_head(lst) : list_tail(lst); while (le) { const struct sip_hdr *hdr = le->data; le = fwd ? le->next : le->prev; if (hdr->id != id) continue; if (!h || h(hdr, msg, arg)) return hdr; } return NULL; } /** * Get an unknown SIP Header from a SIP Message * * @param msg SIP Message * @param name Header name * * @return SIP Header if found, NULL if not found */ const struct sip_hdr *sip_msg_xhdr(const struct sip_msg *msg, const char *name) { return sip_msg_xhdr_apply(msg, true, name, NULL, NULL); } /** * Apply a function handler to certain unknown SIP Headers * * @param msg SIP Message * @param fwd True to traverse forwards, false to traverse backwards * @param name SIP Header name * @param h Function handler * @param arg Handler argument * * @return SIP Header if handler returns true, otherwise NULL */ const struct sip_hdr *sip_msg_xhdr_apply(const struct sip_msg *msg, bool fwd, const char *name, sip_hdr_h *h, void *arg) { struct list *lst; struct le *le; struct pl pl; if (!msg || !name) return NULL; pl_set_str(&pl, name); lst = hash_list(msg->hdrht, hdr_hash(&pl)); le = fwd ? list_head(lst) : list_tail(lst); while (le) { const struct sip_hdr *hdr = le->data; le = fwd ? le->next : le->prev; if (pl_casecmp(&hdr->name, &pl)) continue; if (!h || h(hdr, msg, arg)) return hdr; } return NULL; } static bool count_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { uint32_t *n = arg; (void)hdr; (void)msg; ++(*n); return false; } /** * Count the number of SIP Headers * * @param msg SIP Message * @param id SIP Header ID * * @return Number of SIP Headers */ uint32_t sip_msg_hdr_count(const struct sip_msg *msg, enum sip_hdrid id) { uint32_t n = 0; sip_msg_hdr_apply(msg, true, id, count_handler, &n); return n; } /** * Count the number of unknown SIP Headers * * @param msg SIP Message * @param name SIP Header name * * @return Number of SIP Headers */ uint32_t sip_msg_xhdr_count(const struct sip_msg *msg, const char *name) { uint32_t n = 0; sip_msg_xhdr_apply(msg, true, name, count_handler, &n); return n; } static bool value_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { (void)msg; return 0 == pl_strcasecmp(&hdr->val, (const char *)arg); } /** * Check if a SIP Header matches a certain value * * @param msg SIP Message * @param id SIP Header ID * @param value Header value to check * * @return True if value matches, false if not */ bool sip_msg_hdr_has_value(const struct sip_msg *msg, enum sip_hdrid id, const char *value) { return NULL != sip_msg_hdr_apply(msg, true, id, value_handler, (void *)value); } /** * Check if an unknown SIP Header matches a certain value * * @param msg SIP Message * @param name SIP Header name * @param value Header value to check * * @return True if value matches, false if not */ bool sip_msg_xhdr_has_value(const struct sip_msg *msg, const char *name, const char *value) { return NULL != sip_msg_xhdr_apply(msg, true, name, value_handler, (void *)value); } /** * Print a SIP Message to stdout * * @param msg SIP Message */ void sip_msg_dump(const struct sip_msg *msg) { struct le *le; uint32_t i; if (!msg) return; for (i=0; ihdrht, i)); while (le) { const struct sip_hdr *hdr = le->data; le = le->next; (void)re_printf("%02u '%r'='%r'\n", i, &hdr->name, &hdr->val); } } le = list_head(&msg->hdrl); while (le) { const struct sip_hdr *hdr = le->data; le = le->next; (void)re_printf("%02u '%r'='%r'\n", hdr->id, &hdr->name, &hdr->val); } } re-1.1.0/src/sip/reply.c000066400000000000000000000130661373627245400150230ustar00rootroot00000000000000/** * @file sip/reply.c SIP Reply * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "sip.h" static int vreplyf(struct sip_strans **stp, struct mbuf **mbp, bool trans, struct sip *sip, const struct sip_msg *msg, bool rec_route, uint16_t scode, const char *reason, const char *fmt, va_list ap) { bool rport = false; uint32_t viac = 0; struct mbuf *mb; struct sa dst; struct le *le; int err; if (!sip || !msg || !reason) return EINVAL; if (!pl_strcmp(&msg->met, "ACK")) return 0; mb = mbuf_alloc(1024); if (!mb) { err = ENOMEM; goto out; } err = mbuf_printf(mb, "SIP/2.0 %u %s\r\n", scode, reason); for (le = msg->hdrl.head; le; le = le->next) { struct sip_hdr *hdr = le->data; struct pl rp; switch (hdr->id) { case SIP_HDR_VIA: err |= mbuf_printf(mb, "%r: ", &hdr->name); if (viac++) { err |= mbuf_printf(mb, "%r\r\n", &hdr->val); break; } if (!msg_param_exists(&msg->via.params, "rport", &rp)){ err |= mbuf_write_pl_skip(mb, &hdr->val, &rp); err |= mbuf_printf(mb, ";rport=%u", sa_port(&msg->src)); rport = true; } else err |= mbuf_write_pl(mb, &hdr->val); if (rport || !sa_cmp(&msg->src, &msg->via.addr, SA_ADDR)) err |= mbuf_printf(mb, ";received=%j", &msg->src); err |= mbuf_write_str(mb, "\r\n"); break; case SIP_HDR_TO: err |= mbuf_printf(mb, "%r: %r", &hdr->name, &hdr->val); if (!pl_isset(&msg->to.tag) && scode > 100) err |= mbuf_printf(mb, ";tag=%016llx", msg->tag); err |= mbuf_write_str(mb, "\r\n"); break; case SIP_HDR_RECORD_ROUTE: if (!rec_route) break; /*@fallthrough@*/ case SIP_HDR_FROM: case SIP_HDR_CALL_ID: case SIP_HDR_CSEQ: err |= mbuf_printf(mb, "%r: %r\r\n", &hdr->name, &hdr->val); break; default: break; } } if (sip->software) err |= mbuf_printf(mb, "Server: %s\r\n", sip->software); if (fmt) err |= mbuf_vprintf(mb, fmt, ap); else err |= mbuf_printf(mb, "Content-Length: 0\r\n\r\n"); if (err) goto out; mb->pos = 0; sip_reply_addr(&dst, msg, rport); if (trans) { err = sip_strans_reply(stp, sip, msg, &dst, scode, mb); } else { err = sip_send(sip, msg->sock, msg->tp, &dst, mb); } out: if (err && stp) *stp = mem_deref(*stp); if (!err && mbp) *mbp = mb; else mem_deref(mb); return err; } /** * Formatted reply using Server Transaction * * @param stp Pointer to allocated SIP Server Transaction (optional) * @param mbp Pointer to allocated SIP message buffer (optional) * @param sip SIP Stack instance * @param msg Incoming SIP message * @param rec_route True to copy Record-Route headers * @param scode Response status code * @param reason Response reason phrase * @param fmt Additional formatted SIP headers and body, otherwise NULL * * @return 0 if success, otherwise errorcode */ int sip_treplyf(struct sip_strans **stp, struct mbuf **mbp, struct sip *sip, const struct sip_msg *msg, bool rec_route, uint16_t scode, const char *reason, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = vreplyf(stp, mbp, true, sip, msg, rec_route, scode, reason, fmt, ap); va_end(ap); return err; } /** * Reply using Server Transaction * * @param stp Pointer to allocated SIP Server Transaction (optional) * @param sip SIP Stack instance * @param msg Incoming SIP message * @param scode Response status code * @param reason Response reason phrase * * @return 0 if success, otherwise errorcode */ int sip_treply(struct sip_strans **stp, struct sip *sip, const struct sip_msg *msg, uint16_t scode, const char *reason) { return sip_treplyf(stp, NULL, sip, msg, false, scode, reason, NULL); } /** * Stateless formatted reply * * @param sip SIP Stack instance * @param msg Incoming SIP message * @param scode Response status code * @param reason Response reason phrase * @param fmt Additional formatted SIP headers and body, otherwise NULL * * @return 0 if success, otherwise errorcode */ int sip_replyf(struct sip *sip, const struct sip_msg *msg, uint16_t scode, const char *reason, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = vreplyf(NULL, NULL, false, sip, msg, false, scode, reason, fmt, ap); va_end(ap); return err; } /** * Stateless reply * * @param sip SIP Stack instance * @param msg Incoming SIP message * @param scode Response status code * @param reason Response reason phrase * * @return 0 if success, otherwise errorcode */ int sip_reply(struct sip *sip, const struct sip_msg *msg, uint16_t scode, const char *reason) { return sip_replyf(sip, msg, scode, reason, NULL); } /** * Get the reply address from a SIP message * * @param addr Network address, set on return * @param msg SIP message * @param rport Rport value */ void sip_reply_addr(struct sa *addr, const struct sip_msg *msg, bool rport) { uint16_t port; struct pl pl; if (!addr || !msg) return; port = sa_port(&msg->via.addr); *addr = msg->src; switch (msg->tp) { case SIP_TRANSP_UDP: if (!msg_param_decode(&msg->via.params, "maddr", &pl)) { (void)sa_set(addr, &pl,sip_transp_port(msg->tp, port)); break; } if (rport) break; /*@fallthrough@*/ case SIP_TRANSP_TCP: case SIP_TRANSP_TLS: sa_set_port(addr, sip_transp_port(msg->tp, port)); break; default: break; } } re-1.1.0/src/sip/request.c000066400000000000000000000435651373627245400153670ustar00rootroot00000000000000/** * @file sip/request.c SIP Request * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" struct sip_request { struct le le; struct list cachel; struct list addrl; struct list srvl; struct sip_request **reqp; struct sip_ctrans *ct; struct dns_query *dnsq; struct dns_query *dnsq2; struct sip *sip; char *met; char *uri; char *host; struct mbuf *mb; sip_send_h *sendh; sip_resp_h *resph; void *arg; size_t sortkey; enum sip_transp tp; bool tp_selected; bool stateful; bool canceled; bool provrecv; uint16_t port; }; static int request_next(struct sip_request *req); static bool rr_append_handler(struct dnsrr *rr, void *arg); static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg); static int srv_lookup(struct sip_request *req, const char *domain); static int addr_lookup(struct sip_request *req, const char *name); static int str_ldup(char **dst, const char *src, int len) { struct pl pl; pl.p = src; pl.l = len < 0 ? str_len(src) : (size_t)len; return pl_strdup(dst, &pl); } static void destructor(void *arg) { struct sip_request *req = arg; if (req->reqp && req->stateful) { /* user does deref before request has completed */ *req->reqp = NULL; req->reqp = NULL; req->sendh = NULL; req->resph = NULL; sip_request_cancel(mem_ref(req)); return; } list_flush(&req->cachel); list_flush(&req->addrl); list_flush(&req->srvl); list_unlink(&req->le); mem_deref(req->dnsq); mem_deref(req->dnsq2); mem_deref(req->ct); mem_deref(req->met); mem_deref(req->uri); mem_deref(req->host); mem_deref(req->mb); } static void terminate(struct sip_request *req, int err, const struct sip_msg *msg) { if (req->reqp) { *req->reqp = NULL; req->reqp = NULL; } list_unlink(&req->le); req->sendh = NULL; if (req->resph) { req->resph(err, msg, req->arg); req->resph = NULL; } } static bool close_handler(struct le *le, void *arg) { struct sip_request *req = le->data; (void)arg; req->dnsq = mem_deref(req->dnsq); req->dnsq2 = mem_deref(req->dnsq2); req->ct = mem_deref(req->ct); terminate(req, ECONNABORTED, NULL); mem_deref(req); return false; } static void response_handler(int err, const struct sip_msg *msg, void *arg) { struct sip_request *req = arg; if (msg && msg->scode < 200) { if (!req->provrecv) { req->provrecv = true; if (req->canceled) (void)sip_ctrans_cancel(req->ct); } if (req->resph) req->resph(err, msg, req->arg); return; } req->ct = NULL; if (!req->canceled && (err || msg->scode == 503) && (req->addrl.head || req->srvl.head)) { err = request_next(req); if (!err) return; } terminate(req, err, msg); mem_deref(req); } static int request(struct sip_request *req, enum sip_transp tp, const struct sa *dst) { struct mbuf *mb = NULL; char *branch = NULL; int err = ENOMEM; struct sa laddr; req->provrecv = false; branch = mem_alloc(24, NULL); mb = mbuf_alloc(1024); if (!branch || !mb) goto out; (void)re_snprintf(branch, 24, "z9hG4bK%016llx", rand_u64()); err = sip_transp_laddr(req->sip, &laddr, tp, dst); if (err) goto out; err = mbuf_printf(mb, "%s %s SIP/2.0\r\n", req->met, req->uri); err |= mbuf_printf(mb, "Via: SIP/2.0/%s %J;branch=%s;rport\r\n", sip_transp_name(tp), &laddr, branch); err |= req->sendh ? req->sendh(tp, &laddr, dst, mb, req->arg) : 0; err |= mbuf_write_mem(mb, mbuf_buf(req->mb), mbuf_get_left(req->mb)); if (err) goto out; mb->pos = 0; if (!req->stateful) err = sip_send(req->sip, NULL, tp, dst, mb); else err = sip_ctrans_request(&req->ct, req->sip, tp, dst, req->met, branch, mb, response_handler, req); if (err) goto out; out: mem_deref(branch); mem_deref(mb); return err; } static int request_next(struct sip_request *req) { struct dnsrr *rr; struct sa dst; int err; again: rr = list_ledata(req->addrl.head); if (!rr) { rr = list_ledata(req->srvl.head); if (!rr) return ENOENT; req->port = rr->rdata.srv.port; dns_rrlist_apply2(&req->cachel, rr->rdata.srv.target, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN, true, rr_append_handler, &req->addrl); list_unlink(&rr->le); if (req->addrl.head) { dns_rrlist_sort_addr(&req->addrl, req->sortkey); mem_deref(rr); goto again; } err = addr_lookup(req, rr->rdata.srv.target); mem_deref(rr); return err; } switch (rr->type) { case DNS_TYPE_A: sa_set_in(&dst, rr->rdata.a.addr, req->port); break; case DNS_TYPE_AAAA: sa_set_in6(&dst, rr->rdata.aaaa.addr, req->port); break; default: return EINVAL; } list_unlink(&rr->le); mem_deref(rr); err = request(req, req->tp, &dst); if (err) { if (req->addrl.head || req->srvl.head) goto again; } else if (!req->stateful) { req->resph = NULL; terminate(req, 0, NULL); mem_deref(req); } return err; } static bool transp_next(struct sip *sip, enum sip_transp *tp) { enum sip_transp i; for (i=(enum sip_transp)(*tp+1); iSIP_TRANSP_NONE; i--) { if (!sip_transp_supported(sip, i, AF_UNSPEC)) continue; *tp = i; return true; } return false; } static bool rr_append_handler(struct dnsrr *rr, void *arg) { struct list *lst = arg; switch (rr->type) { case DNS_TYPE_A: case DNS_TYPE_AAAA: case DNS_TYPE_SRV: if (rr->le.list) break; list_append(lst, &rr->le, mem_ref(rr)); break; } return false; } static bool rr_cache_handler(struct dnsrr *rr, void *arg) { struct sip_request *req = arg; switch (rr->type) { case DNS_TYPE_A: if (!sip_transp_supported(req->sip, req->tp, AF_INET)) break; list_unlink(&rr->le_priv); list_append(&req->cachel, &rr->le_priv, rr); break; #ifdef HAVE_INET6 case DNS_TYPE_AAAA: if (!sip_transp_supported(req->sip, req->tp, AF_INET6)) break; list_unlink(&rr->le_priv); list_append(&req->cachel, &rr->le_priv, rr); break; #endif case DNS_TYPE_CNAME: list_unlink(&rr->le_priv); list_append(&req->cachel, &rr->le_priv, rr); break; } return false; } static bool rr_naptr_handler(struct dnsrr *rr, void *arg) { struct sip_request *req = arg; enum sip_transp tp; if (rr->type != DNS_TYPE_NAPTR) return false; if (!str_casecmp(rr->rdata.naptr.services, "SIP+D2U")) tp = SIP_TRANSP_UDP; else if (!str_casecmp(rr->rdata.naptr.services, "SIP+D2T")) tp = SIP_TRANSP_TCP; else if (!str_casecmp(rr->rdata.naptr.services, "SIPS+D2T")) tp = SIP_TRANSP_TLS; else if (!str_casecmp(rr->rdata.naptr.services, "SIP+D2W")) tp = SIP_TRANSP_WS; else if (!str_casecmp(rr->rdata.naptr.services, "SIPS+D2W")) tp = SIP_TRANSP_WSS; else return false; if (!sip_transp_supported(req->sip, tp, AF_UNSPEC)) return false; req->tp = tp; req->tp_selected = true; return true; } static void naptr_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct sip_request *req = arg; struct dnsrr *rr; (void)hdr; (void)authl; dns_rrlist_sort(ansl, DNS_TYPE_NAPTR, req->sortkey); rr = dns_rrlist_apply(ansl, NULL, DNS_TYPE_NAPTR, DNS_CLASS_IN, false, rr_naptr_handler, req); if (!rr) { req->tp = SIP_TRANSPC; if (!transp_next_srv(req->sip, &req->tp)) { err = EPROTONOSUPPORT; goto fail; } err = srv_lookup(req, req->host); if (err) goto fail; return; } dns_rrlist_apply(addl, rr->rdata.naptr.replace, DNS_TYPE_SRV, DNS_CLASS_IN, true, rr_append_handler, &req->srvl); if (!req->srvl.head) { err = dnsc_query(&req->dnsq, req->sip->dnsc, rr->rdata.naptr.replace, DNS_TYPE_SRV, DNS_CLASS_IN, true, srv_handler, req); if (err) goto fail; return; } dns_rrlist_sort(&req->srvl, DNS_TYPE_SRV, req->sortkey); dns_rrlist_apply(addl, NULL, DNS_QTYPE_ANY, DNS_CLASS_IN, false, rr_cache_handler, req); err = request_next(req); if (err) goto fail; return; fail: terminate(req, err, NULL); mem_deref(req); } static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct sip_request *req = arg; (void)hdr; (void)authl; dns_rrlist_apply(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false, rr_append_handler, &req->srvl); if (!req->srvl.head) { if (!req->tp_selected) { if (transp_next_srv(req->sip, &req->tp)) { err = srv_lookup(req, req->host); if (err) goto fail; return; } req->tp = SIP_TRANSP_NONE; if (!transp_next(req->sip, &req->tp)) { err = EPROTONOSUPPORT; goto fail; } } req->port = sip_transp_port(req->tp, 0); err = addr_lookup(req, req->host); if (err) goto fail; return; } dns_rrlist_sort(&req->srvl, DNS_TYPE_SRV, req->sortkey); dns_rrlist_apply(addl, NULL, DNS_QTYPE_ANY, DNS_CLASS_IN, false, rr_cache_handler, req); err = request_next(req); if (err) goto fail; return; fail: terminate(req, err, NULL); mem_deref(req); } static void addr_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct sip_request *req = arg; (void)hdr; (void)authl; (void)addl; dns_rrlist_apply2(ansl, NULL, DNS_TYPE_A, DNS_TYPE_AAAA, DNS_CLASS_IN, false, rr_append_handler, &req->addrl); /* wait for other (A/AAAA) query to complete */ if (req->dnsq || req->dnsq2) return; if (!req->addrl.head && !req->srvl.head) { err = err ? err : EDESTADDRREQ; goto fail; } dns_rrlist_sort_addr(&req->addrl, req->sortkey); err = request_next(req); if (err) goto fail; return; fail: terminate(req, err, NULL); mem_deref(req); } static int srv_lookup(struct sip_request *req, const char *domain) { char name[256]; if (re_snprintf(name, sizeof(name), "%s.%s", sip_transp_srvid(req->tp), domain) < 0) return ENOMEM; return dnsc_query(&req->dnsq, req->sip->dnsc, name, DNS_TYPE_SRV, DNS_CLASS_IN, true, srv_handler, req); } static int addr_lookup(struct sip_request *req, const char *name) { int err; if (sip_transp_supported(req->sip, req->tp, AF_INET)) { err = dnsc_query(&req->dnsq, req->sip->dnsc, name, DNS_TYPE_A, DNS_CLASS_IN, true, addr_handler, req); if (err) return err; } #ifdef HAVE_INET6 if (sip_transp_supported(req->sip, req->tp, AF_INET6)) { err = dnsc_query(&req->dnsq2, req->sip->dnsc, name, DNS_TYPE_AAAA, DNS_CLASS_IN, true, addr_handler, req); if (err) return err; } #endif if (!req->dnsq && !req->dnsq2) return EPROTONOSUPPORT; return 0; } /** * Send a SIP request * * @param reqp Pointer to allocated SIP request object * @param sip SIP Stack * @param stateful Stateful client transaction * @param met SIP Method string * @param metl Length of SIP Method string * @param uri Request URI * @param uril Length of Request URI string * @param route Next hop route URI * @param mb Buffer containing SIP request * @param sortkey Key for DNS record sorting * @param sendh Send handler * @param resph Response handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sip_request(struct sip_request **reqp, struct sip *sip, bool stateful, const char *met, int metl, const char *uri, int uril, const struct uri *route, struct mbuf *mb, size_t sortkey, sip_send_h *sendh, sip_resp_h *resph, void *arg) { struct sip_request *req; struct sa dst; struct pl pl; int err; if (!sip || !met || !uri || !route || !mb) return EINVAL; if (pl_strcasecmp(&route->scheme, "sip")) return ENOSYS; req = mem_zalloc(sizeof(*req), destructor); if (!req) return ENOMEM; list_append(&sip->reql, &req->le, req); err = str_ldup(&req->met, met, metl); if (err) goto out; err = str_ldup(&req->uri, uri, uril); if (err) goto out; if (msg_param_decode(&route->params, "maddr", &pl)) pl = route->host; err = pl_strdup(&req->host, &pl); if (err) goto out; req->stateful = stateful; req->sortkey = sortkey; req->mb = mem_ref(mb); req->sip = sip; req->sendh = sendh; req->resph = resph; req->arg = arg; if (!msg_param_decode(&route->params, "transport", &pl)) { if (!pl_strcasecmp(&pl, "udp")) req->tp = SIP_TRANSP_UDP; else if (!pl_strcasecmp(&pl, "tcp")) req->tp = SIP_TRANSP_TCP; else if (!pl_strcasecmp(&pl, "tls")) req->tp = SIP_TRANSP_TLS; else if (!pl_strcasecmp(&pl, "ws")) req->tp = SIP_TRANSP_WS; else if (!pl_strcasecmp(&pl, "wss")) req->tp = SIP_TRANSP_WSS; else { err = EPROTONOSUPPORT; goto out; } if (!sip_transp_supported(sip, req->tp, AF_UNSPEC)) { err = EPROTONOSUPPORT; goto out; } req->tp_selected = true; } else { req->tp = SIP_TRANSP_NONE; if (!transp_next(sip, &req->tp)) { err = EPROTONOSUPPORT; goto out; } req->tp_selected = false; } if (!sa_set_str(&dst, req->host, sip_transp_port(req->tp, route->port))) { err = request(req, req->tp, &dst); if (!req->stateful) { mem_deref(req); return err; } } else if (route->port) { req->port = sip_transp_port(req->tp, route->port); err = addr_lookup(req, req->host); } else if (req->tp_selected) { err = srv_lookup(req, req->host); } else { err = dnsc_query(&req->dnsq, sip->dnsc, req->host, DNS_TYPE_NAPTR, DNS_CLASS_IN, true, naptr_handler, req); } out: if (err) mem_deref(req); else if (reqp) { req->reqp = reqp; *reqp = req; } return err; } /** * Send a SIP request with formatted arguments * * @param reqp Pointer to allocated SIP request object * @param sip SIP Stack * @param stateful Stateful client transaction * @param met Null-terminated SIP Method string * @param uri Null-terminated Request URI string * @param route Next hop route URI (optional) * @param auth SIP authentication state * @param sendh Send handler * @param resph Response handler * @param arg Handler argument * @param fmt Formatted SIP headers and body * * @return 0 if success, otherwise errorcode */ int sip_requestf(struct sip_request **reqp, struct sip *sip, bool stateful, const char *met, const char *uri, const struct uri *route, struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph, void *arg, const char *fmt, ...) { struct uri lroute; struct mbuf *mb; va_list ap; int err; if (!sip || !met || !uri || !fmt) return EINVAL; if (!route) { struct pl uripl; pl_set_str(&uripl, uri); err = uri_decode(&lroute, &uripl); if (err) return err; route = &lroute; } mb = mbuf_alloc(2048); if (!mb) return ENOMEM; err = mbuf_write_str(mb, "Max-Forwards: 70\r\n"); if (auth) err |= sip_auth_encode(mb, auth, met, uri); if (err) goto out; va_start(ap, fmt); err = mbuf_vprintf(mb, fmt, ap); va_end(ap); if (err) goto out; mb->pos = 0; err = sip_request(reqp, sip, stateful, met, -1, uri, -1, route, mb, (size_t)arg, sendh, resph, arg); if (err) goto out; out: mem_deref(mb); return err; } /** * Send a SIP dialog request with formatted arguments * * @param reqp Pointer to allocated SIP request object * @param sip SIP Stack * @param stateful Stateful client transaction * @param met Null-terminated SIP Method string * @param dlg SIP Dialog state * @param cseq CSeq number * @param auth SIP authentication state * @param sendh Send handler * @param resph Response handler * @param arg Handler argument * @param fmt Formatted SIP headers and body * * @return 0 if success, otherwise errorcode */ int sip_drequestf(struct sip_request **reqp, struct sip *sip, bool stateful, const char *met, struct sip_dialog *dlg, uint32_t cseq, struct sip_auth *auth, sip_send_h *sendh, sip_resp_h *resph, void *arg, const char *fmt, ...) { struct mbuf *mb; va_list ap; int err; if (!sip || !met || !dlg || !fmt) return EINVAL; mb = mbuf_alloc(2048); if (!mb) return ENOMEM; err = mbuf_write_str(mb, "Max-Forwards: 70\r\n"); if (auth) err |= sip_auth_encode(mb, auth, met, sip_dialog_uri(dlg)); err |= sip_dialog_encode(mb, dlg, cseq, met); if (sip->software) err |= mbuf_printf(mb, "User-Agent: %s\r\n", sip->software); if (err) goto out; va_start(ap, fmt); err = mbuf_vprintf(mb, fmt, ap); va_end(ap); if (err) goto out; mb->pos = 0; err = sip_request(reqp, sip, stateful, met, -1, sip_dialog_uri(dlg), -1, sip_dialog_route(dlg), mb, sip_dialog_hash(dlg), sendh, resph, arg); if (err) goto out; out: mem_deref(mb); return err; } /** * Cancel a pending SIP Request * * @param req SIP Request */ void sip_request_cancel(struct sip_request *req) { if (!req || req->canceled) return; req->canceled = true; if (!req->provrecv) return; (void)sip_ctrans_cancel(req->ct); } void sip_request_close(struct sip *sip) { if (!sip) return; list_apply(&sip->reql, true, close_handler, NULL); } /** * Check if a SIP request loops * * @param ls Loop state * @param scode Status code from SIP response * * @return True if loops, otherwise false */ bool sip_request_loops(struct sip_loopstate *ls, uint16_t scode) { bool loop = false; if (!ls) return false; if (scode < 200) { return false; } else if (scode < 300) { ls->failc = 0; } else if (scode < 400) { loop = (++ls->failc >= 16); } else { switch (scode) { default: if (ls->last_scode == scode) loop = true; /*@fallthrough@*/ case 401: case 407: case 491: if (++ls->failc >= 16) loop = true; break; } } ls->last_scode = scode; return loop; } /** * Reset the loop state * * @param ls Loop state */ void sip_loopstate_reset(struct sip_loopstate *ls) { if (!ls) return; ls->last_scode = 0; ls->failc = 0; } re-1.1.0/src/sip/sip.c000066400000000000000000000120201373627245400144500ustar00rootroot00000000000000/** * @file sip.c SIP Core * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" static void websock_shutdown_handler(void *arg) { struct sip *sip = arg; if (sip->exith) sip->exith(sip->arg); } static void destructor(void *arg) { struct sip *sip = arg; if (sip->closing) { sip->closing = false; mem_ref(sip); if (mem_nrefs(sip->websock) > 1) { /* NOTE: we must flush all connections here, since they have a reference to websock */ hash_flush(sip->ht_conn); sip->ht_conn = mem_deref(sip->ht_conn); websock_shutdown(sip->websock); } else { if (sip->exith) sip->exith(sip->arg); } return; } sip_request_close(sip); sip_request_close(sip); hash_flush(sip->ht_ctrans); mem_deref(sip->ht_ctrans); hash_flush(sip->ht_strans); hash_clear(sip->ht_strans_mrg); mem_deref(sip->ht_strans); mem_deref(sip->ht_strans_mrg); hash_flush(sip->ht_conn); mem_deref(sip->ht_conn); hash_flush(sip->ht_udpconn); mem_deref(sip->ht_udpconn); list_flush(&sip->transpl); list_flush(&sip->lsnrl); mem_deref(sip->software); mem_deref(sip->dnsc); mem_deref(sip->stun); mem_deref(sip->websock); } static void lsnr_destructor(void *arg) { struct sip_lsnr *lsnr = arg; if (lsnr->lsnrp) *lsnr->lsnrp = NULL; list_unlink(&lsnr->le); } /** * Allocate a SIP stack instance * * @param sipp Pointer to allocated SIP stack * @param dnsc DNS Client (optional) * @param ctsz Size of client transactions hashtable (power of 2) * @param stsz Size of server transactions hashtable (power of 2) * @param tcsz Size of SIP transport hashtable (power of 2) * @param software Software identifier * @param exith SIP-stack exit handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sip_alloc(struct sip **sipp, struct dnsc *dnsc, uint32_t ctsz, uint32_t stsz, uint32_t tcsz, const char *software, sip_exit_h *exith, void *arg) { struct sip *sip; int err; if (!sipp) return EINVAL; sip = mem_zalloc(sizeof(*sip), destructor); if (!sip) return ENOMEM; err = sip_transp_init(sip, tcsz); if (err) goto out; err = sip_ctrans_init(sip, ctsz); if (err) goto out; err = sip_strans_init(sip, stsz); if (err) goto out; err = hash_alloc(&sip->ht_udpconn, tcsz); if (err) goto out; err = stun_alloc(&sip->stun, NULL, NULL, NULL); if (err) goto out; if (software) { err = str_dup(&sip->software, software); if (err) goto out; } sip->dnsc = mem_ref(dnsc); sip->exith = exith; sip->arg = arg; err = websock_alloc(&sip->websock, websock_shutdown_handler, sip); if (err) goto out; out: if (err) mem_deref(sip); else *sipp = sip; return err; } /** * Close the SIP stack instance * * @param sip SIP stack instance * @param force Don't wait for transactions to complete */ void sip_close(struct sip *sip, bool force) { if (!sip) return; if (force) { sip_request_close(sip); sip_request_close(sip); } else if (!sip->closing) { sip->closing = true; mem_deref(sip); } } /** * Send a SIP message * * @param sip SIP stack instance * @param sock Optional socket to send from * @param tp SIP transport * @param dst Destination network address * @param mb Buffer containing SIP message * * @return 0 if success, otherwise errorcode */ int sip_send(struct sip *sip, void *sock, enum sip_transp tp, const struct sa *dst, struct mbuf *mb) { return sip_transp_send(NULL, sip, sock, tp, dst, mb, NULL, NULL); } /** * Listen for incoming SIP Requests and SIP Responses * * @param lsnrp Pointer to allocated listener * @param sip SIP stack instance * @param req True for Request, false for Response * @param msgh SIP message handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sip_listen(struct sip_lsnr **lsnrp, struct sip *sip, bool req, sip_msg_h *msgh, void *arg) { struct sip_lsnr *lsnr; if (!sip || !msgh) return EINVAL; lsnr = mem_zalloc(sizeof(*lsnr), lsnr_destructor); if (!lsnr) return ENOMEM; list_append(&sip->lsnrl, &lsnr->le, lsnr); lsnr->msgh = msgh; lsnr->arg = arg; lsnr->req = req; if (lsnrp) { lsnr->lsnrp = lsnrp; *lsnrp = lsnr; } return 0; } /** * Print debug information about the SIP stack * * @param pf Print function for debug output * @param sip SIP stack instance * * @return 0 if success, otherwise errorcode */ int sip_debug(struct re_printf *pf, const struct sip *sip) { int err; if (!sip) return 0; err = sip_transp_debug(pf, sip); err |= sip_ctrans_debug(pf, sip); err |= sip_strans_debug(pf, sip); return err; } void sip_set_trace_handler(struct sip *sip, sip_trace_h *traceh) { if (!sip) return; sip->traceh = traceh; } re-1.1.0/src/sip/sip.h000066400000000000000000000050541373627245400144660ustar00rootroot00000000000000/** * @file sip.h SIP Private Interface * * Copyright (C) 2010 Creytiv.com */ struct sip { struct list transpl; struct list lsnrl; struct list reql; struct hash *ht_ctrans; struct hash *ht_strans; struct hash *ht_strans_mrg; struct hash *ht_conn; struct hash *ht_udpconn; struct dnsc *dnsc; struct stun *stun; struct websock *websock; char *software; sip_exit_h *exith; sip_trace_h *traceh; void *arg; bool closing; }; struct sip_lsnr { struct le le; struct sip_lsnr **lsnrp; sip_msg_h *msgh; void *arg; bool req; }; struct sip_keepalive { struct le le; struct sip_keepalive **kap; sip_keepalive_h *kah; void *arg; }; /* request */ void sip_request_close(struct sip *sip); /* ctrans */ struct sip_ctrans; int sip_ctrans_request(struct sip_ctrans **ctp, struct sip *sip, enum sip_transp tp, const struct sa *dst, char *met, char *branch, struct mbuf *mb, sip_resp_h *resph, void *arg); int sip_ctrans_cancel(struct sip_ctrans *ct); int sip_ctrans_init(struct sip *sip, uint32_t sz); int sip_ctrans_debug(struct re_printf *pf, const struct sip *sip); /* strans */ int sip_strans_init(struct sip *sip, uint32_t sz); int sip_strans_debug(struct re_printf *pf, const struct sip *sip); /* transp */ struct sip_connqent; typedef void(sip_transp_h)(int err, void *arg); int sip_transp_init(struct sip *sip, uint32_t sz); int sip_transp_send(struct sip_connqent **qentp, struct sip *sip, void *sock, enum sip_transp tp, const struct sa *dst, struct mbuf *mb, sip_transp_h *transph, void *arg); bool sip_transp_supported(struct sip *sip, enum sip_transp tp, int af); const char *sip_transp_srvid(enum sip_transp tp); bool sip_transp_reliable(enum sip_transp tp); int sip_transp_debug(struct re_printf *pf, const struct sip *sip); /* auth */ int sip_auth_encode(struct mbuf *mb, struct sip_auth *auth, const char *met, const char *uri); /* dialog */ int sip_dialog_encode(struct mbuf *mb, struct sip_dialog *dlg, uint32_t cseq, const char *met); const char *sip_dialog_uri(const struct sip_dialog *dlg); const struct uri *sip_dialog_route(const struct sip_dialog *dlg); uint32_t sip_dialog_hash(const struct sip_dialog *dlg); /* keepalive */ struct sip_conn; void sip_keepalive_signal(struct list *kal, int err); uint64_t sip_keepalive_wait(uint32_t interval); int sip_keepalive_tcp(struct sip_keepalive *ka, struct sip_conn *conn, uint32_t interval); int sip_keepalive_udp(struct sip_keepalive *ka, struct sip *sip, struct udp_sock *us, const struct sa *paddr, uint32_t interval); re-1.1.0/src/sip/strans.c000066400000000000000000000202441373627245400151760ustar00rootroot00000000000000/** * @file strans.c SIP Server Transaction * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum state { TRYING, PROCEEDING, ACCEPTED, COMPLETED, CONFIRMED, }; struct sip_strans { struct le he; struct le he_mrg; struct tmr tmr; struct tmr tmrg; struct sa dst; struct sip *sip; struct sip_msg *msg; struct mbuf *mb; sip_cancel_h *cancelh; void *arg; enum state state; uint32_t txc; bool invite; }; static void destructor(void *arg) { struct sip_strans *st = arg; hash_unlink(&st->he); hash_unlink(&st->he_mrg); tmr_cancel(&st->tmr); tmr_cancel(&st->tmrg); mem_deref(st->msg); mem_deref(st->mb); } static bool strans_cmp(const struct sip_msg *msg1, const struct sip_msg *msg2) { if (pl_cmp(&msg1->via.branch, &msg2->via.branch)) return false; if (pl_cmp(&msg1->via.sentby, &msg2->via.sentby)) return false; return true; } static bool cmp_handler(struct le *le, void *arg) { struct sip_strans *st = le->data; const struct sip_msg *msg = arg; if (!strans_cmp(st->msg, msg)) return false; if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met)) return false; return true; } static bool cmp_ack_handler(struct le *le, void *arg) { struct sip_strans *st = le->data; const struct sip_msg *msg = arg; if (!strans_cmp(st->msg, msg)) return false; if (pl_strcmp(&st->msg->cseq.met, "INVITE")) return false; return true; } static bool cmp_cancel_handler(struct le *le, void *arg) { struct sip_strans *st = le->data; const struct sip_msg *msg = arg; if (!strans_cmp(st->msg, msg)) return false; if (!pl_strcmp(&st->msg->cseq.met, "CANCEL")) return false; return true; } static bool cmp_merge_handler(struct le *le, void *arg) { struct sip_strans *st = le->data; const struct sip_msg *msg = arg; if (pl_cmp(&st->msg->cseq.met, &msg->cseq.met)) return false; if (st->msg->cseq.num != msg->cseq.num) return false; if (pl_cmp(&st->msg->callid, &msg->callid)) return false; if (pl_cmp(&st->msg->from.tag, &msg->from.tag)) return false; if (pl_cmp(&st->msg->ruri, &msg->ruri)) return false; return true; } static void dummy_handler(void *arg) { (void)arg; } static void tmr_handler(void *arg) { struct sip_strans *st = arg; mem_deref(st); } static void retransmit_handler(void *arg) { struct sip_strans *st = arg; (void)sip_send(st->sip, st->msg->sock, st->msg->tp, &st->dst, st->mb); st->txc++; tmr_start(&st->tmrg, MIN(SIP_T1<txc, SIP_T2), retransmit_handler, st); } static bool ack_handler(struct sip *sip, const struct sip_msg *msg) { struct sip_strans *st; st = list_ledata(hash_lookup(sip->ht_strans, hash_joaat_pl(&msg->via.branch), cmp_ack_handler, (void *)msg)); if (!st) return false; switch (st->state) { case ACCEPTED: /* make sure ACKs for 2xx are passed to TU */ return false; case COMPLETED: if (sip_transp_reliable(st->msg->tp)) { mem_deref(st); break; } tmr_start(&st->tmr, SIP_T4, tmr_handler, st); tmr_cancel(&st->tmrg); st->state = CONFIRMED; break; default: break; } return true; } static bool cancel_handler(struct sip *sip, const struct sip_msg *msg) { struct sip_strans *st; st = list_ledata(hash_lookup(sip->ht_strans, hash_joaat_pl(&msg->via.branch), cmp_cancel_handler, (void *)msg)); if (!st) return false; ((struct sip_msg *)msg)->tag = st->msg->tag; (void)sip_reply(sip, msg, 200, "OK"); switch (st->state) { case TRYING: case PROCEEDING: st->cancelh(st->arg); break; default: break; } return true; } static bool request_handler(const struct sip_msg *msg, void *arg) { struct sip_strans *st; struct sip *sip = arg; if (!pl_strcmp(&msg->met, "ACK")) return ack_handler(sip, msg); st = list_ledata(hash_lookup(sip->ht_strans, hash_joaat_pl(&msg->via.branch), cmp_handler, (void *)msg)); if (st) { switch (st->state) { case PROCEEDING: case COMPLETED: (void)sip_send(st->sip, st->msg->sock, st->msg->tp, &st->dst, st->mb); break; default: break; } return true; } else if (!pl_isset(&msg->to.tag)) { st = list_ledata(hash_lookup(sip->ht_strans_mrg, hash_joaat_pl(&msg->callid), cmp_merge_handler, (void *)msg)); if (st) { (void)sip_reply(sip, msg, 482, "Loop Detected"); return true; } } if (!pl_strcmp(&msg->met, "CANCEL")) return cancel_handler(sip, msg); return false; } /** * Allocate a SIP Server Transaction * * @param stp Pointer to allocated SIP Server Transaction * @param sip SIP Stack instance * @param msg Incoming SIP message * @param cancelh Cancel handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sip_strans_alloc(struct sip_strans **stp, struct sip *sip, const struct sip_msg *msg, sip_cancel_h *cancelh, void *arg) { struct sip_strans *st; if (!stp || !sip || !msg) return EINVAL; st = mem_zalloc(sizeof(*st), destructor); if (!st) return ENOMEM; hash_append(sip->ht_strans, hash_joaat_pl(&msg->via.branch), &st->he, st); hash_append(sip->ht_strans_mrg, hash_joaat_pl(&msg->callid), &st->he_mrg, st); st->invite = !pl_strcmp(&msg->met, "INVITE"); st->msg = mem_ref((void *)msg); st->state = TRYING; st->cancelh = cancelh ? cancelh : dummy_handler; st->arg = arg; st->sip = sip; *stp = st; return 0; } /** * Reply using a SIP Server Transaction * * @param stp Pointer to allocated SIP Server Transaction * @param sip SIP Stack instance * @param msg Incoming SIP message * @param dst Destination network address * @param scode Response status code * @param mb Buffer containing SIP response * * @return 0 if success, otherwise errorcode */ int sip_strans_reply(struct sip_strans **stp, struct sip *sip, const struct sip_msg *msg, const struct sa *dst, uint16_t scode, struct mbuf *mb) { struct sip_strans *st = NULL; int err; if (!sip || !mb || !dst || (scode < 200 && !stp)) return EINVAL; if (stp) st = *stp; if (!st) { err = sip_strans_alloc(&st, sip, msg, NULL, NULL); if (err) return err; } mem_deref(st->mb); st->mb = mem_ref(mb); st->dst = *dst; err = sip_send(sip, st->msg->sock, st->msg->tp, dst, mb); if (stp) *stp = (err || scode >= 200) ? NULL : st; if (err) { mem_deref(st); return err; } if (st->invite) { if (scode < 200) { st->state = PROCEEDING; } else if (scode < 300) { tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st); st->state = ACCEPTED; } else { tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st); st->state = COMPLETED; if (!sip_transp_reliable(st->msg->tp)) tmr_start(&st->tmrg, SIP_T1, retransmit_handler, st); } } else { if (scode < 200) { st->state = PROCEEDING; } else { if (!sip_transp_reliable(st->msg->tp)) { tmr_start(&st->tmr, 64 * SIP_T1, tmr_handler, st); st->state = COMPLETED; } else { mem_deref(st); } } } return 0; } int sip_strans_init(struct sip *sip, uint32_t sz) { int err; err = sip_listen(NULL, sip, true, request_handler, sip); if (err) return err; err = hash_alloc(&sip->ht_strans_mrg, sz); if (err) return err; return hash_alloc(&sip->ht_strans, sz); } static const char *statename(enum state state) { switch (state) { case TRYING: return "TRYING"; case PROCEEDING: return "PROCEEDING"; case ACCEPTED: return "ACCEPTED"; case COMPLETED: return "COMPLETED"; case CONFIRMED: return "CONFIRMED"; default: return "???"; } } static bool debug_handler(struct le *le, void *arg) { struct sip_strans *st = le->data; struct re_printf *pf = arg; (void)re_hprintf(pf, " %-10r %-10s %2llus (%r)\n", &st->msg->met, statename(st->state), tmr_get_expire(&st->tmr)/1000, &st->msg->via.branch); return false; } int sip_strans_debug(struct re_printf *pf, const struct sip *sip) { int err; err = re_hprintf(pf, "server transactions:\n"); hash_apply(sip->ht_strans, debug_handler, pf); return err; } re-1.1.0/src/sip/transp.c000066400000000000000000000660631373627245400152040ustar00rootroot00000000000000/** * @file sip/transp.c SIP Transport * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sip.h" enum { TCP_ACCEPT_TIMEOUT = 32, TCP_IDLE_TIMEOUT = 900, TCP_KEEPALIVE_TIMEOUT = 10, TCP_KEEPALIVE_INTVAL = 120, TCP_BUFSIZE_MAX = 65536, }; struct sip_transport { struct le le; struct sa laddr; struct sip *sip; struct tls *tls; void *sock; enum sip_transp tp; struct http_cli *http_cli; struct http_sock *http_sock; }; struct sip_conn { struct le he; struct list ql; struct list kal; struct tmr tmr; struct tmr tmr_ka; struct sa laddr; struct sa paddr; struct tls_conn *sc; struct tcp_conn *tc; struct mbuf *mb; struct sip *sip; uint32_t ka_interval; bool established; enum sip_transp tp; struct websock_conn *websock_conn; }; struct sip_connqent { struct le le; struct mbuf *mb; struct sip_connqent **qentp; sip_transp_h *transph; void *arg; }; static uint8_t crlfcrlf[4] = {0x0d, 0x0a, 0x0d, 0x0a}; static void internal_transport_handler(int err, void *arg) { (void)err; (void)arg; } static void transp_destructor(void *arg) { struct sip_transport *transp = arg; if (transp->tp == SIP_TRANSP_UDP) udp_handler_set(transp->sock, NULL, NULL); list_unlink(&transp->le); mem_deref(transp->sock); mem_deref(transp->tls); mem_deref(transp->http_cli); mem_deref(transp->http_sock); } static void conn_destructor(void *arg) { struct sip_conn *conn = arg; tmr_cancel(&conn->tmr_ka); tmr_cancel(&conn->tmr); list_flush(&conn->kal); list_flush(&conn->ql); hash_unlink(&conn->he); mem_deref(conn->sc); mem_deref(conn->tc); mem_deref(conn->mb); mem_deref(conn->websock_conn); } static void qent_destructor(void *arg) { struct sip_connqent *qent = arg; if (qent->qentp) *qent->qentp = NULL; list_unlink(&qent->le); mem_deref(qent->mb); } static const struct sip_transport *transp_find(struct sip *sip, enum sip_transp tp, int af, const struct sa *dst) { struct le *le; (void)dst; for (le = sip->transpl.head; le; le = le->next) { const struct sip_transport *transp = le->data; if (transp->tp != tp) continue; if (af != AF_UNSPEC && sa_af(&transp->laddr) != af) continue; return transp; } return NULL; } static struct sip_conn *conn_find(struct sip *sip, const struct sa *paddr, bool secure) { struct le *le; le = list_head(hash_list(sip->ht_conn, sa_hash(paddr, SA_ALL))); for (; le; le = le->next) { struct sip_conn *conn = le->data; if (!secure != (conn->sc == NULL)) continue; if (!sa_cmp(&conn->paddr, paddr, SA_ALL)) continue; return conn; } return NULL; } static struct sip_conn *ws_conn_find(struct sip *sip, const struct sa *paddr, enum sip_transp tp) { struct le *le; le = list_head(hash_list(sip->ht_conn, sa_hash(paddr, SA_ALL))); for (; le; le = le->next) { struct sip_conn *conn = le->data; /* if (tp != conn->tp) continue; */ if (!sa_cmp(&conn->paddr, paddr, SA_ALL)) continue; return conn; } return NULL; } static void conn_close(struct sip_conn *conn, int err) { struct le *le; conn->websock_conn = mem_deref(conn->websock_conn); conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); tmr_cancel(&conn->tmr_ka); tmr_cancel(&conn->tmr); hash_unlink(&conn->he); le = list_head(&conn->ql); while (le) { struct sip_connqent *qent = le->data; le = le->next; if (qent->qentp) { *qent->qentp = NULL; qent->qentp = NULL; } qent->transph(err, qent->arg); list_unlink(&qent->le); mem_deref(qent); } sip_keepalive_signal(&conn->kal, err); } static void conn_tmr_handler(void *arg) { struct sip_conn *conn = arg; conn_close(conn, ETIMEDOUT); mem_deref(conn); } static void conn_keepalive_handler(void *arg) { struct sip_conn *conn = arg; struct mbuf mb; int err; mb.buf = crlfcrlf; mb.size = sizeof(crlfcrlf); mb.pos = 0; mb.end = 4; err = tcp_send(conn->tc, &mb); if (err) { conn_close(conn, err); mem_deref(conn); return; } tmr_start(&conn->tmr, TCP_KEEPALIVE_TIMEOUT * 1000, conn_tmr_handler, conn); tmr_start(&conn->tmr_ka, sip_keepalive_wait(conn->ka_interval), conn_keepalive_handler, conn); } static void sip_recv(struct sip *sip, const struct sip_msg *msg, size_t start) { struct le *le = sip->lsnrl.head; if (sip->traceh) { sip->traceh(false, msg->tp, &msg->src, &msg->dst, msg->mb->buf + start, msg->mb->end - start, sip->arg); } while (le) { struct sip_lsnr *lsnr = le->data; le = le->next; if (msg->req != lsnr->req) continue; if (lsnr->msgh(msg, lsnr->arg)) return; } if (msg->req) { (void)re_fprintf(stderr, "unhandeled request from %J: %r %r\n", &msg->src, &msg->met, &msg->ruri); if (!pl_strcmp(&msg->met, "CANCEL")) (void)sip_reply(sip, msg, 481, "Transaction Does Not Exist"); else (void)sip_reply(sip, msg, 501, "Not Implemented"); } else { (void)re_fprintf(stderr, "unhandeled response from %J:" " %u %r (%r)\n", &msg->src, msg->scode, &msg->reason, &msg->cseq.met); } } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct sip_transport *transp = arg; struct stun_unknown_attr ua; struct stun_msg *stun_msg; struct sip_msg *msg; int err; if (mb->end <= 4) return; if (!stun_msg_decode(&stun_msg, mb, &ua)) { if (stun_msg_method(stun_msg) == STUN_METHOD_BINDING) { switch (stun_msg_class(stun_msg)) { case STUN_CLASS_REQUEST: (void)stun_reply(IPPROTO_UDP, transp->sock, src, 0, stun_msg, NULL, 0, false, 2, STUN_ATTR_XOR_MAPPED_ADDR, src, STUN_ATTR_SOFTWARE, transp->sip->software); break; default: (void)stun_ctrans_recv(transp->sip->stun, stun_msg, &ua); break; } } mem_deref(stun_msg); return; } err = sip_msg_decode(&msg, mb); if (err) { (void)re_fprintf(stderr, "sip: msg decode err: %m\n", err); return; } msg->sock = mem_ref(transp->sock); msg->src = *src; msg->dst = transp->laddr; msg->tp = SIP_TRANSP_UDP; sip_recv(transp->sip, msg, 0); mem_deref(msg); } static void tcp_recv_handler(struct mbuf *mb, void *arg) { struct sip_conn *conn = arg; size_t pos; int err = 0; if (conn->mb) { pos = conn->mb->pos; conn->mb->pos = conn->mb->end; err = mbuf_write_mem(conn->mb, mbuf_buf(mb),mbuf_get_left(mb)); if (err) goto out; conn->mb->pos = pos; if (mbuf_get_left(conn->mb) > TCP_BUFSIZE_MAX) { err = EOVERFLOW; goto out; } } else { conn->mb = mem_ref(mb); } for (;;) { struct sip_msg *msg; uint32_t clen; size_t end; if (mbuf_get_left(conn->mb) < 2) break; if (!memcmp(mbuf_buf(conn->mb), "\r\n", 2)) { tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); conn->mb->pos += 2; if (mbuf_get_left(conn->mb) >= 2 && !memcmp(mbuf_buf(conn->mb), "\r\n", 2)) { struct mbuf mbr; conn->mb->pos += 2; mbr.buf = crlfcrlf; mbr.size = sizeof(crlfcrlf); mbr.pos = 0; mbr.end = 2; err = tcp_send(conn->tc, &mbr); if (err) break; } if (mbuf_get_left(conn->mb)) continue; conn->mb = mem_deref(conn->mb); break; } pos = conn->mb->pos; err = sip_msg_decode(&msg, conn->mb); if (err) { if (err == ENODATA) err = 0; break; } if (!msg->clen.p) { mem_deref(msg); err = EBADMSG; break; } clen = pl_u32(&msg->clen); if (mbuf_get_left(conn->mb) < clen) { conn->mb->pos = pos; mem_deref(msg); break; } tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); end = conn->mb->end; msg->mb->end = msg->mb->pos + clen; msg->sock = mem_ref(conn); msg->src = conn->paddr; msg->dst = conn->laddr; msg->tp = conn->sc ? SIP_TRANSP_TLS : SIP_TRANSP_TCP; sip_recv(conn->sip, msg, 0); mem_deref(msg); if (end <= conn->mb->end) { conn->mb = mem_deref(conn->mb); break; } mb = mbuf_alloc(end - conn->mb->end); if (!mb) { err = ENOMEM; goto out; } (void)mbuf_write_mem(mb, &conn->mb->buf[conn->mb->end], end - conn->mb->end); mb->pos = 0; mem_deref(conn->mb); conn->mb = mb; } out: if (err) { conn_close(conn, err); mem_deref(conn); } } static void trace_send(struct sip *sip, enum sip_transp tp, void *sock, const struct sa *dst, struct mbuf *mb) { struct sa src; struct sip_conn *conn; if (sip->traceh) { switch (tp) { case SIP_TRANSP_UDP: if (udp_local_get(sock, &src)) sa_init(&src, sa_af(dst)); break; case SIP_TRANSP_TCP: case SIP_TRANSP_TLS: case SIP_TRANSP_WS: case SIP_TRANSP_WSS: conn = sock; src = conn->laddr; break; default: return; } sip->traceh(true, tp, &src, dst, mbuf_buf(mb), mbuf_get_left(mb), sip->arg); } } static void tcp_estab_handler(void *arg) { struct sip_conn *conn = arg; struct le *le; int err; #ifdef WIN32 tcp_conn_local_get(conn->tc, &conn->laddr); #endif conn->established = true; le = list_head(&conn->ql); while (le) { struct sip_connqent *qent = le->data; le = le->next; if (qent->qentp) { *qent->qentp = NULL; qent->qentp = NULL; } trace_send(conn->sip, conn->sc ? SIP_TRANSP_TLS : SIP_TRANSP_TCP, conn, &conn->paddr, qent->mb); err = tcp_send(conn->tc, qent->mb); if (err) qent->transph(err, qent->arg); list_unlink(&qent->le); mem_deref(qent); } } static void tcp_close_handler(int err, void *arg) { struct sip_conn *conn = arg; conn_close(conn, err ? err : ECONNRESET); mem_deref(conn); } static void tcp_connect_handler(const struct sa *paddr, void *arg) { struct sip_transport *transp = arg; struct sip_conn *conn; int err; conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) { err = ENOMEM; goto out; } hash_append(transp->sip->ht_conn, sa_hash(paddr, SA_ALL), &conn->he, conn); conn->paddr = *paddr; conn->sip = transp->sip; err = tcp_accept(&conn->tc, transp->sock, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, conn); if (err) goto out; err = tcp_conn_local_get(conn->tc, &conn->laddr); if (err) goto out; #ifdef USE_TLS if (transp->tls) { err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0); if (err) goto out; } #endif conn->tp = transp->tls ? SIP_TRANSP_TLS : SIP_TRANSP_TCP; tmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT * 1000, conn_tmr_handler, conn); out: if (err) { tcp_reject(transp->sock); mem_deref(conn); } } static int conn_send(struct sip_connqent **qentp, struct sip *sip, bool secure, const struct sa *dst, struct mbuf *mb, sip_transp_h *transph, void *arg) { struct sip_conn *conn, *new_conn = NULL; struct sip_connqent *qent; int err = 0; conn = conn_find(sip, dst, secure); if (conn) { if (!conn->established) goto enqueue; trace_send(sip, secure ? SIP_TRANSP_TLS : SIP_TRANSP_TCP, conn, dst, mb); return tcp_send(conn->tc, mb); } new_conn = conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return ENOMEM; hash_append(sip->ht_conn, sa_hash(dst, SA_ALL), &conn->he, conn); conn->paddr = *dst; conn->sip = sip; conn->tp = secure ? SIP_TRANSP_TLS : SIP_TRANSP_TCP; err = tcp_connect(&conn->tc, dst, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, conn); if (err) goto out; err = tcp_conn_local_get(conn->tc, &conn->laddr); if (err) goto out; #ifdef USE_TLS if (secure) { const struct sip_transport *transp; transp = transp_find(sip, SIP_TRANSP_TLS, sa_af(dst), dst); if (!transp || !transp->tls) { err = EPROTONOSUPPORT; goto out; } err = tls_start_tcp(&conn->sc, transp->tls, conn->tc, 0); if (err) goto out; } #endif tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); enqueue: qent = mem_zalloc(sizeof(*qent), qent_destructor); if (!qent) { err = ENOMEM; goto out; } list_append(&conn->ql, &qent->le, qent); qent->mb = mem_ref(mb); qent->transph = transph ? transph : internal_transport_handler; qent->arg = arg; if (qentp) { qent->qentp = qentp; *qentp = qent; } out: if (err) mem_deref(new_conn); return err; } static void websock_estab_handler(void *arg) { struct sip_conn *conn = arg; struct le *le; int err; re_printf("<%p> %s websock established to %J\n", conn, sip_transp_name(conn->tp), &conn->paddr); conn->established = true; err = tcp_conn_local_get(websock_tcp(conn->websock_conn), &conn->laddr); if (err) return; le = list_head(&conn->ql); while (le) { struct sip_connqent *qent = le->data; le = le->next; if (qent->qentp) { *qent->qentp = NULL; qent->qentp = NULL; } trace_send(conn->sip, conn->tp, conn, &conn->paddr, qent->mb); re_printf("--> send\n"); err = websock_send(conn->websock_conn, WEBSOCK_BIN, "%b", mbuf_buf(qent->mb), mbuf_get_left(qent->mb)); if (err) qent->transph(err, qent->arg); list_unlink(&qent->le); mem_deref(qent); } } static void websock_recv_handler(const struct websock_hdr *hdr, struct mbuf *mb, void *arg) { struct sip_conn *conn = arg; struct sip_msg *msg; size_t start; int err; #if 0 re_printf( "~ ~ ~ ~ ~ websock receive: ~ ~ ~ ~ ~\n" "\x1b[32m" "%b" "\x1b[;m\t\n" "~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~\n" , mbuf_buf(mb), mbuf_get_left(mb)); #endif if (mb->end <= 4) return; start = mb->pos; err = sip_msg_decode(&msg, mb); if (err) { (void)re_fprintf(stderr, "sip: msg decode err: %m\n", err); return; } msg->sock = mem_ref(conn); msg->src = conn->paddr; msg->dst = conn->laddr; msg->tp = conn->tp; sip_recv(conn->sip, msg, start); mem_deref(msg); } static void websock_close_handler(int err, void *arg) { struct sip_conn *conn = arg; re_printf("sip: websock connection closed (%m)\n", err); conn_close(conn, err ? err : ECONNRESET); mem_deref(conn); } static int ws_conn_send(struct sip_connqent **qentp, struct sip *sip, bool secure, const struct sa *dst, struct mbuf *mb, sip_transp_h *transph, void *arg) { struct sip_conn *conn, *new_conn = NULL; struct sip_connqent *qent; struct sip_transport *transp; enum sip_transp tp; const char *prefix; char ws_uri[256]; int err = 0; if (secure) { prefix = "wss"; tp = SIP_TRANSP_WSS; } else { prefix = "ws"; tp = SIP_TRANSP_WS; } conn = ws_conn_find(sip, dst, tp); if (conn) { if (!conn->established) goto enqueue; trace_send(sip, secure ? SIP_TRANSP_WSS : SIP_TRANSP_WS, conn, dst, mb); return websock_send(conn->websock_conn, WEBSOCK_BIN, "%b", mbuf_buf(mb), mbuf_get_left(mb)); } transp = (struct sip_transport *)transp_find(sip, tp, sa_af(dst), dst); if (!transp) { err = EPROTONOSUPPORT; goto out; } new_conn = conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return ENOMEM; hash_append(sip->ht_conn, sa_hash(dst, SA_ALL), &conn->he, conn); conn->paddr = *dst; conn->sip = sip; conn->tp = tp; /* TODO: how to select ports of outbound SIP/WS proxy ? * TODO: http url path "test" is temp, add config */ /* Use port if specified, otherwise use default HTTP/HTTPS ports */ if (sa_port(dst)) { if (re_snprintf(ws_uri, sizeof(ws_uri), "%s://%J/", prefix, dst) < 0) { err = ENOMEM; goto out; } } else { if (re_snprintf(ws_uri, sizeof(ws_uri), "%s://%j/", prefix, dst) < 0) { err = ENOMEM; goto out; } } if (!transp->http_cli) { err = http_client_alloc(&transp->http_cli, sip->dnsc); if (err) { re_fprintf(stderr, "transp: could not create" " http client (%m)\n", err); goto out; } } re_printf("websock: connecting to '%s'\n", ws_uri); err = websock_connect(&conn->websock_conn, sip->websock, transp->http_cli, ws_uri, 15000, websock_estab_handler, websock_recv_handler, websock_close_handler, conn, "Sec-WebSocket-Protocol: sip\r\n"); if (err) { re_printf("websock_connect: %m\n", err); goto out; } tmr_start(&conn->tmr, TCP_IDLE_TIMEOUT * 1000, conn_tmr_handler, conn); enqueue: qent = mem_zalloc(sizeof(*qent), qent_destructor); if (!qent) { err = ENOMEM; goto out; } list_append(&conn->ql, &qent->le, qent); qent->mb = mem_ref(mb); qent->transph = transph ? transph : internal_transport_handler; qent->arg = arg; if (qentp) { qent->qentp = qentp; *qentp = qent; } out: if (err) mem_deref(new_conn); return err; } int sip_transp_init(struct sip *sip, uint32_t sz) { return hash_alloc(&sip->ht_conn, sz); } static void http_req_handler(struct http_conn *hc, const struct http_msg *msg, void *arg) { struct sip_transport *transp = arg; struct sip_conn *conn = NULL; const struct sa *paddr; const struct http_hdr *hdr; int err; paddr = http_conn_peer(hc); re_printf("http request from %J\n", paddr); hdr = http_msg_hdr(msg, HTTP_HDR_SEC_WEBSOCKET_PROTOCOL); if (!hdr) { re_printf("sip: missing Sec-WebSocket-Protocol header\n"); err = EPROTO; goto out; } if (0 != pl_strcasecmp(&hdr->val, "sip")) { re_printf("sip: unknown Sec-WebSocket-Protocol '%r'\n", &hdr->val); err = EPROTO; goto out; } conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) { err = ENOMEM; goto out; } hash_append(transp->sip->ht_conn, sa_hash(paddr, SA_ALL), &conn->he, conn); conn->paddr = *paddr; conn->sip = transp->sip; conn->tp = transp->tp; err = websock_accept(&conn->websock_conn, transp->sip->websock, hc, msg, 15000, websock_recv_handler, websock_close_handler, conn); if (err) goto out; err = tcp_conn_local_get(websock_tcp(conn->websock_conn), &conn->laddr); if (err) goto out; tmr_start(&conn->tmr, TCP_ACCEPT_TIMEOUT * 1000, conn_tmr_handler, conn); out: if (err) { (void)http_reply(hc, 500, "Server Error", NULL); mem_deref(conn); } } /** * Add a SIP transport * * @param sip SIP stack instance * @param tp SIP Transport * @param laddr Local network address * @param ... Optional transport parameters such as TLS context * * @return 0 if success, otherwise errorcode */ int sip_transp_add(struct sip *sip, enum sip_transp tp, const struct sa *laddr, ...) { struct sip_transport *transp; struct tls *tls; va_list ap; int err = 0; if (!sip || !laddr || !sa_isset(laddr, SA_ADDR)) return EINVAL; transp = mem_zalloc(sizeof(*transp), transp_destructor); if (!transp) return ENOMEM; list_append(&sip->transpl, &transp->le, transp); transp->sip = sip; transp->tp = tp; va_start(ap, laddr); switch (tp) { case SIP_TRANSP_UDP: err = udp_listen((struct udp_sock **)&transp->sock, laddr, udp_recv_handler, transp); if (err) break; err = udp_local_get(transp->sock, &transp->laddr); break; case SIP_TRANSP_TLS: tls = va_arg(ap, struct tls *); if (!tls) { err = EINVAL; break; } transp->tls = mem_ref(tls); /*@fallthrough@*/ case SIP_TRANSP_TCP: err = tcp_listen((struct tcp_sock **)&transp->sock, laddr, tcp_connect_handler, transp); if (err) break; err = tcp_sock_local_get(transp->sock, &transp->laddr); break; default: err = EPROTONOSUPPORT; break; } va_end(ap); if (err) mem_deref(transp); return err; } int sip_transp_add_websock(struct sip *sip, enum sip_transp tp, const struct sa *laddr, bool server, const char *cert) { struct sip_transport *transp; bool secure = tp == SIP_TRANSP_WSS; int err = 0; if (!sip || !laddr || !sa_isset(laddr, SA_ADDR)) return EINVAL; transp = mem_zalloc(sizeof(*transp), transp_destructor); if (!transp) return ENOMEM; list_append(&sip->transpl, &transp->le, transp); transp->sip = sip; transp->tp = tp; if (server) { if (secure) { err = https_listen(&transp->http_sock, laddr, cert, http_req_handler, transp); if (err) { re_fprintf(stderr, "websock: https_listen" " error (%m)\n", err); goto out; } } else { err = http_listen(&transp->http_sock, laddr, http_req_handler, transp); if (err) { re_fprintf(stderr, "websock: http_listen" " error (%m)\n", err); goto out; } } err = tcp_sock_local_get(http_sock_tcp(transp->http_sock), &transp->laddr); if (err) goto out; } else { transp->laddr = *laddr; sa_set_port(&transp->laddr, 9); } out: if (err) mem_deref(transp); return err; } /** * Flush all transports of a SIP stack instance * * @param sip SIP stack instance */ void sip_transp_flush(struct sip *sip) { if (!sip) return; hash_flush(sip->ht_conn); list_flush(&sip->transpl); } int sip_transp_send(struct sip_connqent **qentp, struct sip *sip, void *sock, enum sip_transp tp, const struct sa *dst, struct mbuf *mb, sip_transp_h *transph, void *arg) { const struct sip_transport *transp; struct sip_conn *conn; bool secure = false; int err; if (!sip || !dst || !mb) return EINVAL; switch (tp) { case SIP_TRANSP_UDP: if (!sock) { transp = transp_find(sip, tp, sa_af(dst), dst); if (!transp) return EPROTONOSUPPORT; sock = transp->sock; } trace_send(sip, tp, sock, dst, mb); err = udp_send(sock, dst, mb); break; case SIP_TRANSP_TLS: secure = true; /*@fallthrough@*/ case SIP_TRANSP_TCP: conn = sock; if (conn && conn->tc) { trace_send(sip, tp, conn, dst, mb); err = tcp_send(conn->tc, mb); } else err = conn_send(qentp, sip, secure, dst, mb, transph, arg); break; case SIP_TRANSP_WSS: secure = true; /*@fallthrough@*/ case SIP_TRANSP_WS: conn = sock; if (conn && conn->websock_conn) { trace_send(sip, tp, conn, dst, mb); err = websock_send(conn->websock_conn, WEBSOCK_BIN, "%b", mbuf_buf(mb), mbuf_get_left(mb)); if (err) { re_fprintf(stderr, "websock_send failed" " (%m)\n", err); } } else { err = ws_conn_send(qentp, sip, secure, dst, mb, transph, arg); if (err) { re_fprintf(stderr, "ws_conn_send failed" " (%m)\n", err); } } break; default: err = EPROTONOSUPPORT; break; } return err; } int sip_transp_laddr(struct sip *sip, struct sa *laddr, enum sip_transp tp, const struct sa *dst) { const struct sip_transport *transp; if (!sip || !laddr) return EINVAL; transp = transp_find(sip, tp, sa_af(dst), dst); if (!transp) return EPROTONOSUPPORT; *laddr = transp->laddr; return 0; } bool sip_transp_supported(struct sip *sip, enum sip_transp tp, int af) { if (!sip) return false; return transp_find(sip, tp, af, NULL) != NULL; } /** * Check if network address is part of SIP transports * * @param sip SIP stack instance * @param tp SIP transport * @param laddr Local network address to check * * @return True if part of SIP transports, otherwise false */ bool sip_transp_isladdr(const struct sip *sip, enum sip_transp tp, const struct sa *laddr) { struct le *le; if (!sip || !laddr) return false; for (le=sip->transpl.head; le; le=le->next) { const struct sip_transport *transp = le->data; if (tp != SIP_TRANSP_NONE && transp->tp != tp) continue; if (!sa_cmp(&transp->laddr, laddr, SA_ALL)) continue; return true; } return false; } /** * Get the name of a given SIP Transport * * @param tp SIP Transport * * @return Name of the corresponding SIP Transport */ const char *sip_transp_name(enum sip_transp tp) { switch (tp) { case SIP_TRANSP_UDP: return "UDP"; case SIP_TRANSP_TCP: return "TCP"; case SIP_TRANSP_TLS: return "TLS"; case SIP_TRANSP_WS: return "WS"; case SIP_TRANSP_WSS: return "WSS"; default: return "???"; } } const char *sip_transp_srvid(enum sip_transp tp) { switch (tp) { case SIP_TRANSP_UDP: return "_sip._udp"; case SIP_TRANSP_TCP: return "_sip._tcp"; case SIP_TRANSP_TLS: return "_sips._tcp"; default: return "???"; } } /** * Get the transport parameters for a given SIP Transport * * @param tp SIP Transport * * @return Transport parameters of the corresponding SIP Transport */ const char *sip_transp_param(enum sip_transp tp) { switch (tp) { case SIP_TRANSP_UDP: return ""; case SIP_TRANSP_TCP: return ";transport=tcp"; case SIP_TRANSP_TLS: return ";transport=tls"; case SIP_TRANSP_WS: return ";transport=ws"; case SIP_TRANSP_WSS: return ";transport=wss"; default: return ""; } } bool sip_transp_reliable(enum sip_transp tp) { switch (tp) { case SIP_TRANSP_UDP: return false; case SIP_TRANSP_TCP: return true; case SIP_TRANSP_TLS: return true; case SIP_TRANSP_WS: return true; case SIP_TRANSP_WSS: return true; default: return false; } } /** * Get the default port number for a given SIP Transport * * @param tp SIP Transport * @param port Port number * * @return Corresponding port number */ uint16_t sip_transp_port(enum sip_transp tp, uint16_t port) { if (port) return port; switch (tp) { case SIP_TRANSP_UDP: return SIP_PORT; case SIP_TRANSP_TCP: return SIP_PORT; case SIP_TRANSP_TLS: return SIP_PORT_TLS; case SIP_TRANSP_WS: return 80; case SIP_TRANSP_WSS: return 443; default: return 0; } } static bool debug_handler(struct le *le, void *arg) { const struct sip_transport *transp = le->data; struct re_printf *pf = arg; (void)re_hprintf(pf, " %J (%s)\n", &transp->laddr, sip_transp_name(transp->tp)); return false; } static bool conn_debug_handler(struct le *le, void *arg) { struct sip_conn *conn = le->data; struct re_printf *pf = arg; (void)re_hprintf(pf, " [%u] %5s %J --> %J (%s)\n", mem_nrefs(conn), sip_transp_name(conn->tp), &conn->laddr, &conn->paddr, conn->established ? "Established" : "..." ); return false; } int sip_transp_debug(struct re_printf *pf, const struct sip *sip) { int err; err = re_hprintf(pf, "transports:\n"); list_apply(&sip->transpl, true, debug_handler, pf); err |= re_hprintf(pf, "connections:\n"); hash_apply(sip->ht_conn, conn_debug_handler, pf); return err; } /** * Get the TCP Connection from a SIP Message * * @param msg SIP Message * * @return TCP Connection if reliable transport, otherwise NULL */ struct tcp_conn *sip_msg_tcpconn(const struct sip_msg *msg) { if (!msg || !msg->sock) return NULL; switch (msg->tp) { case SIP_TRANSP_TCP: case SIP_TRANSP_TLS: return ((struct sip_conn *)msg->sock)->tc; case SIP_TRANSP_WS: case SIP_TRANSP_WSS: { struct sip_conn *conn = msg->sock; return websock_tcp(conn->websock_conn); } default: return NULL; } } int sip_keepalive_tcp(struct sip_keepalive *ka, struct sip_conn *conn, uint32_t interval) { if (!ka || !conn) return EINVAL; if (!conn->tc || !conn->established) return ENOTCONN; list_append(&conn->kal, &ka->le, ka); if (!tmr_isrunning(&conn->tmr_ka)) { interval = MAX(interval ? interval : TCP_KEEPALIVE_INTVAL, TCP_KEEPALIVE_TIMEOUT * 2); conn->ka_interval = interval; tmr_start(&conn->tmr_ka, sip_keepalive_wait(conn->ka_interval), conn_keepalive_handler, conn); } return 0; } re-1.1.0/src/sip/via.c000066400000000000000000000032361373627245400144450ustar00rootroot00000000000000/** * @file via.c SIP Via decode * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include static int decode_hostport(const struct pl *hostport, struct pl *host, struct pl *port) { /* Try IPv6 first */ if (!re_regex(hostport->p, hostport->l, "\\[[0-9a-f:]+\\][:]*[0-9]*", host, NULL, port)) return 0; /* Then non-IPv6 host */ return re_regex(hostport->p, hostport->l, "[^:]+[:]*[0-9]*", host, NULL, port); } /** * Decode a pointer-length string into a SIP Via header * * @param via SIP Via header * @param pl Pointer-length string * * @return 0 for success, otherwise errorcode */ int sip_via_decode(struct sip_via *via, const struct pl *pl) { struct pl transp, host, port; int err; if (!via || !pl) return EINVAL; err = re_regex(pl->p, pl->l, "SIP[ \t\r\n]*/[ \t\r\n]*2.0[ \t\r\n]*/[ \t\r\n]*" "[A-Z]+[ \t\r\n]*[^; \t\r\n]+[ \t\r\n]*[^]*", NULL, NULL, NULL, NULL, &transp, NULL, &via->sentby, NULL, &via->params); if (err) return err; if (!pl_strcmp(&transp, "TCP")) via->tp = SIP_TRANSP_TCP; else if (!pl_strcmp(&transp, "TLS")) via->tp = SIP_TRANSP_TLS; else if (!pl_strcmp(&transp, "UDP")) via->tp = SIP_TRANSP_UDP; else via->tp = SIP_TRANSP_NONE; err = decode_hostport(&via->sentby, &host, &port); if (err) return err; sa_init(&via->addr, AF_INET); (void)sa_set(&via->addr, &host, 0); if (pl_isset(&port)) sa_set_port(&via->addr, pl_u32(&port)); via->val = *pl; return msg_param_decode(&via->params, "branch", &via->branch); } re-1.1.0/src/sipevent/000077500000000000000000000000001373627245400145605ustar00rootroot00000000000000re-1.1.0/src/sipevent/listen.c000066400000000000000000000160051373627245400162240ustar00rootroot00000000000000/** * @file sipevent/listen.c SIP Event Listen * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipevent.h" struct subcmp { const struct sipevent_event *evt; const struct sip_msg *msg; }; static void destructor(void *arg) { struct sipevent_sock *sock = arg; mem_deref(sock->lsnr); hash_flush(sock->ht_not); hash_flush(sock->ht_sub); mem_deref(sock->ht_not); mem_deref(sock->ht_sub); } static bool event_cmp(const struct sipevent_event *evt, const char *event, const char *id, int32_t refer_cseq) { if (pl_strcmp(&evt->event, event)) return false; if (!pl_isset(&evt->id) && !id) return true; if (!pl_isset(&evt->id)) return false; if (!id) { if (refer_cseq >= 0 && (int32_t)pl_u32(&evt->id) == refer_cseq) return true; return false; } if (pl_strcmp(&evt->id, id)) return false; return true; } static bool not_cmp_handler(struct le *le, void *arg) { const struct subcmp *cmp = arg; struct sipnot *not = le->data; return sip_dialog_cmp(not->dlg, cmp->msg) && event_cmp(cmp->evt, not->event, not->id, -1); } static bool sub_cmp_handler(struct le *le, void *arg) { const struct subcmp *cmp = arg; struct sipsub *sub = le->data; return sip_dialog_cmp(sub->dlg, cmp->msg) && (!cmp->evt || event_cmp(cmp->evt, sub->event, sub->id, sub->refer_cseq)); } static bool sub_cmp_half_handler(struct le *le, void *arg) { const struct subcmp *cmp = arg; struct sipsub *sub = le->data; return sip_dialog_cmp_half(sub->dlg, cmp->msg) && !sip_dialog_established(sub->dlg) && (!cmp->evt || event_cmp(cmp->evt, sub->event, sub->id, sub->refer_cseq)); } static struct sipnot *sipnot_find(struct sipevent_sock *sock, const struct sip_msg *msg, const struct sipevent_event *evt) { struct subcmp cmp; cmp.msg = msg; cmp.evt = evt; return list_ledata(hash_lookup(sock->ht_not, hash_joaat_pl(&msg->callid), not_cmp_handler, &cmp)); } struct sipsub *sipsub_find(struct sipevent_sock *sock, const struct sip_msg *msg, const struct sipevent_event *evt, bool full) { struct subcmp cmp; cmp.msg = msg; cmp.evt = evt; return list_ledata(hash_lookup(sock->ht_sub, hash_joaat_pl(&msg->callid), full ? sub_cmp_handler : sub_cmp_half_handler, &cmp)); } static void notify_handler(struct sipevent_sock *sock, const struct sip_msg *msg) { struct sipevent_substate state; struct sipevent_event event; struct sip *sip = sock->sip; const struct sip_hdr *hdr; struct sipsub *sub; uint32_t nrefs; char m[256]; int err; hdr = sip_msg_hdr(msg, SIP_HDR_EVENT); if (!hdr || sipevent_event_decode(&event, &hdr->val)) { (void)sip_reply(sip, msg, 489, "Bad Event"); return; } hdr = sip_msg_hdr(msg, SIP_HDR_SUBSCRIPTION_STATE); if (!hdr || sipevent_substate_decode(&state, &hdr->val)) { (void)sip_reply(sip, msg, 400,"Bad Subscription-State Header"); return; } sub = sipsub_find(sock, msg, &event, true); if (!sub) { sub = sipsub_find(sock, msg, &event, false); if (!sub) { (void)sip_reply(sip, msg, 481, "Subscription Does Not Exist"); return; } if (sub->forkh) { struct sipsub *fsub; err = sub->forkh(&fsub, sub, msg, sub->arg); if (err) { (void)sip_reply(sip, msg, 500, str_error(err, m, sizeof(m))); return; } sub = fsub; } else { err = sip_dialog_create(sub->dlg, msg); if (err) { (void)sip_reply(sip, msg, 500, str_error(err, m, sizeof(m))); return; } } } else { if (!sip_dialog_rseq_valid(sub->dlg, msg)) { (void)sip_reply(sip, msg, 500, "Bad Sequence"); return; } (void)sip_dialog_update(sub->dlg, msg); } if (sub->refer_cseq >= 0 && !sub->id && pl_isset(&event.id)) { err = pl_strdup(&sub->id, &event.id); if (err) { (void)sip_treply(NULL, sip, msg, 500, str_error(err, m, sizeof(m))); return; } } switch (state.state) { case SIPEVENT_ACTIVE: case SIPEVENT_PENDING: if (!sub->termconf) sub->subscribed = true; if (!sub->terminated && !sub->termwait && pl_isset(&state.expires)) sipsub_reschedule(sub, pl_u32(&state.expires) * 900); break; case SIPEVENT_TERMINATED: sub->subscribed = false; sub->termconf = true; break; } mem_ref(sub); sub->notifyh(sip, msg, sub->arg); nrefs = mem_nrefs(sub); mem_deref(sub); /* check if subscription was deref'd from notify handler */ if (nrefs == 1) return; if (state.state == SIPEVENT_TERMINATED) { if (!sub->terminated) { sub->termwait = false; sipsub_terminate(sub, 0, msg, &state); } else if (sub->termwait) { sub->termwait = false; tmr_cancel(&sub->tmr); mem_deref(sub); } } } static void subscribe_handler(struct sipevent_sock *sock, const struct sip_msg *msg) { struct sipevent_event event; struct sip *sip = sock->sip; const struct sip_hdr *hdr; struct sipnot *not; uint32_t expires; hdr = sip_msg_hdr(msg, SIP_HDR_EVENT); if (!hdr || sipevent_event_decode(&event, &hdr->val)) { (void)sip_reply(sip, msg, 400, "Bad Event Header"); return; } not = sipnot_find(sock, msg, &event); if (!not || not->terminated) { (void)sip_reply(sip, msg, 481, "Subscription Does Not Exist"); return; } if (pl_isset(&msg->expires)) expires = pl_u32(&msg->expires); else expires = not->expires_dfl; if (expires > 0 && expires < not->expires_min) { (void)sip_replyf(sip, msg, 423, "Interval Too Brief", "Min-Expires: %u\r\n" "Content-Length: 0\r\n" "\r\n", not->expires_min); return; } if (!sip_dialog_rseq_valid(not->dlg, msg)) { (void)sip_reply(sip, msg, 500, "Bad Sequence"); return; } (void)sip_dialog_update(not->dlg, msg); sipnot_refresh(not, expires); (void)sipnot_reply(not, msg, 200, "OK"); (void)sipnot_notify(not); } static bool request_handler(const struct sip_msg *msg, void *arg) { struct sipevent_sock *sock = arg; if (!pl_strcmp(&msg->met, "SUBSCRIBE")) { if (pl_isset(&msg->to.tag)) { subscribe_handler(sock, msg); return true; } return sock->subh ? sock->subh(msg, sock->arg) : false; } else if (!pl_strcmp(&msg->met, "NOTIFY")) { notify_handler(sock, msg); return true; } else { return false; } } int sipevent_listen(struct sipevent_sock **sockp, struct sip *sip, uint32_t htsize_not, uint32_t htsize_sub, sip_msg_h *subh, void *arg) { struct sipevent_sock *sock; int err; if (!sockp || !sip || !htsize_not || !htsize_sub) return EINVAL; sock = mem_zalloc(sizeof(*sock), destructor); if (!sock) return ENOMEM; err = sip_listen(&sock->lsnr, sip, true, request_handler, sock); if (err) goto out; err = hash_alloc(&sock->ht_not, htsize_not); if (err) goto out; err = hash_alloc(&sock->ht_sub, htsize_sub); if (err) goto out; sock->sip = sip; sock->subh = subh; sock->arg = arg; out: if (err) mem_deref(sock); else *sockp = sock; return err; } re-1.1.0/src/sipevent/mod.mk000066400000000000000000000002311373627245400156640ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sipevent/listen.c SRCS += sipevent/msg.c SRCS += sipevent/notify.c SRCS += sipevent/subscribe.c re-1.1.0/src/sipevent/msg.c000066400000000000000000000052651373627245400155220ustar00rootroot00000000000000/** * @file sipevent/msg.c SIP event messages * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include int sipevent_event_decode(struct sipevent_event *se, const struct pl *pl) { struct pl param; int err; if (!se || !pl) return EINVAL; err = re_regex(pl->p, pl->l, "[^; \t\r\n]+[ \t\r\n]*[^]*", &se->event, NULL, &se->params); if (err) return EBADMSG; if (!msg_param_decode(&se->params, "id", ¶m)) se->id = param; else se->id = pl_null; return 0; } int sipevent_substate_decode(struct sipevent_substate *ss, const struct pl *pl) { struct pl state, param; int err; if (!ss || !pl) return EINVAL; err = re_regex(pl->p, pl->l, "[a-z]+[ \t\r\n]*[^]*", &state, NULL, &ss->params); if (err) return EBADMSG; if (!pl_strcasecmp(&state, "active")) ss->state = SIPEVENT_ACTIVE; else if (!pl_strcasecmp(&state, "pending")) ss->state = SIPEVENT_PENDING; else if (!pl_strcasecmp(&state, "terminated")) ss->state = SIPEVENT_TERMINATED; else ss->state = -1; if (!msg_param_decode(&ss->params, "reason", ¶m)) { if (!pl_strcasecmp(¶m, "deactivated")) ss->reason = SIPEVENT_DEACTIVATED; else if (!pl_strcasecmp(¶m, "probation")) ss->reason = SIPEVENT_PROBATION; else if (!pl_strcasecmp(¶m, "rejected")) ss->reason = SIPEVENT_REJECTED; else if (!pl_strcasecmp(¶m, "timeout")) ss->reason = SIPEVENT_TIMEOUT; else if (!pl_strcasecmp(¶m, "giveup")) ss->reason = SIPEVENT_GIVEUP; else if (!pl_strcasecmp(¶m, "noresource")) ss->reason = SIPEVENT_NORESOURCE; else ss->reason = -1; } else { ss->reason = -1; } if (!msg_param_decode(&ss->params, "expires", ¶m)) ss->expires = param; else ss->expires = pl_null; if (!msg_param_decode(&ss->params, "retry-after", ¶m)) ss->retry_after = param; else ss->retry_after = pl_null; return 0; } const char *sipevent_substate_name(enum sipevent_subst state) { switch (state) { case SIPEVENT_ACTIVE: return "active"; case SIPEVENT_PENDING: return "pending"; case SIPEVENT_TERMINATED: return "terminated"; default: return "unknown"; } } const char *sipevent_reason_name(enum sipevent_reason reason) { switch (reason) { case SIPEVENT_DEACTIVATED: return "deactivated"; case SIPEVENT_PROBATION: return "probation"; case SIPEVENT_REJECTED: return "rejected"; case SIPEVENT_TIMEOUT: return "timeout"; case SIPEVENT_GIVEUP: return "giveup"; case SIPEVENT_NORESOURCE: return "noresource"; default: return "unknown"; } } re-1.1.0/src/sipevent/notify.c000066400000000000000000000215261373627245400162420ustar00rootroot00000000000000/** * @file notify.c SIP Event Notify * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sipevent.h" static int notify_request(struct sipnot *not, bool reset_ls); static void internal_close_handler(int err, const struct sip_msg *msg, void *arg) { (void)err; (void)msg; (void)arg; } static bool terminate(struct sipnot *not, enum sipevent_reason reason) { not->terminated = true; not->reason = reason; not->closeh = internal_close_handler; if (not->req) { mem_ref(not); return true; } if (not->subscribed && !notify_request(not, true)) { mem_ref(not); return true; } return false; } static void destructor(void *arg) { struct sipnot *not = arg; tmr_cancel(¬->tmr); if (!not->terminated) { if (terminate(not, SIPEVENT_DEACTIVATED)) return; } hash_unlink(¬->he); mem_deref(not->req); mem_deref(not->dlg); mem_deref(not->auth); mem_deref(not->mb); mem_deref(not->event); mem_deref(not->id); mem_deref(not->cuser); mem_deref(not->hdrs); mem_deref(not->ctype); mem_deref(not->sock); mem_deref(not->sip); } static void sipnot_terminate(struct sipnot *not, int err, const struct sip_msg *msg, enum sipevent_reason reason) { sipnot_close_h *closeh; void *arg; closeh = not->closeh; arg = not->arg; tmr_cancel(¬->tmr); (void)terminate(not, reason); closeh(err, msg, arg); } static void tmr_handler(void *arg) { struct sipnot *not = arg; if (not->terminated) return; sipnot_terminate(not, ETIMEDOUT, NULL, SIPEVENT_TIMEOUT); } void sipnot_refresh(struct sipnot *not, uint32_t expires) { not->expires = min(expires, not->expires_max); tmr_start(¬->tmr, not->expires * 1000, tmr_handler, not); } static void response_handler(int err, const struct sip_msg *msg, void *arg) { struct sipnot *not = arg; if (err) { if (err == ETIMEDOUT) not->subscribed = false; goto out; } if (sip_request_loops(¬->ls, msg->scode)) { not->subscribed = false; goto out; } if (msg->scode < 200) { return; } else if (msg->scode < 300) { (void)sip_dialog_update(not->dlg, msg); } else { switch (msg->scode) { case 401: case 407: err = sip_auth_authenticate(not->auth, msg); if (err) { err = (err == EAUTH) ? 0 : err; break; } err = notify_request(not, false); if (err) break; return; } not->subscribed = false; } out: if (not->termsent) { mem_deref(not); } else if (not->terminated) { if (!not->subscribed || notify_request(not, true)) mem_deref(not); } else if (!not->subscribed) { sipnot_terminate(not, err, msg, -1); } else if (not->notify_pending) { (void)notify_request(not, true); } } static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sip_contact contact; struct sipnot *not = arg; (void)dst; sip_contact_set(&contact, not->cuser, src, tp); return mbuf_printf(mb, "%H", sip_contact_print, &contact); } static int print_event(struct re_printf *pf, const struct sipnot *not) { if (not->id) return re_hprintf(pf, "%s;id=%s", not->event, not->id); else return re_hprintf(pf, "%s", not->event); } static int print_substate(struct re_printf *pf, const struct sipnot *not) { int err; if (not->terminated) { err = re_hprintf(pf, "terminated;reason=%s", sipevent_reason_name(not->reason)); if (not->retry_after) err |= re_hprintf(pf, ";retry-after=%u", not->retry_after); } else { uint32_t expires; expires = (uint32_t)(tmr_get_expire(¬->tmr) / 1000); err = re_hprintf(pf, "%s;expires=%u", sipevent_substate_name(not->substate), expires); } return err; } static int print_content(struct re_printf *pf, const struct sipnot *not) { if (!not->mb) return re_hprintf(pf, "Content-Length: 0\r\n" "\r\n"); else return re_hprintf(pf, "Content-Type: %s\r\n" "Content-Length: %zu\r\n" "\r\n" "%b", not->ctype, mbuf_get_left(not->mb), mbuf_buf(not->mb), mbuf_get_left(not->mb)); } static int notify_request(struct sipnot *not, bool reset_ls) { if (reset_ls) sip_loopstate_reset(¬->ls); if (not->terminated) not->termsent = true; not->notify_pending = false; return sip_drequestf(¬->req, not->sip, true, "NOTIFY", not->dlg, 0, not->auth, send_handler, response_handler, not, "Event: %H\r\n" "Subscription-State: %H\r\n" "%s" "%H", print_event, not, print_substate, not, not->hdrs, print_content, not); } int sipnot_notify(struct sipnot *not) { if (not->expires == 0) { return 0; } if (not->req) { not->notify_pending = true; return 0; } return notify_request(not, true); } int sipnot_reply(struct sipnot *not, const struct sip_msg *msg, uint16_t scode, const char *reason) { struct sip_contact contact; uint32_t expires; expires = (uint32_t)(tmr_get_expire(¬->tmr) / 1000); sip_contact_set(&contact, not->cuser, &msg->dst, msg->tp); return sip_treplyf(NULL, NULL, not->sip, msg, true, scode, reason, "%H" "Expires: %u\r\n" "Content-Length: 0\r\n" "\r\n", sip_contact_print, &contact, expires); } int sipevent_accept(struct sipnot **notp, struct sipevent_sock *sock, const struct sip_msg *msg, struct sip_dialog *dlg, const struct sipevent_event *event, uint16_t scode, const char *reason, uint32_t expires_min, uint32_t expires_dfl, uint32_t expires_max, const char *cuser, const char *ctype, sip_auth_h *authh, void *aarg, bool aref, sipnot_close_h *closeh, void *arg, const char *fmt, ...) { struct sipnot *not; uint32_t expires; int err; if (!notp || !sock || !msg || !scode || !reason || !expires_dfl || !expires_max || !cuser || !ctype || expires_dfl < expires_min) return EINVAL; not = mem_zalloc(sizeof(*not), destructor); if (!not) return ENOMEM; if (!pl_strcmp(&msg->met, "REFER")) { err = str_dup(¬->event, "refer"); if (err) goto out; err = re_sdprintf(¬->id, "%u", msg->cseq.num); if (err) goto out; } else { if (!event) { err = EINVAL; goto out; } err = pl_strdup(¬->event, &event->event); if (err) goto out; if (pl_isset(&event->id)) { err = pl_strdup(¬->id, &event->id); if (err) goto out; } } if (dlg) { not->dlg = mem_ref(dlg); } else { err = sip_dialog_accept(¬->dlg, msg); if (err) goto out; } hash_append(sock->ht_not, hash_joaat_str(sip_dialog_callid(not->dlg)), ¬->he, not); err = sip_auth_alloc(¬->auth, authh, aarg, aref); if (err) goto out; err = str_dup(¬->cuser, cuser); if (err) goto out; err = str_dup(¬->ctype, ctype); if (err) goto out; if (fmt) { va_list ap; va_start(ap, fmt); err = re_vsdprintf(¬->hdrs, fmt, ap); va_end(ap); if (err) goto out; } not->expires_min = expires_min; not->expires_dfl = expires_dfl; not->expires_max = expires_max; not->substate = SIPEVENT_PENDING; not->sock = mem_ref(sock); not->sip = mem_ref(sock->sip); not->closeh = closeh ? closeh : internal_close_handler; not->arg = arg; if (pl_isset(&msg->expires)) expires = pl_u32(&msg->expires); else expires = not->expires_dfl; sipnot_refresh(not, expires); err = sipnot_reply(not, msg, scode, reason); if (err) goto out; not->subscribed = true; out: if (err) mem_deref(not); else *notp = not; return err; } int sipevent_notify(struct sipnot *not, struct mbuf *mb, enum sipevent_subst state, enum sipevent_reason reason, uint32_t retry_after) { if (!not || not->terminated) return EINVAL; if (mb || state != SIPEVENT_TERMINATED) { mem_deref(not->mb); not->mb = mem_ref(mb); } switch (state) { case SIPEVENT_ACTIVE: case SIPEVENT_PENDING: not->substate = state; return sipnot_notify(not); case SIPEVENT_TERMINATED: tmr_cancel(¬->tmr); not->retry_after = retry_after; (void)terminate(not, reason); return 0; default: return EINVAL; } } int sipevent_notifyf(struct sipnot *not, struct mbuf **mbp, enum sipevent_subst state, enum sipevent_reason reason, uint32_t retry_after, const char *fmt, ...) { struct mbuf *mb; va_list ap; int err; if (!not || not->terminated || !fmt) return EINVAL; if (mbp && *mbp) return sipevent_notify(not, *mbp, state, reason, retry_after); mb = mbuf_alloc(1024); if (!mb) return ENOMEM; va_start(ap, fmt); err = mbuf_vprintf(mb, fmt, ap); va_end(ap); if (err) goto out; mb->pos = 0; err = sipevent_notify(not, mb, state, reason, retry_after); if (err) goto out; out: if (err || !mbp) mem_deref(mb); else *mbp = mb; return err; } re-1.1.0/src/sipevent/sipevent.h000066400000000000000000000035461373627245400165760ustar00rootroot00000000000000/** * @file sipevent.h SIP Event Private Interface * * Copyright (C) 2010 Creytiv.com */ /* Listener Socket */ struct sipevent_sock { struct sip_lsnr *lsnr; struct hash *ht_not; struct hash *ht_sub; struct sip *sip; sip_msg_h *subh; void *arg; }; /* Notifier */ struct sipnot { struct le he; struct sip_loopstate ls; struct tmr tmr; struct sipevent_sock *sock; struct sip_request *req; struct sip_dialog *dlg; struct sip_auth *auth; struct sip *sip; struct mbuf *mb; char *event; char *id; char *cuser; char *hdrs; char *ctype; sipnot_close_h *closeh; void *arg; uint32_t expires; uint32_t expires_min; uint32_t expires_dfl; uint32_t expires_max; uint32_t retry_after; enum sipevent_subst substate; enum sipevent_reason reason; bool notify_pending; bool subscribed; bool terminated; bool termsent; }; void sipnot_refresh(struct sipnot *not, uint32_t expires); int sipnot_notify(struct sipnot *not); int sipnot_reply(struct sipnot *not, const struct sip_msg *msg, uint16_t scode, const char *reason); /* Subscriber */ struct sipsub { struct le he; struct sip_loopstate ls; struct tmr tmr; struct sipevent_sock *sock; struct sip_request *req; struct sip_dialog *dlg; struct sip_auth *auth; struct sip *sip; char *event; char *id; char *cuser; char *hdrs; char *refer_hdrs; sipsub_fork_h *forkh; sipsub_notify_h *notifyh; sipsub_close_h *closeh; void *arg; int32_t refer_cseq; uint32_t expires; uint32_t failc; bool subscribed; bool terminated; bool termconf; bool termwait; bool refer; }; struct sipsub *sipsub_find(struct sipevent_sock *sock, const struct sip_msg *msg, const struct sipevent_event *evt, bool full); void sipsub_reschedule(struct sipsub *sub, uint64_t wait); void sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg, const struct sipevent_substate *substate); re-1.1.0/src/sipevent/subscribe.c000066400000000000000000000347771373627245400167270ustar00rootroot00000000000000/** * @file subscribe.c SIP Event Subscribe * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sipevent.h" enum { DEFAULT_EXPIRES = 3600, RESUB_FAIL_WAIT = 60000, RESUB_FAILC_MAX = 7, NOTIFY_TIMEOUT = 10000, }; static int request(struct sipsub *sub, bool reset_ls); static void internal_notify_handler(struct sip *sip, const struct sip_msg *msg, void *arg) { (void)arg; (void)sip_treply(NULL, sip, msg, 200, "OK"); } static void internal_close_handler(int err, const struct sip_msg *msg, const struct sipevent_substate *substate, void *arg) { (void)err; (void)msg; (void)substate; (void)arg; } static bool terminate(struct sipsub *sub) { sub->terminated = true; sub->forkh = NULL; sub->notifyh = internal_notify_handler; sub->closeh = internal_close_handler; if (sub->termwait) { mem_ref(sub); return true; } tmr_cancel(&sub->tmr); if (sub->req) { mem_ref(sub); return true; } if (sub->expires && sub->subscribed && !request(sub, true)) { mem_ref(sub); return true; } return false; } static void destructor(void *arg) { struct sipsub *sub = arg; if (!sub->terminated) { if (terminate(sub)) return; } tmr_cancel(&sub->tmr); hash_unlink(&sub->he); mem_deref(sub->req); mem_deref(sub->dlg); mem_deref(sub->auth); mem_deref(sub->event); mem_deref(sub->id); mem_deref(sub->cuser); mem_deref(sub->hdrs); mem_deref(sub->refer_hdrs); mem_deref(sub->sock); mem_deref(sub->sip); } static void notify_timeout_handler(void *arg) { struct sipsub *sub = arg; sub->termwait = false; if (sub->terminated) mem_deref(sub); else sipsub_terminate(sub, ETIMEDOUT, NULL, NULL); } static void tmr_handler(void *arg) { struct sipsub *sub = arg; int err; if (sub->req || sub->terminated) return; err = request(sub, true); if (err) { if (++sub->failc < RESUB_FAILC_MAX) { sipsub_reschedule(sub, RESUB_FAIL_WAIT); } else { sipsub_terminate(sub, err, NULL, NULL); } } } void sipsub_reschedule(struct sipsub *sub, uint64_t wait) { tmr_start(&sub->tmr, wait, tmr_handler, sub); } void sipsub_terminate(struct sipsub *sub, int err, const struct sip_msg *msg, const struct sipevent_substate *substate) { sipsub_close_h *closeh; void *arg; closeh = sub->closeh; arg = sub->arg; (void)terminate(sub); closeh(err, msg, substate, arg); } static void response_handler(int err, const struct sip_msg *msg, void *arg) { const struct sip_hdr *minexp; struct sipsub *sub = arg; if (err || sip_request_loops(&sub->ls, msg->scode)) goto out; if (msg->scode < 200) { return; } else if (msg->scode < 300) { uint32_t wait; if (sub->forkh) { struct sipsub *fsub; fsub = sipsub_find(sub->sock, msg, NULL, true); if (!fsub) { err = sub->forkh(&fsub, sub, msg, sub->arg); if (err) return; } else { (void)sip_dialog_update(fsub->dlg, msg); } sub = fsub; } else if (!sip_dialog_established(sub->dlg)) { err = sip_dialog_create(sub->dlg, msg); if (err) { sub->subscribed = false; goto out; } } else { /* Ignore 2xx responses for other dialogs * if forking is disabled */ if (!sip_dialog_cmp(sub->dlg, msg)) return; (void)sip_dialog_update(sub->dlg, msg); } if (!sub->termconf) sub->subscribed = true; sub->failc = 0; if (!sub->expires && !sub->termconf) { tmr_start(&sub->tmr, NOTIFY_TIMEOUT, notify_timeout_handler, sub); sub->termwait = true; return; } if (sub->terminated) goto out; if (sub->refer) { sub->refer = false; return; } if (pl_isset(&msg->expires)) wait = pl_u32(&msg->expires); else wait = sub->expires; sipsub_reschedule(sub, wait * 900); return; } else { if (sub->terminated && !sub->subscribed) goto out; switch (msg->scode) { case 401: case 407: err = sip_auth_authenticate(sub->auth, msg); if (err) { err = (err == EAUTH) ? 0 : err; break; } err = request(sub, false); if (err) break; return; case 403: sip_auth_reset(sub->auth); break; case 423: minexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES); if (!minexp || !pl_u32(&minexp->val) || !sub->expires) break; sub->expires = pl_u32(&minexp->val); err = request(sub, false); if (err) break; return; case 481: sub->subscribed = false; break; } } out: sub->refer = false; if (sub->terminated) { if (!sub->expires || !sub->subscribed || request(sub, true)) mem_deref(sub); } else { if (sub->subscribed && ++sub->failc < RESUB_FAILC_MAX) sipsub_reschedule(sub, RESUB_FAIL_WAIT); else sipsub_terminate(sub, err, msg, NULL); } } static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sip_contact contact; struct sipsub *sub = arg; (void)dst; sip_contact_set(&contact, sub->cuser, src, tp); return mbuf_printf(mb, "%H", sip_contact_print, &contact); } static int print_event(struct re_printf *pf, const struct sipsub *sub) { if (sub->id) return re_hprintf(pf, "%s;id=%s", sub->event, sub->id); else return re_hprintf(pf, "%s", sub->event); } static int request(struct sipsub *sub, bool reset_ls) { if (reset_ls) sip_loopstate_reset(&sub->ls); if (sub->refer) { sub->refer_cseq = sip_dialog_lseq(sub->dlg); return sip_drequestf(&sub->req, sub->sip, true, "REFER", sub->dlg, 0, sub->auth, send_handler, response_handler, sub, "%s" "Content-Length: 0\r\n" "\r\n", sub->refer_hdrs); } else { if (sub->terminated) sub->expires = 0; return sip_drequestf(&sub->req, sub->sip, true, "SUBSCRIBE", sub->dlg, 0, sub->auth, send_handler, response_handler, sub, "Event: %H\r\n" "Expires: %u\r\n" "%s" "Content-Length: 0\r\n" "\r\n", print_event, sub, sub->expires, sub->hdrs); } } static int sipsub_alloc(struct sipsub **subp, struct sipevent_sock *sock, bool refer, struct sip_dialog *dlg, const char *uri, const char *from_name, const char *from_uri, const char *event, const char *id, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, sip_auth_h *authh, void *aarg, bool aref, sipsub_fork_h *forkh, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, va_list ap) { struct sipsub *sub; int err; if (!subp || !sock || !event || !cuser) return EINVAL; if (!dlg && (!uri || !from_uri)) return EINVAL; sub = mem_zalloc(sizeof(*sub), destructor); if (!sub) return ENOMEM; if (dlg) { sub->dlg = mem_ref(dlg); } else { err = sip_dialog_alloc(&sub->dlg, uri, uri, from_name, from_uri, routev, routec); if (err) goto out; } hash_append(sock->ht_sub, hash_joaat_str(sip_dialog_callid(sub->dlg)), &sub->he, sub); err = sip_auth_alloc(&sub->auth, authh, aarg, aref); if (err) goto out; err = str_dup(&sub->event, event); if (err) goto out; if (id) { err = str_dup(&sub->id, id); if (err) goto out; } err = str_dup(&sub->cuser, cuser); if (err) goto out; if (fmt) { err = re_vsdprintf(refer ? &sub->refer_hdrs : &sub->hdrs, fmt, ap); if (err) goto out; } sub->refer_cseq = -1; sub->refer = refer; sub->sock = mem_ref(sock); sub->sip = mem_ref(sock->sip); sub->expires = expires; sub->forkh = forkh; sub->notifyh = notifyh ? notifyh : internal_notify_handler; sub->closeh = closeh ? closeh : internal_close_handler; sub->arg = arg; err = request(sub, true); if (err) goto out; out: if (err) mem_deref(sub); else *subp = sub; return err; } /** * Allocate a SIP subscriber client * * @param subp Pointer to allocated SIP subscriber client * @param sock SIP Event socket * @param uri SIP Request URI * @param from_name SIP From-header Name (optional) * @param from_uri SIP From-header URI * @param event SIP Event to subscribe to * @param id SIP Event ID (optional) * @param expires Subscription expires value * @param cuser Contact username or URI * @param routev Optional route vector * @param routec Number of routes * @param authh Authentication handler * @param aarg Authentication handler argument * @param aref True to ref argument * @param forkh Fork handler * @param notifyh Notify handler * @param closeh Close handler * @param arg Response handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipevent_subscribe(struct sipsub **subp, struct sipevent_sock *sock, const char *uri, const char *from_name, const char *from_uri, const char *event, const char *id, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, sip_auth_h *authh, void *aarg, bool aref, sipsub_fork_h *forkh, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = sipsub_alloc(subp, sock, false, NULL, uri, from_name, from_uri, event, id, expires, cuser, routev, routec, authh, aarg, aref, forkh, notifyh, closeh, arg, fmt, ap); va_end(ap); return err; } /** * Allocate a SIP subscriber client using an existing dialog * * @param subp Pointer to allocated SIP subscriber client * @param sock SIP Event socket * @param dlg Established SIP Dialog * @param event SIP Event to subscribe to * @param id SIP Event ID (optional) * @param expires Subscription expires value * @param cuser Contact username or URI * @param authh Authentication handler * @param aarg Authentication handler argument * @param aref True to ref argument * @param notifyh Notify handler * @param closeh Close handler * @param arg Response handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipevent_dsubscribe(struct sipsub **subp, struct sipevent_sock *sock, struct sip_dialog *dlg, const char *event, const char *id, uint32_t expires, const char *cuser, sip_auth_h *authh, void *aarg, bool aref, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = sipsub_alloc(subp, sock, false, dlg, NULL, NULL, NULL, event, id, expires, cuser, NULL, 0, authh, aarg, aref, NULL, notifyh, closeh, arg, fmt, ap); va_end(ap); return err; } /** * Allocate a SIP refer client * * @param subp Pointer to allocated SIP subscriber client * @param sock SIP Event socket * @param uri SIP Request URI * @param from_name SIP From-header Name (optional) * @param from_uri SIP From-header URI * @param cuser Contact username or URI * @param routev Optional route vector * @param routec Number of routes * @param authh Authentication handler * @param aarg Authentication handler argument * @param aref True to ref argument * @param forkh Fork handler * @param notifyh Notify handler * @param closeh Close handler * @param arg Response handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipevent_refer(struct sipsub **subp, struct sipevent_sock *sock, const char *uri, const char *from_name, const char *from_uri, const char *cuser, const char *routev[], uint32_t routec, sip_auth_h *authh, void *aarg, bool aref, sipsub_fork_h *forkh, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = sipsub_alloc(subp, sock, true, NULL, uri, from_name, from_uri, "refer", NULL, DEFAULT_EXPIRES, cuser, routev, routec, authh, aarg, aref, forkh, notifyh, closeh, arg, fmt, ap); va_end(ap); return err; } /** * Allocate a SIP refer client using an existing dialog * * @param subp Pointer to allocated SIP subscriber client * @param sock SIP Event socket * @param dlg Established SIP Dialog * @param cuser Contact username or URI * @param authh Authentication handler * @param aarg Authentication handler argument * @param aref True to ref argument * @param notifyh Notify handler * @param closeh Close handler * @param arg Response handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipevent_drefer(struct sipsub **subp, struct sipevent_sock *sock, struct sip_dialog *dlg, const char *cuser, sip_auth_h *authh, void *aarg, bool aref, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg, const char *fmt, ...) { va_list ap; int err; va_start(ap, fmt); err = sipsub_alloc(subp, sock, true, dlg, NULL, NULL, NULL, "refer", NULL, DEFAULT_EXPIRES, cuser, NULL, 0, authh, aarg, aref, NULL, notifyh, closeh, arg, fmt, ap); va_end(ap); return err; } int sipevent_fork(struct sipsub **subp, struct sipsub *osub, const struct sip_msg *msg, sip_auth_h *authh, void *aarg, bool aref, sipsub_notify_h *notifyh, sipsub_close_h *closeh, void *arg) { struct sipsub *sub; int err; if (!subp || !osub || !msg) return EINVAL; sub = mem_zalloc(sizeof(*sub), destructor); if (!sub) return ENOMEM; err = sip_dialog_fork(&sub->dlg, osub->dlg, msg); if (err) goto out; hash_append(osub->sock->ht_sub, hash_joaat_str(sip_dialog_callid(sub->dlg)), &sub->he, sub); err = sip_auth_alloc(&sub->auth, authh, aarg, aref); if (err) goto out; sub->event = mem_ref(osub->event); sub->id = mem_ref(osub->id); sub->cuser = mem_ref(osub->cuser); sub->hdrs = mem_ref(osub->hdrs); sub->refer = osub->refer; sub->sock = mem_ref(osub->sock); sub->sip = mem_ref(osub->sip); sub->expires = osub->expires; sub->forkh = NULL; sub->notifyh = notifyh ? notifyh : internal_notify_handler; sub->closeh = closeh ? closeh : internal_close_handler; sub->arg = arg; if (!sub->expires) { tmr_start(&sub->tmr, NOTIFY_TIMEOUT, notify_timeout_handler, sub); sub->termwait = true; } out: if (err) mem_deref(sub); else *subp = sub; return err; } re-1.1.0/src/sipreg/000077500000000000000000000000001373627245400142145ustar00rootroot00000000000000re-1.1.0/src/sipreg/mod.mk000066400000000000000000000001061373627245400153210ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sipreg/reg.c re-1.1.0/src/sipreg/reg.c000066400000000000000000000230021373627245400151320ustar00rootroot00000000000000/** * @file reg.c SIP Registration * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include enum { DEFAULT_EXPIRES = 3600, }; /** Defines a SIP Registration client */ struct sipreg { struct sip_loopstate ls; struct sa laddr; struct tmr tmr; struct sip *sip; struct sip_keepalive *ka; struct sip_request *req; struct sip_dialog *dlg; struct sip_auth *auth; struct mbuf *hdrs; char *cuser; sip_resp_h *resph; void *arg; uint32_t expires; uint32_t pexpires; uint32_t failc; uint32_t rwait; uint32_t wait; uint32_t fbregint; enum sip_transp tp; bool registered; bool terminated; char *params; int regid; }; static int request(struct sipreg *reg, bool reset_ls); static void dummy_handler(int err, const struct sip_msg *msg, void *arg) { (void)err; (void)msg; (void)arg; } static void destructor(void *arg) { struct sipreg *reg = arg; tmr_cancel(®->tmr); if (!reg->terminated) { reg->resph = dummy_handler; reg->terminated = true; if (reg->req) { mem_ref(reg); return; } if (reg->registered && !request(reg, true)) { mem_ref(reg); return; } } mem_deref(reg->ka); mem_deref(reg->dlg); mem_deref(reg->auth); mem_deref(reg->cuser); mem_deref(reg->sip); mem_deref(reg->hdrs); mem_deref(reg->params); } static uint32_t failwait(uint32_t failc) { return min(1800, (30 * (1<tmr, failwait(++reg->failc), tmr_handler, reg); reg->resph(err, NULL, reg->arg); } } static void keepalive_handler(int err, void *arg) { struct sipreg *reg = arg; /* failure will be handled in response handler */ if (reg->req || reg->terminated) return; tmr_start(®->tmr, failwait(++reg->failc), tmr_handler, reg); reg->resph(err, NULL, reg->arg); } static void start_outbound(struct sipreg *reg, const struct sip_msg *msg) { const struct sip_hdr *flowtimer; if (!sip_msg_hdr_has_value(msg, SIP_HDR_REQUIRE, "outbound")) return; flowtimer = sip_msg_hdr(msg, SIP_HDR_FLOW_TIMER); (void)sip_keepalive_start(®->ka, reg->sip, msg, flowtimer ? pl_u32(&flowtimer->val) : 0, keepalive_handler, reg); } static bool contact_handler(const struct sip_hdr *hdr, const struct sip_msg *msg, void *arg) { struct sipreg *reg = arg; struct sip_addr c; struct pl pval; char uri[256]; if (sip_addr_decode(&c, &hdr->val)) return false; if (re_snprintf(uri, sizeof(uri), "sip:%s@%J%s", reg->cuser, ®->laddr, sip_transp_param(reg->tp)) < 0) return false; if (pl_strcmp(&c.auri, uri)) return false; if (!msg_param_decode(&c.params, "expires", &pval)) { reg->wait = pl_u32(&pval); } else if (pl_isset(&msg->expires)) reg->wait = pl_u32(&msg->expires); else reg->wait = DEFAULT_EXPIRES; return true; } static void response_handler(int err, const struct sip_msg *msg, void *arg) { const struct sip_hdr *minexp; struct sipreg *reg = arg; reg->wait = failwait(reg->failc + 1); if (err || !msg || sip_request_loops(®->ls, msg->scode)) { reg->failc++; goto out; } if (msg->scode < 200) { return; } else if (msg->scode < 300) { reg->wait = reg->expires; sip_msg_hdr_apply(msg, true, SIP_HDR_CONTACT, contact_handler, reg); reg->registered = reg->wait > 0; reg->pexpires = reg->wait; reg->wait *= reg->rwait * (1000 / 100); reg->failc = 0; if (reg->regid > 0 && !reg->terminated && !reg->ka) start_outbound(reg, msg); } else { if (reg->terminated && !reg->registered) goto out; switch (msg->scode) { case 401: case 407: sip_auth_reset(reg->auth); err = sip_auth_authenticate(reg->auth, msg); if (err) { err = (err == EAUTH) ? 0 : err; break; } err = request(reg, false); if (err) break; return; case 403: sip_auth_reset(reg->auth); break; case 423: minexp = sip_msg_hdr(msg, SIP_HDR_MIN_EXPIRES); if (!minexp || !pl_u32(&minexp->val) || !reg->expires) break; reg->expires = pl_u32(&minexp->val); err = request(reg, false); if (err) break; return; } ++reg->failc; } out: if (!reg->expires) { if (msg && msg->scode >= 400 && msg->scode < 500) reg->fbregint = 0; if (!reg->terminated && reg->fbregint) { tmr_start(®->tmr, reg->fbregint * 1000, tmr_handler, reg); reg->resph(err, msg, reg->arg); } else if (reg->terminated) { mem_deref(reg); } } else if (reg->terminated) { if (!reg->registered || request(reg, true)) mem_deref(reg); } else { tmr_start(®->tmr, reg->wait, tmr_handler, reg); reg->resph(err, msg, reg->arg); } } static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sipreg *reg = arg; int err; (void)dst; reg->laddr = *src; reg->tp = tp; err = mbuf_printf(mb, "Contact: ;expires=%u%s%s", reg->cuser, ®->laddr, sip_transp_param(reg->tp), reg->expires, reg->params ? ";" : "", reg->params ? reg->params : ""); if (reg->regid > 0) err |= mbuf_printf(mb, ";reg-id=%d", reg->regid); err |= mbuf_printf(mb, "\r\n"); return err; } static int request(struct sipreg *reg, bool reset_ls) { if (reg->terminated) reg->expires = 0; if (reset_ls) { sip_loopstate_reset(®->ls); sip_auth_reset(reg->auth); } return sip_drequestf(®->req, reg->sip, true, "REGISTER", reg->dlg, 0, reg->auth, send_handler, response_handler, reg, "%s" "%b" "Content-Length: 0\r\n" "\r\n", reg->regid > 0 ? "Supported: gruu, outbound, path\r\n" : "", reg->hdrs ? mbuf_buf(reg->hdrs) : NULL, reg->hdrs ? mbuf_get_left(reg->hdrs) : (size_t)0); } /** * Allocate a SIP Registration client * * @param regp Pointer to allocated SIP Registration client * @param sip SIP Stack instance * @param reg_uri SIP Request URI * @param to_uri SIP To-header URI * @param from_name SIP From-header display name (optional) * @param from_uri SIP From-header URI * @param expires Registration expiry time in [seconds] * @param cuser Contact username * @param routev Optional route vector * @param routec Number of routes * @param regid Register identification * @param authh Authentication handler * @param aarg Authentication handler argument * @param aref True to ref argument * @param resph Response handler * @param arg Response handler argument * @param params Optional Contact-header parameters * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipreg_register(struct sipreg **regp, struct sip *sip, const char *reg_uri, const char *to_uri, const char *from_name, const char *from_uri, uint32_t expires, const char *cuser, const char *routev[], uint32_t routec, int regid, sip_auth_h *authh, void *aarg, bool aref, sip_resp_h *resph, void *arg, const char *params, const char *fmt, ...) { struct sipreg *reg; int err; if (!regp || !sip || !reg_uri || !to_uri || !from_uri || !cuser) return EINVAL; reg = mem_zalloc(sizeof(*reg), destructor); if (!reg) return ENOMEM; err = sip_dialog_alloc(®->dlg, reg_uri, to_uri, from_name, from_uri, routev, routec); if (err) goto out; err = sip_auth_alloc(®->auth, authh, aarg, aref); if (err) goto out; err = str_dup(®->cuser, cuser); if (params) err |= str_dup(®->params, params); if (err) goto out; /* Custom SIP headers */ if (fmt) { va_list ap; reg->hdrs = mbuf_alloc(256); if (!reg->hdrs) { err = ENOMEM; goto out; } va_start(ap, fmt); err = mbuf_vprintf(reg->hdrs, fmt, ap); reg->hdrs->pos = 0; va_end(ap); if (err) goto out; } reg->sip = mem_ref(sip); reg->expires = expires; reg->rwait = 90; reg->resph = resph ? resph : dummy_handler; reg->arg = arg; reg->regid = regid; err = request(reg, true); if (err) goto out; out: if (err) mem_deref(reg); else *regp = reg; return err; } /** * Set the relative registration interval in percent from proxy expiry time. A * value from 5-95% is accepted. * * @param reg SIP Registration client * @param rwait The relative registration interval in [%]. * * @return 0 if success, otherwise errorcode */ int sipreg_set_rwait(struct sipreg *reg, uint32_t rwait) { if (!reg || rwait < 5 || rwait > 95) return EINVAL; reg->rwait = rwait; return 0; } /** * Get the local socket address for a SIP Registration client * * @param reg SIP Registration client * * @return Local socket address */ const struct sa *sipreg_laddr(const struct sipreg *reg) { return reg ? ®->laddr : NULL; } /** * Get the proxy expires value of a SIP registration client * * @param reg SIP registration client * * @return the proxy expires value */ uint32_t sipreg_proxy_expires(const struct sipreg *reg) { return reg ? reg->pexpires : 0; } bool sipreg_registered(const struct sipreg *reg) { return reg ? reg->registered : false; } bool sipreg_failed(const struct sipreg *reg) { return reg ? reg->failc > 0 : false; } void sipreg_incfailc(struct sipreg *reg) { if (!reg) return; reg->failc++; } int sipreg_set_fbregint(struct sipreg *reg, uint32_t fbregint) { if (!reg) return EINVAL; reg->fbregint = fbregint; return 0; } re-1.1.0/src/sipsess/000077500000000000000000000000001373627245400144145ustar00rootroot00000000000000re-1.1.0/src/sipsess/accept.c000066400000000000000000000134551373627245400160270ustar00rootroot00000000000000/** * @file accept.c SIP Session Accept * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static void cancel_handler(void *arg) { struct sipsess *sess = arg; (void)sip_treply(&sess->st, sess->sip, sess->msg, 487, "Request Terminated"); sess->peerterm = true; if (sess->terminated) return; sipsess_terminate(sess, ECONNRESET, NULL); } /** * Accept an incoming SIP Session connection * * @param sessp Pointer to allocated SIP Session * @param sock SIP Session socket * @param msg Incoming SIP message * @param scode Response status code * @param reason Response reason phrase * @param cuser Contact username or URI * @param ctype Session content-type * @param desc Content description (e.g. SDP) * @param authh SIP Authentication handler * @param aarg Authentication handler argument * @param aref True to mem_ref() aarg * @param offerh Session offer handler * @param answerh Session answer handler * @param estabh Session established handler * @param infoh Session info handler * @param referh Session refer handler * @param closeh Session close handler * @param arg Handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_accept(struct sipsess **sessp, struct sipsess_sock *sock, const struct sip_msg *msg, uint16_t scode, const char *reason, const char *cuser, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg, const char *fmt, ...) { struct sipsess *sess; va_list ap; int err; if (!sessp || !sock || !msg || scode < 101 || scode > 299 || !cuser || !ctype) return EINVAL; err = sipsess_alloc(&sess, sock, cuser, ctype, NULL, authh, aarg, aref, offerh, answerh, NULL, estabh, infoh, referh, closeh, arg); if (err) return err; err = sip_dialog_accept(&sess->dlg, msg); if (err) goto out; hash_append(sock->ht_sess, hash_joaat_str(sip_dialog_callid(sess->dlg)), &sess->he, sess); sess->msg = mem_ref((void *)msg); err = sip_strans_alloc(&sess->st, sess->sip, msg, cancel_handler, sess); if (err) goto out; va_start(ap, fmt); if (scode >= 200) err = sipsess_reply_2xx(sess, msg, scode, reason, desc, fmt, &ap); else { struct sip_contact contact; sip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp); err = sip_treplyf(&sess->st, NULL, sess->sip, msg, true, scode, reason, "%H" "%v" "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", sip_contact_print, &contact, fmt, &ap, desc ? "Content-Type: " : "", desc ? sess->ctype : "", desc ? "\r\n" : "", desc ? mbuf_get_left(desc) : (size_t)0, desc ? mbuf_buf(desc) : NULL, desc ? mbuf_get_left(desc) : (size_t)0); } va_end(ap); if (err) goto out; out: if (err) mem_deref(sess); else *sessp = sess; return err; } /** * Send progress response * * @param sess SIP Session * @param scode Response status code * @param reason Response reason phrase * @param desc Content description (e.g. SDP) * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_progress(struct sipsess *sess, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, ...) { struct sip_contact contact; va_list ap; int err; if (!sess || !sess->st || !sess->msg || scode < 101 || scode > 199) return EINVAL; va_start(ap, fmt); sip_contact_set(&contact, sess->cuser, &sess->msg->dst, sess->msg->tp); err = sip_treplyf(&sess->st, NULL, sess->sip, sess->msg, true, scode, reason, "%H" "%v" "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", sip_contact_print, &contact, fmt, &ap, desc ? "Content-Type: " : "", desc ? sess->ctype : "", desc ? "\r\n" : "", desc ? mbuf_get_left(desc) : (size_t)0, desc ? mbuf_buf(desc) : NULL, desc ? mbuf_get_left(desc) : (size_t)0); va_end(ap); return err; } /** * Answer an incoming SIP Session connection * * @param sess SIP Session * @param scode Response status code * @param reason Response reason phrase * @param desc Content description (e.g. SDP) * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_answer(struct sipsess *sess, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, ...) { va_list ap; int err; if (!sess || !sess->st || !sess->msg || scode < 200 || scode > 299) return EINVAL; va_start(ap, fmt); err = sipsess_reply_2xx(sess, sess->msg, scode, reason, desc, fmt, &ap); va_end(ap); return err; } /** * Reject an incoming SIP Session connection * * @param sess SIP Session * @param scode Response status code * @param reason Response reason phrase * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_reject(struct sipsess *sess, uint16_t scode, const char *reason, const char *fmt, ...) { va_list ap; int err; if (!sess || !sess->st || !sess->msg || scode < 300) return EINVAL; va_start(ap, fmt); err = sip_treplyf(&sess->st, NULL, sess->sip, sess->msg, false, scode, reason, fmt ? "%v" : NULL, fmt, &ap); va_end(ap); return err; } re-1.1.0/src/sipsess/ack.c000066400000000000000000000052531373627245400153230ustar00rootroot00000000000000/** * @file ack.c SIP Session ACK * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" struct sipsess_ack { struct le he; struct tmr tmr; struct sa dst; struct sip_request *req; struct sip_dialog *dlg; struct mbuf *mb; enum sip_transp tp; uint32_t cseq; }; static void destructor(void *arg) { struct sipsess_ack *ack = arg; hash_unlink(&ack->he); tmr_cancel(&ack->tmr); mem_deref(ack->req); mem_deref(ack->dlg); mem_deref(ack->mb); } static void tmr_handler(void *arg) { struct sipsess_ack *ack = arg; mem_deref(ack); } static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sipsess_ack *ack = arg; (void)src; mem_deref(ack->mb); ack->mb = mem_ref(mb); ack->dst = *dst; ack->tp = tp; tmr_start(&ack->tmr, 64 * SIP_T1, tmr_handler, ack); return 0; } static void resp_handler(int err, const struct sip_msg *msg, void *arg) { struct sipsess_ack *ack = arg; (void)err; (void)msg; mem_deref(ack); } int sipsess_ack(struct sipsess_sock *sock, struct sip_dialog *dlg, uint32_t cseq, struct sip_auth *auth, const char *ctype, struct mbuf *desc) { struct sipsess_ack *ack; int err; ack = mem_zalloc(sizeof(*ack), destructor); if (!ack) return ENOMEM; hash_append(sock->ht_ack, hash_joaat_str(sip_dialog_callid(dlg)), &ack->he, ack); ack->dlg = mem_ref(dlg); ack->cseq = cseq; err = sip_drequestf(&ack->req, sock->sip, false, "ACK", dlg, cseq, auth, send_handler, resp_handler, ack, "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", desc ? "Content-Type: " : "", desc ? ctype : "", desc ? "\r\n" : "", desc ? mbuf_get_left(desc) : (size_t)0, desc ? mbuf_buf(desc) : NULL, desc ? mbuf_get_left(desc) : (size_t)0); if (err) goto out; out: if (err) mem_deref(ack); return err; } static bool cmp_handler(struct le *le, void *arg) { struct sipsess_ack *ack = le->data; const struct sip_msg *msg = arg; if (!sip_dialog_cmp(ack->dlg, msg)) return false; if (ack->cseq != msg->cseq.num) return false; return true; } int sipsess_ack_again(struct sipsess_sock *sock, const struct sip_msg *msg) { struct sipsess_ack *ack; ack = list_ledata(hash_lookup(sock->ht_ack, hash_joaat_pl(&msg->callid), cmp_handler, (void *)msg)); if (!ack) return ENOENT; return sip_send(sock->sip, NULL, ack->tp, &ack->dst, ack->mb); } re-1.1.0/src/sipsess/close.c000066400000000000000000000023211373627245400156630ustar00rootroot00000000000000/** * @file close.c SIP Session Close * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static void bye_resp_handler(int err, const struct sip_msg *msg, void *arg) { struct sipsess *sess = arg; if (err || sip_request_loops(&sess->ls, msg->scode)) goto out; if (msg->scode < 200) { return; } else if (msg->scode < 300) { ; } else { if (sess->peerterm) goto out; switch (msg->scode) { case 401: case 407: err = sip_auth_authenticate(sess->auth, msg); if (err) break; err = sipsess_bye(sess, false); if (err) break; return; } } out: mem_deref(sess); } int sipsess_bye(struct sipsess *sess, bool reset_ls) { if (sess->req) return EPROTO; if (reset_ls) sip_loopstate_reset(&sess->ls); return sip_drequestf(&sess->req, sess->sip, true, "BYE", sess->dlg, 0, sess->auth, NULL, bye_resp_handler, sess, "%s" "Content-Length: 0\r\n" "\r\n", sess->close_hdrs); } re-1.1.0/src/sipsess/connect.c000066400000000000000000000126401373627245400162140ustar00rootroot00000000000000/** * @file connect.c SIP Session Connect * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static int invite(struct sipsess *sess); static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sip_contact contact; struct sipsess *sess = arg; (void)dst; sip_contact_set(&contact, sess->cuser, src, tp); return mbuf_printf(mb, "%H", sip_contact_print, &contact); } static void invite_resp_handler(int err, const struct sip_msg *msg, void *arg) { struct sipsess *sess = arg; struct mbuf *desc = NULL; if (err || sip_request_loops(&sess->ls, msg->scode)) goto out; if (msg->scode < 200) { sess->progrh(msg, sess->arg); return; } else if (msg->scode < 300) { sess->hdrs = mem_deref(sess->hdrs); err = sip_dialog_create(sess->dlg, msg); if (err) goto out; if (sess->sent_offer) err = sess->answerh(msg, sess->arg); else { sess->modify_pending = false; err = sess->offerh(&desc, msg, sess->arg); } err |= sipsess_ack(sess->sock, sess->dlg, msg->cseq.num, sess->auth, sess->ctype, desc); sess->established = true; mem_deref(desc); if (err || sess->terminated) goto out; if (sess->modify_pending) (void)sipsess_reinvite(sess, true); else sess->desc = mem_deref(sess->desc); sess->estabh(msg, sess->arg); return; } else if (msg->scode < 400) { /* Redirect to first Contact */ if (sess->terminated) goto out; err = sip_dialog_update(sess->dlg, msg); if (err) goto out; err = invite(sess); if (err) goto out; return; } else { if (sess->terminated) goto out; switch (msg->scode) { case 401: case 407: err = sip_auth_authenticate(sess->auth, msg); if (err) { err = (err == EAUTH) ? 0 : err; break; } err = invite(sess); if (err) break; return; } } out: if (!sess->terminated) sipsess_terminate(sess, err, msg); else mem_deref(sess); } static int invite(struct sipsess *sess) { sess->sent_offer = sess->desc ? true : false; sess->modify_pending = false; return sip_drequestf(&sess->req, sess->sip, true, "INVITE", sess->dlg, 0, sess->auth, send_handler, invite_resp_handler, sess, "%b" "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", sess->hdrs ? mbuf_buf(sess->hdrs) : NULL, sess->hdrs ? mbuf_get_left(sess->hdrs) :(size_t)0, sess->desc ? "Content-Type: " : "", sess->desc ? sess->ctype : "", sess->desc ? "\r\n" : "", sess->desc ? mbuf_get_left(sess->desc) :(size_t)0, sess->desc ? mbuf_buf(sess->desc) : NULL, sess->desc ? mbuf_get_left(sess->desc):(size_t)0); } /** * Connect to a remote SIP useragent * * @param sessp Pointer to allocated SIP Session * @param sock SIP Session socket * @param to_uri To SIP uri * @param from_name From display name * @param from_uri From SIP uri * @param cuser Contact username or URI * @param routev Outbound route vector * @param routec Outbound route vector count * @param ctype Session content-type * @param desc Content description (e.g. SDP) * @param authh SIP Authentication handler * @param aarg Authentication handler argument * @param aref True to mem_ref() aarg * @param offerh Session offer handler * @param answerh Session answer handler * @param progrh Session progress handler * @param estabh Session established handler * @param infoh Session info handler * @param referh Session refer handler * @param closeh Session close handler * @param arg Handler argument * @param fmt Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_connect(struct sipsess **sessp, struct sipsess_sock *sock, const char *to_uri, const char *from_name, const char *from_uri, const char *cuser, const char *routev[], uint32_t routec, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_progr_h *progrh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg, const char *fmt, ...) { struct sipsess *sess; int err; if (!sessp || !sock || !to_uri || !from_uri || !cuser || !ctype) return EINVAL; err = sipsess_alloc(&sess, sock, cuser, ctype, desc, authh, aarg, aref, offerh, answerh, progrh, estabh, infoh, referh, closeh, arg); if (err) return err; /* Custom SIP headers */ if (fmt) { va_list ap; sess->hdrs = mbuf_alloc(256); if (!sess->hdrs) { err = ENOMEM; goto out; } va_start(ap, fmt); err = mbuf_vprintf(sess->hdrs, fmt, ap); sess->hdrs->pos = 0; va_end(ap); if (err) goto out; } sess->owner = true; err = sip_dialog_alloc(&sess->dlg, to_uri, to_uri, from_name, from_uri, routev, routec); if (err) goto out; hash_append(sock->ht_sess, hash_joaat_str(sip_dialog_callid(sess->dlg)), &sess->he, sess); err = invite(sess); if (err) goto out; out: if (err) mem_deref(sess); else *sessp = sess; return err; } re-1.1.0/src/sipsess/info.c000066400000000000000000000044571373627245400155250ustar00rootroot00000000000000/** * @file info.c SIP Session Info * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static int info_request(struct sipsess_request *req); static void info_resp_handler(int err, const struct sip_msg *msg, void *arg) { struct sipsess_request *req = arg; if (err || sip_request_loops(&req->ls, msg->scode)) goto out; if (msg->scode < 200) { return; } else if (msg->scode < 300) { ; } else { if (req->sess->terminated) goto out; switch (msg->scode) { case 401: case 407: err = sip_auth_authenticate(req->sess->auth, msg); if (err) { err = (err == EAUTH) ? 0 : err; break; } err = info_request(req); if (err) break; return; case 408: case 481: sipsess_terminate(req->sess, 0, msg); break; } } out: if (!req->sess->terminated) { if (err == ETIMEDOUT) sipsess_terminate(req->sess, err, NULL); else req->resph(err, msg, req->arg); } mem_deref(req); } static int info_request(struct sipsess_request *req) { return sip_drequestf(&req->req, req->sess->sip, true, "INFO", req->sess->dlg, 0, req->sess->auth, NULL, info_resp_handler, req, "Content-Type: %s\r\n" "Content-Length: %zu\r\n" "\r\n" "%b", req->ctype, mbuf_get_left(req->body), mbuf_buf(req->body), mbuf_get_left(req->body)); } /** * Send a SIP INFO request in the SIP Session * * @param sess SIP Session * @param ctype Content-type * @param body Content description (e.g. SDP) * @param resph Response handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sipsess_info(struct sipsess *sess, const char *ctype, struct mbuf *body, sip_resp_h *resph, void *arg) { struct sipsess_request *req; int err; if (!sess || sess->terminated || !ctype || !body) return EINVAL; if (!sip_dialog_established(sess->dlg)) return ENOTCONN; err = sipsess_request_alloc(&req, sess, ctype, body, resph, arg); if (err) return err; err = info_request(req); if (err) mem_deref(req); return err; } re-1.1.0/src/sipsess/listen.c000066400000000000000000000162111373627245400160570ustar00rootroot00000000000000/** * @file sipsess/listen.c SIP Session Listen * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static void destructor(void *arg) { struct sipsess_sock *sock = arg; mem_deref(sock->lsnr_resp); mem_deref(sock->lsnr_req); hash_flush(sock->ht_sess); mem_deref(sock->ht_sess); hash_flush(sock->ht_ack); mem_deref(sock->ht_ack); } static void internal_connect_handler(const struct sip_msg *msg, void *arg) { struct sipsess_sock *sock = arg; (void)sip_treply(NULL, sock->sip, msg, 486, "Busy Here"); } static bool cmp_handler(struct le *le, void *arg) { struct sipsess *sess = le->data; const struct sip_msg *msg = arg; return sip_dialog_cmp(sess->dlg, msg); } static struct sipsess *sipsess_find(struct sipsess_sock *sock, const struct sip_msg *msg) { return list_ledata(hash_lookup(sock->ht_sess, hash_joaat_pl(&msg->callid), cmp_handler, (void *)msg)); } static void info_handler(struct sipsess_sock *sock, const struct sip_msg *msg) { struct sip *sip = sock->sip; struct sipsess *sess; sess = sipsess_find(sock, msg); if (!sess || sess->terminated) { (void)sip_reply(sip, msg, 481, "Call Does Not Exist"); return; } if (!sip_dialog_rseq_valid(sess->dlg, msg)) { (void)sip_reply(sip, msg, 500, "Server Internal Error"); return; } if (!sess->infoh) { (void)sip_reply(sip, msg, 501, "Not Implemented"); return; } sess->infoh(sip, msg, sess->arg); } static void refer_handler(struct sipsess_sock *sock, const struct sip_msg *msg) { struct sip *sip = sock->sip; struct sipsess *sess; sess = sipsess_find(sock, msg); if (!sess || sess->terminated) { (void)sip_reply(sip, msg, 481, "Call Does Not Exist"); return; } if (!sip_dialog_rseq_valid(sess->dlg, msg)) { (void)sip_reply(sip, msg, 500, "Server Internal Error"); return; } if (!sess->referh) { (void)sip_reply(sip, msg, 501, "Not Implemented"); return; } sess->referh(sip, msg, sess->arg); } static void bye_handler(struct sipsess_sock *sock, const struct sip_msg *msg) { struct sip *sip = sock->sip; struct sipsess *sess; sess = sipsess_find(sock, msg); if (!sess) { (void)sip_reply(sip, msg, 481, "Call Does Not Exist"); return; } if (!sip_dialog_rseq_valid(sess->dlg, msg)) { (void)sip_reply(sip, msg, 500, "Server Internal Error"); return; } (void)sip_treplyf(NULL, NULL, sip, msg, false, 200, "OK", "%s" "Content-Length: 0\r\n" "\r\n", sess->close_hdrs); sess->peerterm = true; if (sess->terminated) return; if (sess->st) { (void)sip_treply(&sess->st, sess->sip, sess->msg, 487, "Request Terminated"); } sipsess_terminate(sess, ECONNRESET, NULL); } static void ack_handler(struct sipsess_sock *sock, const struct sip_msg *msg) { struct sipsess *sess; bool awaiting_answer; int err = 0; sess = sipsess_find(sock, msg); if (!sess) return; if (sipsess_reply_ack(sess, msg, &awaiting_answer)) return; if (sess->terminated) { if (!sess->replyl.head) { sess->established = true; mem_deref(sess); } return; } if (awaiting_answer) { sess->awaiting_answer = false; err = sess->answerh(msg, sess->arg); } if (sess->modify_pending && !sess->replyl.head) (void)sipsess_reinvite(sess, true); if (sess->established) return; sess->msg = mem_deref((void *)sess->msg); sess->established = true; if (err) sipsess_terminate(sess, err, NULL); else sess->estabh(msg, sess->arg); } static void reinvite_handler(struct sipsess_sock *sock, const struct sip_msg *msg) { struct sip *sip = sock->sip; struct sipsess *sess; struct mbuf *desc; char m[256]; int err; sess = sipsess_find(sock, msg); if (!sess || sess->terminated) { (void)sip_treply(NULL, sip, msg, 481, "Call Does Not Exist"); return; } if (!sip_dialog_rseq_valid(sess->dlg, msg)) { (void)sip_treply(NULL, sip, msg, 500, "Server Internal Error"); return; } if (sess->st || sess->awaiting_answer) { (void)sip_treplyf(NULL, NULL, sip, msg, false, 500, "Server Internal Error", "Retry-After: 5\r\n" "Content-Length: 0\r\n" "\r\n"); return; } if (sess->req) { (void)sip_treply(NULL, sip, msg, 491, "Request Pending"); return; } err = sess->offerh(&desc, msg, sess->arg); if (err) { (void)sip_reply(sip, msg, 488, str_error(err, m, sizeof(m))); return; } (void)sip_dialog_update(sess->dlg, msg); (void)sipsess_reply_2xx(sess, msg, 200, "OK", desc, NULL, NULL); /* pending modifications considered outdated; sdp may have changed in above exchange */ sess->desc = mem_deref(sess->desc); sess->modify_pending = false; tmr_cancel(&sess->tmr); mem_deref(desc); } static void invite_handler(struct sipsess_sock *sock, const struct sip_msg *msg) { sock->connh(msg, sock->arg); } static bool request_handler(const struct sip_msg *msg, void *arg) { struct sipsess_sock *sock = arg; if (!pl_strcmp(&msg->met, "INVITE")) { if (pl_isset(&msg->to.tag)) reinvite_handler(sock, msg); else invite_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "ACK")) { ack_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "BYE")) { bye_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "INFO")) { info_handler(sock, msg); return true; } else if (!pl_strcmp(&msg->met, "REFER")) { if (!pl_isset(&msg->to.tag)) return false; refer_handler(sock, msg); return true; } return false; } static bool response_handler(const struct sip_msg *msg, void *arg) { struct sipsess_sock *sock = arg; if (pl_strcmp(&msg->cseq.met, "INVITE")) return false; if (msg->scode < 200 || msg->scode > 299) return false; (void)sipsess_ack_again(sock, msg); return true; } /** * Listen to a SIP Session socket for incoming connections * * @param sockp Pointer to allocated SIP Session socket * @param sip SIP Stack instance * @param htsize Hashtable size * @param connh Connection handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int sipsess_listen(struct sipsess_sock **sockp, struct sip *sip, int htsize, sipsess_conn_h *connh, void *arg) { struct sipsess_sock *sock; int err; if (!sockp || !sip || !htsize) return EINVAL; sock = mem_zalloc(sizeof(*sock), destructor); if (!sock) return ENOMEM; err = sip_listen(&sock->lsnr_resp, sip, false, response_handler, sock); if (err) goto out; err = sip_listen(&sock->lsnr_req, sip, true, request_handler, sock); if (err) goto out; err = hash_alloc(&sock->ht_sess, htsize); if (err) goto out; err = hash_alloc(&sock->ht_ack, htsize); if (err) goto out; sock->sip = sip; sock->connh = connh ? connh : internal_connect_handler; sock->arg = connh ? arg : sock; out: if (err) mem_deref(sock); else *sockp = sock; return err; } /** * Close all SIP Sessions * * @param sock SIP Session socket */ void sipsess_close_all(struct sipsess_sock *sock) { if (!sock) return; hash_flush(sock->ht_sess); } re-1.1.0/src/sipsess/mod.mk000066400000000000000000000004441373627245400155260ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sipsess/sess.c SRCS += sipsess/accept.c SRCS += sipsess/ack.c SRCS += sipsess/close.c SRCS += sipsess/connect.c SRCS += sipsess/info.c SRCS += sipsess/listen.c SRCS += sipsess/modify.c SRCS += sipsess/reply.c SRCS += sipsess/request.c re-1.1.0/src/sipsess/modify.c000066400000000000000000000067201373627245400160540ustar00rootroot00000000000000/** * @file modify.c SIP Session Modify * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static void tmr_handler(void *arg) { struct sipsess *sess = arg; (void)sipsess_reinvite(sess, true); } static void reinvite_resp_handler(int err, const struct sip_msg *msg, void *arg) { struct sipsess *sess = arg; const struct sip_hdr *hdr; struct mbuf *desc = NULL; if (err || sip_request_loops(&sess->ls, msg->scode)) goto out; if (msg->scode < 200) { return; } else if (msg->scode < 300) { (void)sip_dialog_update(sess->dlg, msg); if (sess->sent_offer) (void)sess->answerh(msg, sess->arg); else { sess->modify_pending = false; (void)sess->offerh(&desc, msg, sess->arg); } (void)sipsess_ack(sess->sock, sess->dlg, msg->cseq.num, sess->auth, sess->ctype, desc); mem_deref(desc); } else { if (sess->terminated) goto out; switch (msg->scode) { case 401: case 407: err = sip_auth_authenticate(sess->auth, msg); if (err) { err = (err == EAUTH) ? 0 : err; break; } err = sipsess_reinvite(sess, false); if (err) break; return; case 408: case 481: sipsess_terminate(sess, 0, msg); return; case 491: tmr_start(&sess->tmr, sess->owner ? 3000 : 1000, tmr_handler, sess); return; case 500: hdr = sip_msg_hdr(msg, SIP_HDR_RETRY_AFTER); if (!hdr) break; tmr_start(&sess->tmr, pl_u32(&hdr->val) * 1000, tmr_handler, sess); return; } } out: if (sess->terminated) mem_deref(sess); else if (err == ETIMEDOUT) sipsess_terminate(sess, err, NULL); else if (sess->modify_pending) (void)sipsess_reinvite(sess, true); else sess->desc = mem_deref(sess->desc); } static int send_handler(enum sip_transp tp, const struct sa *src, const struct sa *dst, struct mbuf *mb, void *arg) { struct sip_contact contact; struct sipsess *sess = arg; (void)dst; sip_contact_set(&contact, sess->cuser, src, tp); return mbuf_printf(mb, "%H", sip_contact_print, &contact); } int sipsess_reinvite(struct sipsess *sess, bool reset_ls) { if (sess->req) return EPROTO; sess->sent_offer = sess->desc ? true : false; sess->modify_pending = false; if (reset_ls) sip_loopstate_reset(&sess->ls); return sip_drequestf(&sess->req, sess->sip, true, "INVITE", sess->dlg, 0, sess->auth, send_handler, reinvite_resp_handler, sess, "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", sess->desc ? "Content-Type: " : "", sess->desc ? sess->ctype : "", sess->desc ? "\r\n" : "", sess->desc ? mbuf_get_left(sess->desc) :(size_t)0, sess->desc ? mbuf_buf(sess->desc) : NULL, sess->desc ? mbuf_get_left(sess->desc):(size_t)0); } /** * Modify an established SIP Session sending Re-INVITE or UPDATE * * @param sess SIP Session * @param desc Content description (e.g. SDP) * * @return 0 if success, otherwise errorcode */ int sipsess_modify(struct sipsess *sess, struct mbuf *desc) { if (!sess || sess->st || sess->terminated) return EINVAL; mem_deref(sess->desc); sess->desc = mem_ref(desc); if (sess->req || sess->tmr.th || sess->replyl.head) { sess->modify_pending = true; return 0; } return sipsess_reinvite(sess, true); } re-1.1.0/src/sipsess/reply.c000066400000000000000000000062431373627245400157200ustar00rootroot00000000000000/** * @file sipsess/reply.c SIP Session Reply * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" struct sipsess_reply { struct le le; struct tmr tmr; struct tmr tmrg; const struct sip_msg *msg; struct mbuf *mb; struct sipsess *sess; bool awaiting_answer; uint32_t seq; uint32_t txc; }; static void destructor(void *arg) { struct sipsess_reply *reply = arg; list_unlink(&reply->le); tmr_cancel(&reply->tmr); tmr_cancel(&reply->tmrg); mem_deref((void *)reply->msg); mem_deref(reply->mb); } static void tmr_handler(void *arg) { struct sipsess_reply *reply = arg; struct sipsess *sess = reply->sess; mem_deref(reply); /* wait for all pending ACKs */ if (sess->replyl.head) return; /* we want to send bye */ sess->established = true; if (!sess->terminated) sipsess_terminate(sess, ETIMEDOUT, NULL); else mem_deref(sess); } static void retransmit_handler(void *arg) { struct sipsess_reply *reply = arg; (void)sip_send(reply->sess->sip, reply->msg->sock, reply->msg->tp, &reply->msg->src, reply->mb); reply->txc++; tmr_start(&reply->tmrg, MIN(SIP_T1<txc, SIP_T2), retransmit_handler, reply); } int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, va_list *ap) { struct sipsess_reply *reply; struct sip_contact contact; int err = ENOMEM; reply = mem_zalloc(sizeof(*reply), destructor); if (!reply) goto out; list_append(&sess->replyl, &reply->le, reply); reply->seq = msg->cseq.num; reply->msg = mem_ref((void *)msg); reply->sess = sess; sip_contact_set(&contact, sess->cuser, &msg->dst, msg->tp); err = sip_treplyf(&sess->st, &reply->mb, sess->sip, msg, true, scode, reason, "%H" "%v" "%s%s%s" "Content-Length: %zu\r\n" "\r\n" "%b", sip_contact_print, &contact, fmt, ap, desc ? "Content-Type: " : "", desc ? sess->ctype : "", desc ? "\r\n" : "", desc ? mbuf_get_left(desc) : (size_t)0, desc ? mbuf_buf(desc) : NULL, desc ? mbuf_get_left(desc) : (size_t)0); if (err) goto out; tmr_start(&reply->tmr, 64 * SIP_T1, tmr_handler, reply); tmr_start(&reply->tmrg, SIP_T1, retransmit_handler, reply); if (!mbuf_get_left(msg->mb) && desc) { reply->awaiting_answer = true; sess->awaiting_answer = true; } out: if (err) { sess->st = mem_deref(sess->st); mem_deref(reply); } return err; } static bool cmp_handler(struct le *le, void *arg) { struct sipsess_reply *reply = le->data; const struct sip_msg *msg = arg; return msg->cseq.num == reply->seq; } int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg, bool *awaiting_answer) { struct sipsess_reply *reply; reply = list_ledata(list_apply(&sess->replyl, false, cmp_handler, (void *)msg)); if (!reply) return ENOENT; *awaiting_answer = reply->awaiting_answer; mem_deref(reply); return 0; } re-1.1.0/src/sipsess/request.c000066400000000000000000000026431373627245400162550ustar00rootroot00000000000000/** * @file sipsess/request.c SIP Session Non-INVITE Request * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static void destructor(void *arg) { struct sipsess_request *req = arg; list_unlink(&req->le); mem_deref(req->ctype); mem_deref(req->body); mem_deref(req->req); /* wait for pending requests */ if (req->sess->terminated && !req->sess->requestl.head) mem_deref(req->sess); } static void internal_resp_handler(int err, const struct sip_msg *msg, void *arg) { (void)err; (void)msg; (void)arg; } int sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess, const char *ctype, struct mbuf *body, sip_resp_h *resph, void *arg) { struct sipsess_request *req; int err = 0; if (!reqp || !sess || sess->terminated) return EINVAL; req = mem_zalloc(sizeof(*req), destructor); if (!req) return ENOMEM; list_append(&sess->requestl, &req->le, req); if (ctype) { err = str_dup(&req->ctype, ctype); if (err) goto out; } req->sess = sess; req->body = mem_ref(body); req->resph = resph ? resph : internal_resp_handler; req->arg = arg; out: if (err) mem_deref(req); else *reqp = req; return 0; } re-1.1.0/src/sipsess/sess.c000066400000000000000000000114211373627245400155340ustar00rootroot00000000000000/** * @file sipsess/sess.c SIP Session Core * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "sipsess.h" static int internal_offer_handler(struct mbuf **descp, const struct sip_msg *msg, void *arg) { (void)descp; (void)msg; (void)arg; return ENOSYS; } static int internal_answer_handler(const struct sip_msg *msg, void *arg) { (void)msg; (void)arg; return ENOSYS; } static void internal_progress_handler(const struct sip_msg *msg, void *arg) { (void)msg; (void)arg; } static void internal_establish_handler(const struct sip_msg *msg, void *arg) { (void)msg; (void)arg; } static void internal_close_handler(int err, const struct sip_msg *msg, void *arg) { (void)err; (void)msg; (void)arg; } static bool termwait(struct sipsess *sess) { bool wait = false; sess->terminated = 1; sess->offerh = internal_offer_handler; sess->answerh = internal_answer_handler; sess->progrh = internal_progress_handler; sess->estabh = internal_establish_handler; sess->infoh = NULL; sess->referh = NULL; sess->closeh = internal_close_handler; sess->arg = sess; tmr_cancel(&sess->tmr); if (sess->st) { (void)sip_treply(&sess->st, sess->sip, sess->msg, 486, "Busy Here"); } if (sess->req) { sip_request_cancel(sess->req); mem_ref(sess); wait = true; } if (sess->replyl.head) { mem_ref(sess); wait = true; } if (sess->requestl.head) { mem_ref(sess); wait = true; } return wait; } static bool terminate(struct sipsess *sess) { sess->terminated = 2; if (!sess->established || sess->peerterm) return false; if (sipsess_bye(sess, true)) return false; mem_ref(sess); return true; } static void destructor(void *arg) { struct sipsess *sess = arg; switch (sess->terminated) { case 0: if (termwait(sess)) return; /*@fallthrough@*/ case 1: if (terminate(sess)) return; break; } hash_unlink(&sess->he); tmr_cancel(&sess->tmr); list_flush(&sess->replyl); list_flush(&sess->requestl); mem_deref((void *)sess->msg); mem_deref(sess->req); mem_deref(sess->dlg); mem_deref(sess->auth); mem_deref(sess->cuser); mem_deref(sess->ctype); mem_deref(sess->close_hdrs); mem_deref(sess->hdrs); mem_deref(sess->desc); mem_deref(sess->sock); mem_deref(sess->sip); mem_deref(sess->st); } int sipsess_alloc(struct sipsess **sessp, struct sipsess_sock *sock, const char *cuser, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_progr_h *progrh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg) { struct sipsess *sess; int err; sess = mem_zalloc(sizeof(*sess), destructor); if (!sess) return ENOMEM; err = sip_auth_alloc(&sess->auth, authh, aarg, aref); if (err) goto out; err = str_dup(&sess->cuser, cuser); if (err) goto out; err = str_dup(&sess->ctype, ctype); if (err) goto out; sess->sock = mem_ref(sock); sess->desc = mem_ref(desc); sess->sip = mem_ref(sock->sip); sess->offerh = offerh ? offerh : internal_offer_handler; sess->answerh = answerh ? answerh : internal_answer_handler; sess->progrh = progrh ? progrh : internal_progress_handler; sess->estabh = estabh ? estabh : internal_establish_handler; sess->infoh = infoh; sess->referh = referh; sess->closeh = closeh ? closeh : internal_close_handler; sess->arg = arg; out: if (err) mem_deref(sess); else *sessp = sess; return err; } void sipsess_terminate(struct sipsess *sess, int err, const struct sip_msg *msg) { sipsess_close_h *closeh; void *arg; if (sess->terminated) return; closeh = sess->closeh; arg = sess->arg; if (!termwait(sess)) (void)terminate(sess); closeh(err, msg, arg); } /** * Get the SIP dialog from a SIP Session * * @param sess SIP Session * * @return SIP Dialog object */ struct sip_dialog *sipsess_dialog(const struct sipsess *sess) { return sess ? sess->dlg : NULL; } /** * Set extra SIP headers for inclusion in Session "close" messages * like BYE and 200 OK. Multiple headers can be included. * * @param sess SIP Session * @param hdrs Formatted strings with extra SIP Headers * * @return 0 if success, otherwise errorcode */ int sipsess_set_close_headers(struct sipsess *sess, const char *hdrs, ...) { int err = 0; va_list ap; if (!sess) return EINVAL; sess->close_hdrs = mem_deref(sess->close_hdrs); if (hdrs) { va_start(ap, hdrs); err = re_vsdprintf(&sess->close_hdrs, hdrs, ap); va_end(ap); } return err; } re-1.1.0/src/sipsess/sipsess.h000066400000000000000000000046011373627245400162570ustar00rootroot00000000000000/** * @file sipsess.h SIP Session Private Interface * * Copyright (C) 2010 Creytiv.com */ struct sipsess { struct le he; struct tmr tmr; struct list replyl; struct list requestl; struct sip_loopstate ls; struct sipsess_sock *sock; const struct sip_msg *msg; struct sip_request *req; struct sip_dialog *dlg; struct sip_strans *st; struct sip_auth *auth; struct sip *sip; char *cuser; char *ctype; char *close_hdrs; struct mbuf *hdrs; struct mbuf *desc; sipsess_offer_h *offerh; sipsess_answer_h *answerh; sipsess_progr_h *progrh; sipsess_estab_h *estabh; sipsess_info_h *infoh; sipsess_refer_h *referh; sipsess_close_h *closeh; void *arg; bool owner; bool sent_offer; bool awaiting_answer; bool modify_pending; bool established; bool peerterm; int terminated; }; struct sipsess_sock { struct sip_lsnr *lsnr_resp; struct sip_lsnr *lsnr_req; struct hash *ht_sess; struct hash *ht_ack; struct sip *sip; sipsess_conn_h *connh; void *arg; }; struct sipsess_request { struct le le; struct sip_loopstate ls; struct sipsess *sess; struct sip_request *req; char *ctype; struct mbuf *body; sip_resp_h *resph; void *arg; }; int sipsess_alloc(struct sipsess **sessp, struct sipsess_sock *sock, const char *cuser, const char *ctype, struct mbuf *desc, sip_auth_h *authh, void *aarg, bool aref, sipsess_offer_h *offerh, sipsess_answer_h *answerh, sipsess_progr_h *progrh, sipsess_estab_h *estabh, sipsess_info_h *infoh, sipsess_refer_h *referh, sipsess_close_h *closeh, void *arg); void sipsess_terminate(struct sipsess *sess, int err, const struct sip_msg *msg); int sipsess_ack(struct sipsess_sock *sock, struct sip_dialog *dlg, uint32_t cseq, struct sip_auth *auth, const char *ctype, struct mbuf *desc); int sipsess_ack_again(struct sipsess_sock *sock, const struct sip_msg *msg); int sipsess_reply_2xx(struct sipsess *sess, const struct sip_msg *msg, uint16_t scode, const char *reason, struct mbuf *desc, const char *fmt, va_list *ap); int sipsess_reply_ack(struct sipsess *sess, const struct sip_msg *msg, bool *awaiting_answer); int sipsess_reinvite(struct sipsess *sess, bool reset_ls); int sipsess_bye(struct sipsess *sess, bool reset_ls); int sipsess_request_alloc(struct sipsess_request **reqp, struct sipsess *sess, const char *ctype, struct mbuf *body, sip_resp_h *resph, void *arg); re-1.1.0/src/srtp/000077500000000000000000000000001373627245400137135ustar00rootroot00000000000000re-1.1.0/src/srtp/README000066400000000000000000000021701373627245400145730ustar00rootroot00000000000000SRTP module ----------- The SRTP module implements Secure RTP as defined in RFC 3711. It provides a clean and user friendly API and can be used as a standalone module. Requirements and features: RFC 3711 yes RFC 6188 yes Multiple Master keys: no Key derivation rate: 0 (zero) Salting keys: yes SRTP protection: yes SRTCP protection: yes Replay protection: yes Encryption: yes Authentication: yes MKI (Master Key Identifier): no Authentication tag length: 32-bit and 80-bit ROC (Roll Over Counter): yes Master key lifetime: no Multiple SSRCs: yes Performance: better than libsrtp Cryptographic transforms: - AES in Counter mode: yes - AES in f8-mode: no - NULL Cipher: no Authentication transform: - HMAC-SHA1: yes - NULL auth: no master key lengths: - 128 bits yes - 192 bits no - 256 bits yes re-1.1.0/src/srtp/misc.c000066400000000000000000000052361373627245400150200ustar00rootroot00000000000000/** * @file srtp/misc.c SRTP functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "srtp.h" /* * Appendix A: Pseudocode for Index Determination * * In the following, signed arithmetic is assumed. */ uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq) { int v; if (s_l < 32768) { if ((int)seq - (int)s_l > 32768) v = (roc-1) & 0xffffffffu; else v = roc; } else { if ((int)s_l - 32768 > seq) v = (roc+1) & 0xffffffffu; else v = roc; } return seq + v*65536; } int srtp_derive(uint8_t *out, size_t out_len, uint8_t label, const uint8_t *master_key, size_t key_bytes, const uint8_t *master_salt, size_t salt_bytes) { uint8_t x[AES_BLOCK_SIZE] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; static const uint8_t null[AES_BLOCK_SIZE * 2]; struct aes *aes; int err; if (!out || !master_key || !master_salt) return EINVAL; if (out_len > sizeof(null) || salt_bytes > sizeof(x)) return EINVAL; memcpy(x, master_salt, salt_bytes); x[7] ^= label; /* NOTE: Counter Mode is used for both CTR and GCM */ err = aes_alloc(&aes, AES_MODE_CTR, master_key, key_bytes*8, x); if (err) return err; err = aes_encr(aes, out, null, out_len); mem_deref(aes); return err; } void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s, uint32_t ssrc, uint64_t ix) { if (!iv || !k_s) return; iv->u32[0] = k_s->u32[0]; iv->u32[1] = k_s->u32[1] ^ htonl(ssrc); iv->u32[2] = k_s->u32[2] ^ htonl((uint32_t)(ix>>16)); iv->u16[6] = k_s->u16[6] ^ htons((uint16_t)ix); iv->u16[7] = 0; } /* * NOTE: The IV for AES-GCM is 12 bytes */ void srtp_iv_calc_gcm(union vect128 *iv, const union vect128 *k_s, uint32_t ssrc, uint64_t ix) { if (!iv || !k_s) return; iv->u16[0] = k_s->u16[0]; iv->u16[1] = k_s->u16[1] ^ htons(ssrc >> 16); iv->u16[2] = k_s->u16[2] ^ htons(ssrc & 0xffff); iv->u16[3] = k_s->u16[3] ^ htons((ix >> 32) & 0xffff); iv->u16[4] = k_s->u16[4] ^ htons((ix >> 16) & 0xffff); iv->u16[5] = k_s->u16[5] ^ htons(ix & 0xffff); } const char *srtp_suite_name(enum srtp_suite suite) { switch (suite) { case SRTP_AES_CM_128_HMAC_SHA1_32: return "AES_CM_128_HMAC_SHA1_32"; case SRTP_AES_CM_128_HMAC_SHA1_80: return "AES_CM_128_HMAC_SHA1_80"; case SRTP_AES_256_CM_HMAC_SHA1_32: return "AES_256_CM_HMAC_SHA1_32"; case SRTP_AES_256_CM_HMAC_SHA1_80: return "AES_256_CM_HMAC_SHA1_80"; case SRTP_AES_128_GCM: return "AEAD_AES_128_GCM"; case SRTP_AES_256_GCM: return "AEAD_AES_256_GCM"; default: return "?"; } } re-1.1.0/src/srtp/mod.mk000066400000000000000000000002321373627245400150200ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += srtp/misc.c SRCS += srtp/replay.c SRCS += srtp/srtcp.c SRCS += srtp/srtp.c SRCS += srtp/stream.c re-1.1.0/src/srtp/replay.c000066400000000000000000000020121373627245400153460ustar00rootroot00000000000000/** * @file srtp/replay.c SRTP replay protection * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include "srtp.h" enum { SRTP_WINDOW_SIZE = 64 }; void srtp_replay_init(struct replay *replay) { if (!replay) return; replay->bitmap = 0; replay->lix = 0; } /* * Returns false if packet disallowed, true if packet permitted */ bool srtp_replay_check(struct replay *replay, uint64_t ix) { uint64_t diff; if (!replay) return false; if (ix > replay->lix) { diff = ix - replay->lix; if (diff < SRTP_WINDOW_SIZE) { /* In window */ replay->bitmap <<= diff; replay->bitmap |= 1; /* set bit for this packet */ } else replay->bitmap = 1; replay->lix = ix; return true; } diff = replay->lix - ix; if (diff >= SRTP_WINDOW_SIZE) return false; if (replay->bitmap & (1ULL << diff)) return false; /* already seen */ /* mark as seen */ replay->bitmap |= (1ULL << diff); return true; } re-1.1.0/src/srtp/srtcp.c000066400000000000000000000114761373627245400152230ustar00rootroot00000000000000/** * @file srtcp.c Secure Real-time Transport Control Protocol (SRTCP) * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include "srtp.h" static int get_rtcp_ssrc(uint32_t *ssrc, struct mbuf *mb) { if (mbuf_get_left(mb) < 8) return EBADMSG; mbuf_advance(mb, 4); *ssrc = ntohl(mbuf_read_u32(mb)); return 0; } int srtcp_encrypt(struct srtp *srtp, struct mbuf *mb) { struct srtp_stream *strm; struct comp *rtcp; uint32_t ssrc; size_t start; uint32_t ep = 0; int err; if (!srtp || !mb) return EINVAL; rtcp = &srtp->rtcp; start = mb->pos; err = get_rtcp_ssrc(&ssrc, mb); if (err) return err; err = stream_get(&strm, srtp, ssrc); if (err) return err; strm->rtcp_index = (strm->rtcp_index+1) & 0x7fffffff; if (rtcp->aes && rtcp->mode == AES_MODE_CTR) { union vect128 iv; uint8_t *p = mbuf_buf(mb); srtp_iv_calc(&iv, &rtcp->k_s, ssrc, strm->rtcp_index); aes_set_iv(rtcp->aes, iv.u8); err = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb)); if (err) return err; ep = 1; } else if (rtcp->aes && rtcp->mode == AES_MODE_GCM) { union vect128 iv; uint8_t *p = mbuf_buf(mb); uint8_t tag[GCM_TAGLEN]; const uint32_t ix_be = htonl(1<<31 | strm->rtcp_index); srtp_iv_calc_gcm(&iv, &rtcp->k_s, ssrc, strm->rtcp_index); aes_set_iv(rtcp->aes, iv.u8); /* The RTCP Header and Index is Associated Data */ err = aes_encr(rtcp->aes, NULL, &mb->buf[start], mb->pos - start); err |= aes_encr(rtcp->aes, NULL, (void *)&ix_be, sizeof(ix_be)); if (err) return err; err = aes_encr(rtcp->aes, p, p, mbuf_get_left(mb)); if (err) return err; err = aes_get_authtag(rtcp->aes, tag, sizeof(tag)); if (err) return err; mb->pos = mb->end; err = mbuf_write_mem(mb, tag, sizeof(tag)); if (err) return err; ep = 1; } /* append E-bit and SRTCP-index */ mb->pos = mb->end; err = mbuf_write_u32(mb, htonl(ep<<31 | strm->rtcp_index)); if (err) return err; if (rtcp->hmac) { uint8_t tag[SHA_DIGEST_LENGTH]; mb->pos = start; err = hmac_digest(rtcp->hmac, tag, sizeof(tag), mbuf_buf(mb), mbuf_get_left(mb)); if (err) return err; mb->pos = mb->end; err = mbuf_write_mem(mb, tag, rtcp->tag_len); if (err) return err; } mb->pos = start; return 0; } int srtcp_decrypt(struct srtp *srtp, struct mbuf *mb) { size_t start, eix_start, pld_start; struct srtp_stream *strm; struct comp *rtcp; uint32_t v, ix; uint32_t ssrc; bool ep; int err; if (!srtp || !mb) return EINVAL; rtcp = &srtp->rtcp; start = mb->pos; err = get_rtcp_ssrc(&ssrc, mb); if (err) return err; err = stream_get(&strm, srtp, ssrc); if (err) return err; pld_start = mb->pos; if (mbuf_get_left(mb) < (4 + rtcp->tag_len)) return EBADMSG; /* Read out E-Bit, SRTCP-index and Authentication Tag */ eix_start = mb->end - (4 + rtcp->tag_len); mb->pos = eix_start; v = ntohl(mbuf_read_u32(mb)); ep = (v >> 31) & 1; ix = v & 0x7fffffff; if (rtcp->hmac) { uint8_t tag[SHA_DIGEST_LENGTH], tag_pkt[SHA_DIGEST_LENGTH]; const size_t tag_start = mb->pos; err = mbuf_read_mem(mb, tag_pkt, rtcp->tag_len); if (err) return err; mb->pos = start; mb->end = tag_start; err = hmac_digest(rtcp->hmac, tag, sizeof(tag), mbuf_buf(mb), mbuf_get_left(mb)); if (err) return err; if (0 != memcmp(tag, tag_pkt, rtcp->tag_len)) return EAUTH; /* * SRTCP replay protection is as defined in Section 3.3.2, * but using the SRTCP index as the index i and a separate * Replay List that is specific to SRTCP. */ if (!srtp_replay_check(&strm->replay_rtcp, ix)) return EALREADY; } mb->end = eix_start; if (rtcp->aes && ep && rtcp->mode == AES_MODE_CTR) { union vect128 iv; uint8_t *p; mb->pos = pld_start; p = mbuf_buf(mb); srtp_iv_calc(&iv, &rtcp->k_s, ssrc, ix); aes_set_iv(rtcp->aes, iv.u8); err = aes_decr(rtcp->aes, p, p, mbuf_get_left(mb)); if (err) return err; } else if (rtcp->aes && ep && rtcp->mode == AES_MODE_GCM) { union vect128 iv; size_t tag_start; uint8_t *p; srtp_iv_calc_gcm(&iv, &rtcp->k_s, ssrc, ix); aes_set_iv(rtcp->aes, iv.u8); /* The RTP Header is Associated Data */ err = aes_decr(rtcp->aes, NULL, &mb->buf[start], pld_start - start); err |= aes_decr(rtcp->aes, NULL, &mb->buf[eix_start], 4); if (err) return err; mb->pos = pld_start; p = mbuf_buf(mb); if (mbuf_get_left(mb) < GCM_TAGLEN) return EBADMSG; tag_start = mb->end - GCM_TAGLEN; err = aes_decr(rtcp->aes, p, p, tag_start - pld_start); if (err) return err; err = aes_authenticate(rtcp->aes, &mb->buf[tag_start], GCM_TAGLEN); if (err) return err; mb->end = tag_start; } mb->pos = start; return 0; } re-1.1.0/src/srtp/srtp.c000066400000000000000000000200041373627245400150430ustar00rootroot00000000000000/** * @file srtp.c Secure Real-time Transport Protocol (SRTP) * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "srtp.h" /** SRTP protocol values */ enum { MAX_KEYLEN = 32, /**< Maximum keylength in bytes */ }; static inline int seq_diff(uint16_t x, uint16_t y) { return (int)y - (int)x; } static int comp_init(struct comp *c, unsigned offs, const uint8_t *key, size_t key_b, const uint8_t *s, size_t s_b, size_t tag_len, bool encrypted, bool hash, enum aes_mode mode) { uint8_t k_e[MAX_KEYLEN], k_a[SHA_DIGEST_LENGTH]; int err = 0; if (key_b > sizeof(k_e)) return EINVAL; if (tag_len > SHA_DIGEST_LENGTH) return EINVAL; c->tag_len = tag_len; c->mode = mode; err |= srtp_derive(k_e, key_b, 0x00+offs, key, key_b, s, s_b); err |= srtp_derive(k_a, sizeof(k_a), 0x01+offs, key, key_b, s, s_b); err |= srtp_derive(c->k_s.u8, 14, 0x02+offs, key, key_b, s, s_b); if (err) return err; if (encrypted) { err = aes_alloc(&c->aes, mode, k_e, key_b*8, NULL); if (err) return err; } if (hash) { err = hmac_create(&c->hmac, HMAC_HASH_SHA1, k_a, sizeof(k_a)); if (err) return err; } return err; } static void destructor(void *arg) { struct srtp *srtp = arg; mem_deref(srtp->rtp.aes); mem_deref(srtp->rtcp.aes); mem_deref(srtp->rtp.hmac); mem_deref(srtp->rtcp.hmac); list_flush(&srtp->streaml); } int srtp_alloc(struct srtp **srtpp, enum srtp_suite suite, const uint8_t *key, size_t key_bytes, int flags) { struct srtp *srtp; const uint8_t *master_salt; size_t cipher_bytes, salt_bytes, auth_bytes; enum aes_mode mode; bool hash; int err = 0; if (!srtpp || !key) return EINVAL; switch (suite) { case SRTP_AES_CM_128_HMAC_SHA1_80: mode = AES_MODE_CTR; cipher_bytes = 16; salt_bytes = 14; auth_bytes = 10; hash = true; break; case SRTP_AES_CM_128_HMAC_SHA1_32: mode = AES_MODE_CTR; cipher_bytes = 16; salt_bytes = 14; auth_bytes = 4; hash = true; break; case SRTP_AES_256_CM_HMAC_SHA1_80: mode = AES_MODE_CTR; cipher_bytes = 32; salt_bytes = 14; auth_bytes = 10; hash = true; break; case SRTP_AES_256_CM_HMAC_SHA1_32: mode = AES_MODE_CTR; cipher_bytes = 32; salt_bytes = 14; auth_bytes = 4; hash = true; break; case SRTP_AES_128_GCM: mode = AES_MODE_GCM; cipher_bytes = 16; salt_bytes = 12; auth_bytes = 0; hash = false; break; case SRTP_AES_256_GCM: mode = AES_MODE_GCM; cipher_bytes = 32; salt_bytes = 12; auth_bytes = 0; hash = false; break; default: return ENOTSUP; }; if ((cipher_bytes + salt_bytes) != key_bytes) return EINVAL; master_salt = &key[cipher_bytes]; srtp = mem_zalloc(sizeof(*srtp), destructor); if (!srtp) return ENOMEM; err |= comp_init(&srtp->rtp, 0, key, cipher_bytes, master_salt, salt_bytes, auth_bytes, true, hash, mode); err |= comp_init(&srtp->rtcp, 3, key, cipher_bytes, master_salt, salt_bytes, auth_bytes, !(flags & SRTP_UNENCRYPTED_SRTCP), hash, mode); if (err) goto out; out: if (err) mem_deref(srtp); else *srtpp = srtp; return err; } int srtp_encrypt(struct srtp *srtp, struct mbuf *mb) { struct srtp_stream *strm; struct rtp_header hdr; struct comp *comp; size_t start; uint64_t ix; int err; if (!srtp || !mb) return EINVAL; comp = &srtp->rtp; start = mb->pos; err = rtp_hdr_decode(&hdr, mb); if (err) return err; err = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq); if (err) return err; /* Roll-Over Counter (ROC) */ if (seq_diff(strm->s_l, hdr.seq) <= -32768) { strm->roc++; strm->s_l = 0; } ix = 65536ULL * strm->roc + hdr.seq; if (comp->aes && comp->mode == AES_MODE_CTR) { union vect128 iv; uint8_t *p = mbuf_buf(mb); srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix); aes_set_iv(comp->aes, iv.u8); err = aes_encr(comp->aes, p, p, mbuf_get_left(mb)); if (err) return err; } else if (comp->aes && comp->mode == AES_MODE_GCM) { union vect128 iv; uint8_t *p = mbuf_buf(mb); uint8_t tag[GCM_TAGLEN]; srtp_iv_calc_gcm(&iv, &comp->k_s, strm->ssrc, ix); aes_set_iv(comp->aes, iv.u8); /* The RTP Header is Associated Data */ err = aes_encr(comp->aes, NULL, &mb->buf[start], mb->pos - start); if (err) return err; err = aes_encr(comp->aes, p, p, mbuf_get_left(mb)); if (err) return err; err = aes_get_authtag(comp->aes, tag, sizeof(tag)); if (err) return err; mb->pos = mb->end; err = mbuf_write_mem(mb, tag, sizeof(tag)); if (err) return err; } if (comp->hmac) { const size_t tag_start = mb->end; uint8_t tag[SHA_DIGEST_LENGTH]; mb->pos = tag_start; err = mbuf_write_u32(mb, htonl(strm->roc)); if (err) return err; mb->pos = start; err = hmac_digest(comp->hmac, tag, sizeof(tag), mbuf_buf(mb), mbuf_get_left(mb)); if (err) return err; mb->pos = mb->end = tag_start; err = mbuf_write_mem(mb, tag, comp->tag_len); if (err) return err; } if (hdr.seq > strm->s_l) strm->s_l = hdr.seq; mb->pos = start; return 0; } int srtp_decrypt(struct srtp *srtp, struct mbuf *mb) { struct srtp_stream *strm; struct rtp_header hdr; struct comp *comp; uint64_t ix; size_t start; int diff; int err; if (!srtp || !mb) return EINVAL; comp = &srtp->rtp; start = mb->pos; err = rtp_hdr_decode(&hdr, mb); if (err) return err; err = stream_get_seq(&strm, srtp, hdr.ssrc, hdr.seq); if (err) return err; diff = seq_diff(strm->s_l, hdr.seq); if (diff > 32768) return ETIMEDOUT; /* Roll-Over Counter (ROC) */ if (diff <= -32768) { strm->roc++; strm->s_l = 0; } ix = srtp_get_index(strm->roc, strm->s_l, hdr.seq); if (comp->hmac) { uint8_t tag_calc[SHA_DIGEST_LENGTH]; uint8_t tag_pkt[SHA_DIGEST_LENGTH]; size_t pld_start, tag_start; if (mbuf_get_left(mb) < comp->tag_len) return EBADMSG; pld_start = mb->pos; tag_start = mb->end - comp->tag_len; mb->pos = tag_start; err = mbuf_read_mem(mb, tag_pkt, comp->tag_len); if (err) return err; mb->pos = mb->end = tag_start; err = mbuf_write_u32(mb, htonl(strm->roc)); if (err) return err; mb->pos = start; err = hmac_digest(comp->hmac, tag_calc, sizeof(tag_calc), mbuf_buf(mb), mbuf_get_left(mb)); if (err) return err; mb->pos = pld_start; mb->end = tag_start; if (0 != memcmp(tag_calc, tag_pkt, comp->tag_len)) return EAUTH; /* * 3.3.2. Replay Protection * * Secure replay protection is only possible when * integrity protection is present. */ if (!srtp_replay_check(&strm->replay_rtp, ix)) return EALREADY; } if (comp->aes && comp->mode == AES_MODE_CTR) { union vect128 iv; uint8_t *p = mbuf_buf(mb); srtp_iv_calc(&iv, &comp->k_s, strm->ssrc, ix); aes_set_iv(comp->aes, iv.u8); err = aes_decr(comp->aes, p, p, mbuf_get_left(mb)); if (err) return err; } else if (comp->aes && comp->mode == AES_MODE_GCM) { union vect128 iv; uint8_t *p = mbuf_buf(mb); size_t tag_start; srtp_iv_calc_gcm(&iv, &comp->k_s, strm->ssrc, ix); aes_set_iv(comp->aes, iv.u8); /* The RTP Header is Associated Data */ err = aes_decr(comp->aes, NULL, &mb->buf[start], mb->pos - start); if (err) return err; if (mbuf_get_left(mb) < GCM_TAGLEN) return EBADMSG; tag_start = mb->end - GCM_TAGLEN; err = aes_decr(comp->aes, p, p, tag_start - mb->pos); if (err) return err; err = aes_authenticate(comp->aes, &mb->buf[tag_start], GCM_TAGLEN); if (err) return err; mb->end = tag_start; /* * 3.3.2. Replay Protection * * Secure replay protection is only possible when * integrity protection is present. */ if (!srtp_replay_check(&strm->replay_rtp, ix)) return EALREADY; } if (hdr.seq > strm->s_l) strm->s_l = hdr.seq; mb->pos = start; return 0; } re-1.1.0/src/srtp/srtp.h000066400000000000000000000045171373627245400150630ustar00rootroot00000000000000/** * @file srtp.h Secure Real-time Transport Protocol (SRTP) -- internal * * Copyright (C) 2010 Creytiv.com */ /** SRTP Protocol values */ enum { GCM_TAGLEN = 16, /**< GCM taglength in bytes */ }; /** Defines a 128-bit vector in network order */ union vect128 { uint64_t u64[ 2]; uint32_t u32[ 4]; uint16_t u16[ 8]; uint8_t u8[16]; }; /** Replay protection */ struct replay { uint64_t bitmap; /**< Session state - must be 64 bits */ uint64_t lix; /**< Last received index */ }; /** SRTP stream/context -- shared state between RTP/RTCP */ struct srtp_stream { struct le le; /**< Linked-list element */ struct replay replay_rtp; /**< recv -- replay protection for RTP */ struct replay replay_rtcp; /**< recv -- replay protection for RTCP */ uint32_t ssrc; /**< SSRC -- lookup key */ uint32_t roc; /**< send/recv Roll-Over Counter (ROC) */ uint16_t s_l; /**< send/recv -- highest SEQ number */ bool s_l_set; /**< True if s_l has been set */ uint32_t rtcp_index; /**< RTCP-index for sending (31-bits) */ }; /** SRTP Session */ struct srtp { struct comp { struct aes *aes; /**< AES Context */ enum aes_mode mode; /**< AES encryption mode */ struct hmac *hmac; /**< HMAC Context */ union vect128 k_s; /**< Derived salting key (14 bytes) */ size_t tag_len; /**< CTR Auth. tag length [bytes] */ } rtp, rtcp; struct list streaml; /**< SRTP-streams (struct srtp_stream) */ }; int stream_get(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc); int stream_get_seq(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc, uint16_t seq); int srtp_derive(uint8_t *out, size_t out_len, uint8_t label, const uint8_t *master_key, size_t key_bytes, const uint8_t *master_salt, size_t salt_bytes); void srtp_iv_calc(union vect128 *iv, const union vect128 *k_s, uint32_t ssrc, uint64_t ix); void srtp_iv_calc_gcm(union vect128 *iv, const union vect128 *k_s, uint32_t ssrc, uint64_t ix); uint64_t srtp_get_index(uint32_t roc, uint16_t s_l, uint16_t seq); /* Replay protection */ void srtp_replay_init(struct replay *replay); bool srtp_replay_check(struct replay *replay, uint64_t ix); re-1.1.0/src/srtp/stream.c000066400000000000000000000035321373627245400153550ustar00rootroot00000000000000/** * @file srtp/stream.c Secure Real-time Transport Protocol (SRTP) -- stream * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include "srtp.h" /** SRTP protocol values */ #ifndef SRTP_MAX_STREAMS #define SRTP_MAX_STREAMS (8) /**< Maximum number of SRTP streams */ #endif static void stream_destructor(void *arg) { struct srtp_stream *strm = arg; list_unlink(&strm->le); } static struct srtp_stream *stream_find(struct srtp *srtp, uint32_t ssrc) { struct le *le; for (le = srtp->streaml.head; le; le = le->next) { struct srtp_stream *strm = le->data; if (strm->ssrc == ssrc) return strm; } return NULL; } static int stream_new(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc) { struct srtp_stream *strm; if (list_count(&srtp->streaml) >= SRTP_MAX_STREAMS) return ENOSR; strm = mem_zalloc(sizeof(*strm), stream_destructor); if (!strm) return ENOMEM; strm->ssrc = ssrc; srtp_replay_init(&strm->replay_rtp); srtp_replay_init(&strm->replay_rtcp); list_append(&srtp->streaml, &strm->le, strm); if (strmp) *strmp = strm; return 0; } int stream_get(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc) { struct srtp_stream *strm; if (!strmp || !srtp) return EINVAL; strm = stream_find(srtp, ssrc); if (strm) { *strmp = strm; return 0; } return stream_new(strmp, srtp, ssrc); } int stream_get_seq(struct srtp_stream **strmp, struct srtp *srtp, uint32_t ssrc, uint16_t seq) { struct srtp_stream *strm; int err; if (!strmp || !srtp) return EINVAL; err = stream_get(&strm, srtp, ssrc); if (err) return err; /* Set the initial sequence number once only */ if (!strm->s_l_set) { strm->s_l = seq; strm->s_l_set = true; } *strmp = strm; return 0; } re-1.1.0/src/stun/000077500000000000000000000000001373627245400137145ustar00rootroot00000000000000re-1.1.0/src/stun/addr.c000066400000000000000000000041501373627245400147720ustar00rootroot00000000000000/** * @file stun/addr.c STUN Address encoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include "stun.h" static void in6_xor_tid(uint8_t *in6, const uint8_t *tid) { int i; /* XOR with Magic Cookie (alignment safe) */ in6[0] ^= 0x21; in6[1] ^= 0x12; in6[2] ^= 0xa4; in6[3] ^= 0x42; for (i=0; i>16) : sa_port(addr); switch (sa_af(addr)) { case AF_INET: addr4 = tid ? sa_in(addr)^STUN_MAGIC_COOKIE : sa_in(addr); err |= mbuf_write_u8(mb, 0); err |= mbuf_write_u8(mb, STUN_AF_IPv4); err |= mbuf_write_u16(mb, htons(port)); err |= mbuf_write_u32(mb, htonl(addr4)); break; #ifdef HAVE_INET6 case AF_INET6: sa_in6(addr, addr6); if (tid) in6_xor_tid(addr6, tid); err |= mbuf_write_u8(mb, 0); err |= mbuf_write_u8(mb, STUN_AF_IPv6); err |= mbuf_write_u16(mb, htons(port)); err |= mbuf_write_mem(mb, addr6, 16); break; #endif default: err = EAFNOSUPPORT; break; } return err; } int stun_addr_decode(struct mbuf *mb, struct sa *addr, const uint8_t *tid) { uint8_t family, addr6[16]; uint32_t addr4; uint16_t port; if (!mb || !addr) return EINVAL; if (mbuf_get_left(mb) < 4) return EBADMSG; (void)mbuf_read_u8(mb); family = mbuf_read_u8(mb); port = ntohs(mbuf_read_u16(mb)); if (tid) port ^= STUN_MAGIC_COOKIE>>16; switch (family) { case STUN_AF_IPv4: if (mbuf_get_left(mb) < 4) return EBADMSG; addr4 = ntohl(mbuf_read_u32(mb)); if (tid) addr4 ^= STUN_MAGIC_COOKIE; sa_set_in(addr, addr4, port); break; case STUN_AF_IPv6: if (mbuf_get_left(mb) < 16) return EBADMSG; (void)mbuf_read_mem(mb, addr6, 16); if (tid) in6_xor_tid(addr6, tid); sa_set_in6(addr, addr6, port); break; default: return EAFNOSUPPORT; } return 0; } re-1.1.0/src/stun/attr.c000066400000000000000000000255031373627245400150370ustar00rootroot00000000000000/** * @file stun/attr.c STUN Attributes * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include "stun.h" static int str_decode(struct mbuf *mb, char **str, size_t len) { if (mbuf_get_left(mb) < len) return EBADMSG; return mbuf_strdup(mb, str, len); } static void destructor(void *arg) { struct stun_attr *attr = arg; switch (attr->type) { case STUN_ATTR_USERNAME: case STUN_ATTR_REALM: case STUN_ATTR_NONCE: case STUN_ATTR_SOFTWARE: mem_deref(attr->v.str); break; case STUN_ATTR_ERR_CODE: mem_deref(attr->v.err_code.reason); break; case STUN_ATTR_DATA: case STUN_ATTR_PADDING: mem_deref(attr->v.mb.buf); break; } list_unlink(&attr->le); } int stun_attr_encode(struct mbuf *mb, uint16_t type, const void *v, const uint8_t *tid, uint8_t padding) { const struct stun_change_req *ch_req = v; const struct stun_errcode *err_code = v; const struct stun_unknown_attr *ua = v; const uint32_t *num32 = v; const uint16_t *num16 = v; const uint8_t *num8 = v; const struct mbuf *mbd = v; size_t start, len; uint32_t i, n; int err = 0; if (!mb || !v) return EINVAL; mb->pos += 4; start = mb->pos; switch (type) { case STUN_ATTR_MAPPED_ADDR: case STUN_ATTR_ALT_SERVER: case STUN_ATTR_RESP_ORIGIN: case STUN_ATTR_OTHER_ADDR: tid = NULL; /*@fallthrough@*/ case STUN_ATTR_XOR_PEER_ADDR: case STUN_ATTR_XOR_RELAY_ADDR: case STUN_ATTR_XOR_MAPPED_ADDR: err |= stun_addr_encode(mb, v, tid); break; case STUN_ATTR_CHANGE_REQ: n = (uint32_t)ch_req->ip << 2 | (uint32_t)ch_req->port << 1; err |= mbuf_write_u32(mb, htonl(n)); break; case STUN_ATTR_USERNAME: case STUN_ATTR_REALM: case STUN_ATTR_NONCE: case STUN_ATTR_SOFTWARE: err |= mbuf_write_str(mb, v); break; case STUN_ATTR_MSG_INTEGRITY: err |= mbuf_write_mem(mb, v, 20); break; case STUN_ATTR_ERR_CODE: err |= mbuf_write_u16(mb, 0x00); err |= mbuf_write_u8(mb, err_code->code/100); err |= mbuf_write_u8(mb, err_code->code%100); err |= mbuf_write_str(mb, err_code->reason); break; case STUN_ATTR_UNKNOWN_ATTR: for (i=0; itypec; i++) err |= mbuf_write_u16(mb, htons(ua->typev[i])); break; case STUN_ATTR_CHANNEL_NUMBER: case STUN_ATTR_RESP_PORT: err |= mbuf_write_u16(mb, htons(*num16)); err |= mbuf_write_u16(mb, 0x0000); break; case STUN_ATTR_LIFETIME: case STUN_ATTR_PRIORITY: case STUN_ATTR_FINGERPRINT: err |= mbuf_write_u32(mb, htonl(*num32)); break; case STUN_ATTR_DATA: case STUN_ATTR_PADDING: if (mb == mbd) { mb->pos = mb->end; break; } err |= mbuf_write_mem(mb, mbuf_buf(mbd), mbuf_get_left(mbd)); break; case STUN_ATTR_REQ_ADDR_FAMILY: case STUN_ATTR_REQ_TRANSPORT: err |= mbuf_write_u8(mb, *num8); err |= mbuf_write_u8(mb, 0x00); err |= mbuf_write_u16(mb, 0x0000); break; case STUN_ATTR_EVEN_PORT: err |= mbuf_write_u8(mb, ((struct stun_even_port *)v)->r << 7); break; case STUN_ATTR_DONT_FRAGMENT: case STUN_ATTR_USE_CAND: /* no value */ break; case STUN_ATTR_RSV_TOKEN: case STUN_ATTR_CONTROLLED: case STUN_ATTR_CONTROLLING: err |= mbuf_write_u64(mb, sys_htonll(*(uint64_t *)v)); break; default: err = EINVAL; break; } /* header */ len = mb->pos - start; mb->pos = start - 4; err |= mbuf_write_u16(mb, htons(type)); err |= mbuf_write_u16(mb, htons(len)); mb->pos += len; /* padding */ while ((mb->pos - start) & 0x03) err |= mbuf_write_u8(mb, padding); return err; } int stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb, const uint8_t *tid, struct stun_unknown_attr *ua) { struct stun_attr *attr; size_t start, len; uint32_t i, n; int err = 0; if (!mb || !attrp) return EINVAL; if (mbuf_get_left(mb) < 4) return EBADMSG; attr = mem_zalloc(sizeof(*attr), destructor); if (!attr) return ENOMEM; attr->type = ntohs(mbuf_read_u16(mb)); len = ntohs(mbuf_read_u16(mb)); if (mbuf_get_left(mb) < len) goto badmsg; start = mb->pos; switch (attr->type) { case STUN_ATTR_MAPPED_ADDR: case STUN_ATTR_ALT_SERVER: case STUN_ATTR_RESP_ORIGIN: case STUN_ATTR_OTHER_ADDR: tid = NULL; /*@fallthrough@*/ case STUN_ATTR_XOR_PEER_ADDR: case STUN_ATTR_XOR_RELAY_ADDR: case STUN_ATTR_XOR_MAPPED_ADDR: err = stun_addr_decode(mb, &attr->v.sa, tid); break; case STUN_ATTR_CHANGE_REQ: if (len != 4) goto badmsg; n = ntohl(mbuf_read_u32(mb)); attr->v.change_req.ip = (n >> 2) & 0x1; attr->v.change_req.port = (n >> 1) & 0x1; break; case STUN_ATTR_USERNAME: case STUN_ATTR_REALM: case STUN_ATTR_NONCE: case STUN_ATTR_SOFTWARE: err = str_decode(mb, &attr->v.str, len); break; case STUN_ATTR_MSG_INTEGRITY: if (len != 20) goto badmsg; err = mbuf_read_mem(mb, attr->v.msg_integrity, 20); break; case STUN_ATTR_ERR_CODE: if (len < 4) goto badmsg; mb->pos += 2; attr->v.err_code.code = (mbuf_read_u8(mb) & 0x7) * 100; attr->v.err_code.code += mbuf_read_u8(mb); err = str_decode(mb, &attr->v.err_code.reason, len - 4); break; case STUN_ATTR_UNKNOWN_ATTR: for (i=0; i= ARRAY_SIZE(attr->v.unknown_attr.typev)) continue; attr->v.unknown_attr.typev[i] = type; attr->v.unknown_attr.typec++; } break; case STUN_ATTR_CHANNEL_NUMBER: case STUN_ATTR_RESP_PORT: if (len < 2) goto badmsg; attr->v.uint16 = ntohs(mbuf_read_u16(mb)); break; case STUN_ATTR_LIFETIME: case STUN_ATTR_PRIORITY: case STUN_ATTR_FINGERPRINT: if (len != 4) goto badmsg; attr->v.uint32 = ntohl(mbuf_read_u32(mb)); break; case STUN_ATTR_DATA: case STUN_ATTR_PADDING: attr->v.mb.buf = mem_ref(mb->buf); attr->v.mb.size = mb->size; attr->v.mb.pos = mb->pos; attr->v.mb.end = mb->pos + len; mb->pos += len; break; case STUN_ATTR_REQ_ADDR_FAMILY: case STUN_ATTR_REQ_TRANSPORT: if (len < 1) goto badmsg; attr->v.uint8 = mbuf_read_u8(mb); break; case STUN_ATTR_EVEN_PORT: if (len < 1) goto badmsg; attr->v.even_port.r = (mbuf_read_u8(mb) >> 7) & 0x1; break; case STUN_ATTR_DONT_FRAGMENT: case STUN_ATTR_USE_CAND: if (len > 0) goto badmsg; /* no value */ break; case STUN_ATTR_RSV_TOKEN: case STUN_ATTR_CONTROLLING: case STUN_ATTR_CONTROLLED: if (len != 8) goto badmsg; attr->v.uint64 = sys_ntohll(mbuf_read_u64(mb)); break; default: mb->pos += len; if (attr->type >= 0x8000) break; if (ua && ua->typec < ARRAY_SIZE(ua->typev)) ua->typev[ua->typec++] = attr->type; break; } if (err) goto error; /* padding */ while (((mb->pos - start) & 0x03) && mbuf_get_left(mb)) ++mb->pos; *attrp = attr; return 0; badmsg: err = EBADMSG; error: mem_deref(attr); return err; } /** * Get the name of a STUN attribute * * @param type STUN attribute type * * @return String with attribute name */ const char *stun_attr_name(uint16_t type) { switch (type) { case STUN_ATTR_MAPPED_ADDR: return "MAPPED-ADDRESS"; case STUN_ATTR_CHANGE_REQ: return "CHANGE-REQUEST"; case STUN_ATTR_USERNAME: return "USERNAME"; case STUN_ATTR_MSG_INTEGRITY: return "MESSAGE-INTEGRITY"; case STUN_ATTR_ERR_CODE: return "ERROR-CODE"; case STUN_ATTR_UNKNOWN_ATTR: return "UNKNOWN-ATTRIBUTE"; case STUN_ATTR_CHANNEL_NUMBER: return "CHANNEL-NUMBER"; case STUN_ATTR_LIFETIME: return "LIFETIME"; case STUN_ATTR_XOR_PEER_ADDR: return "XOR-PEER-ADDRESS"; case STUN_ATTR_DATA: return "DATA"; case STUN_ATTR_REALM: return "REALM"; case STUN_ATTR_NONCE: return "NONCE"; case STUN_ATTR_XOR_RELAY_ADDR: return "XOR-RELAYED-ADDRESS"; case STUN_ATTR_REQ_ADDR_FAMILY: return "REQUESTED-ADDRESS-FAMILY"; case STUN_ATTR_EVEN_PORT: return "EVEN_PORT"; case STUN_ATTR_REQ_TRANSPORT: return "REQUESTED-TRANSPORT"; case STUN_ATTR_DONT_FRAGMENT: return "DONT-FRAGMENT"; case STUN_ATTR_XOR_MAPPED_ADDR: return "XOR-MAPPED-ADDRESS"; case STUN_ATTR_RSV_TOKEN: return "RESERVATION-TOKEN"; case STUN_ATTR_PRIORITY: return "PRIORITY"; case STUN_ATTR_USE_CAND: return "USE-CANDIDATE"; case STUN_ATTR_PADDING: return "PADDING"; case STUN_ATTR_RESP_PORT: return "RESPONSE-PORT"; case STUN_ATTR_SOFTWARE: return "SOFTWARE"; case STUN_ATTR_ALT_SERVER: return "ALTERNATE-SERVER"; case STUN_ATTR_FINGERPRINT: return "FINGERPRINT"; case STUN_ATTR_CONTROLLING: return "ICE-CONTROLLING"; case STUN_ATTR_CONTROLLED: return "ICE-CONTROLLED"; case STUN_ATTR_RESP_ORIGIN: return "RESPONSE-ORIGIN"; case STUN_ATTR_OTHER_ADDR: return "OTHER-ADDR"; default: return "???"; } } void stun_attr_dump(const struct stun_attr *a) { uint32_t i; size_t len; if (!a) return; (void)re_printf(" %-25s", stun_attr_name(a->type)); switch (a->type) { case STUN_ATTR_MAPPED_ADDR: case STUN_ATTR_XOR_PEER_ADDR: case STUN_ATTR_XOR_RELAY_ADDR: case STUN_ATTR_XOR_MAPPED_ADDR: case STUN_ATTR_ALT_SERVER: case STUN_ATTR_RESP_ORIGIN: case STUN_ATTR_OTHER_ADDR: (void)re_printf("%J", &a->v.sa); break; case STUN_ATTR_CHANGE_REQ: (void)re_printf("ip=%u port=%u", a->v.change_req.ip, a->v.change_req.port); break; case STUN_ATTR_USERNAME: case STUN_ATTR_REALM: case STUN_ATTR_NONCE: case STUN_ATTR_SOFTWARE: (void)re_printf("%s", a->v.str); break; case STUN_ATTR_MSG_INTEGRITY: (void)re_printf("%w", a->v.msg_integrity, sizeof(a->v.msg_integrity)); break; case STUN_ATTR_ERR_CODE: (void)re_printf("%u %s", a->v.err_code.code, a->v.err_code.reason); break; case STUN_ATTR_UNKNOWN_ATTR: for (i=0; iv.unknown_attr.typec; i++) (void)re_printf("0x%04x ", a->v.unknown_attr.typev[i]); break; case STUN_ATTR_CHANNEL_NUMBER: (void)re_printf("0x%04x", a->v.uint16); break; case STUN_ATTR_LIFETIME: case STUN_ATTR_PRIORITY: (void)re_printf("%u", a->v.uint32); break; case STUN_ATTR_DATA: case STUN_ATTR_PADDING: len = min(mbuf_get_left(&a->v.mb), 16); (void)re_printf("%w%s (%zu bytes)", mbuf_buf(&a->v.mb), len, mbuf_get_left(&a->v.mb) > 16 ? "..." : "", mbuf_get_left(&a->v.mb)); break; case STUN_ATTR_REQ_ADDR_FAMILY: case STUN_ATTR_REQ_TRANSPORT: (void)re_printf("%u", a->v.uint8); break; case STUN_ATTR_EVEN_PORT: (void)re_printf("r=%u", a->v.even_port.r); break; case STUN_ATTR_DONT_FRAGMENT: case STUN_ATTR_USE_CAND: /* no value */ break; case STUN_ATTR_RSV_TOKEN: (void)re_printf("0x%016llx", a->v.rsv_token); break; case STUN_ATTR_RESP_PORT: (void)re_printf("%u", a->v.uint16); break; case STUN_ATTR_FINGERPRINT: (void)re_printf("0x%08x", a->v.fingerprint); break; case STUN_ATTR_CONTROLLING: case STUN_ATTR_CONTROLLED: (void)re_printf("%llu", a->v.uint64); break; default: (void)re_printf("???"); break; } (void)re_printf("\n"); } re-1.1.0/src/stun/ctrans.c000066400000000000000000000152651373627245400153630ustar00rootroot00000000000000/** * @file stun/ctrans.c STUN Client transactions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "stun.h" struct stun_ctrans { struct le le; struct tmr tmr; struct sa dst; uint8_t tid[STUN_TID_SIZE]; struct stun_ctrans **ctp; uint8_t *key; size_t keylen; void *sock; struct mbuf *mb; size_t pos; struct stun *stun; stun_resp_h *resph; void *arg; int proto; uint32_t txc; uint32_t ival; uint16_t met; }; static void completed(struct stun_ctrans *ct, int err, uint16_t scode, const char *reason, const struct stun_msg *msg) { stun_resp_h *resph = ct->resph; void *arg = ct->arg; list_unlink(&ct->le); tmr_cancel(&ct->tmr); if (ct->ctp) { *ct->ctp = NULL; ct->ctp = NULL; } ct->resph = NULL; /* must be destroyed before calling handler */ mem_deref(ct); if (resph) resph(err, scode, reason, msg, arg); } static void destructor(void *arg) { struct stun_ctrans *ct = arg; list_unlink(&ct->le); tmr_cancel(&ct->tmr); mem_deref(ct->key); mem_deref(ct->sock); mem_deref(ct->mb); } static void timeout_handler(void *arg) { struct stun_ctrans *ct = arg; const struct stun_conf *cfg = stun_conf(ct->stun); int err = ETIMEDOUT; if (ct->txc++ >= cfg->rc) goto error; ct->mb->pos = ct->pos; err = stun_send(ct->proto, ct->sock, &ct->dst, ct->mb); if (err) goto error; ct->ival = (ct->txc >= cfg->rc) ? cfg->rto * cfg->rm : ct->ival * 2; tmr_start(&ct->tmr, ct->ival, timeout_handler, ct); return; error: completed(ct, err, 0, NULL, NULL); } static bool match_handler(struct le *le, void *arg) { struct stun_ctrans *ct = le->data; struct stun_msg *msg = arg; if (ct->met != stun_msg_method(msg)) return false; if (memcmp(ct->tid, stun_msg_tid(msg), STUN_TID_SIZE)) return false; return true; } static void udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { struct stun *stun = arg; (void)src; (void)stun_recv(stun, mb); } static void tcp_recv_handler(struct mbuf *mb, void *arg) { struct stun_ctrans *ct = arg; (void)stun_recv(ct->stun, mb); } static void tcp_estab_handler(void *arg) { struct stun_ctrans *ct = arg; int err; err = tcp_send(ct->sock, ct->mb); if (!err) return; completed(ct, err, 0, NULL, NULL); } static void tcp_close_handler(int err, void *arg) { struct stun_ctrans *ct = arg; completed(ct, err, 0, NULL, NULL); } /** * Handle an incoming STUN message to a Client Transaction * * @param stun STUN instance * @param msg STUN message * @param ua Unknown attributes * * @return 0 if success, otherwise errorcode */ int stun_ctrans_recv(struct stun *stun, const struct stun_msg *msg, const struct stun_unknown_attr *ua) { struct stun_errcode ec = {0, "OK"}; struct stun_attr *errcode; struct stun_ctrans *ct; int err = 0, herr = 0; if (!stun || !msg || !ua) return EINVAL; switch (stun_msg_class(msg)) { case STUN_CLASS_ERROR_RESP: errcode = stun_msg_attr(msg, STUN_ATTR_ERR_CODE); if (!errcode) herr = EPROTO; else ec = errcode->v.err_code; /*@fallthrough@*/ case STUN_CLASS_SUCCESS_RESP: ct = list_ledata(list_apply(&stun->ctl, true, match_handler, (void *)msg)); if (!ct) { err = ENOENT; break; } switch (ec.code) { case 401: case 438: break; default: if (!ct->key) break; err = stun_msg_chk_mi(msg, ct->key, ct->keylen); break; } if (err) break; if (!herr && ua->typec > 0) herr = EPROTO; completed(ct, herr, ec.code, ec.reason, msg); break; default: break; } return err; } int stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto, void *sock, const struct sa *dst, struct mbuf *mb, const uint8_t tid[], uint16_t met, const uint8_t *key, size_t keylen, stun_resp_h *resph, void *arg) { struct stun_ctrans *ct; int err = 0; if (!stun || !mb) return EINVAL; ct = mem_zalloc(sizeof(*ct), destructor); if (!ct) return ENOMEM; list_append(&stun->ctl, &ct->le, ct); memcpy(ct->tid, tid, STUN_TID_SIZE); ct->proto = proto; ct->sock = mem_ref(sock); ct->mb = mem_ref(mb); ct->pos = mb->pos; ct->stun = stun; ct->met = met; if (key) { ct->key = mem_alloc(keylen, NULL); if (!ct->key) { err = ENOMEM; goto out; } memcpy(ct->key, key, keylen); ct->keylen = keylen; } switch (proto) { case IPPROTO_UDP: if (!dst) { err = EINVAL; break; } ct->dst = *dst; ct->ival = stun_conf(stun)->rto; tmr_start(&ct->tmr, ct->ival, timeout_handler, ct); if (!sock) { err = udp_listen((struct udp_sock **)&ct->sock, NULL, udp_recv_handler, stun); if (err) break; } ct->txc = 1; err = udp_send(ct->sock, dst, mb); break; case IPPROTO_TCP: ct->txc = stun_conf(stun)->rc; tmr_start(&ct->tmr, stun_conf(stun)->ti, timeout_handler, ct); if (sock) { err = tcp_send(sock, mb); break; } err = tcp_connect((struct tcp_conn **)&ct->sock, dst, tcp_estab_handler, tcp_recv_handler, tcp_close_handler, ct); break; #ifdef USE_DTLS case STUN_TRANSP_DTLS: if (!sock) { err = EINVAL; break; } ct->ival = stun_conf(stun)->rto; tmr_start(&ct->tmr, ct->ival, timeout_handler, ct); ct->txc = 1; err = dtls_send(ct->sock, mb); break; #endif default: err = EPROTONOSUPPORT; break; } out: if (!err) { if (ctp) { ct->ctp = ctp; *ctp = ct; } ct->resph = resph; ct->arg = arg; } else mem_deref(ct); return err; } static bool close_handler(struct le *le, void *arg) { struct stun_ctrans *ct = le->data; (void)arg; completed(ct, ECONNABORTED, 0, NULL, NULL); return false; } void stun_ctrans_close(struct stun *stun) { if (!stun) return; (void)list_apply(&stun->ctl, true, close_handler, NULL); } static bool debug_handler(struct le *le, void *arg) { struct stun_ctrans *ct = le->data; struct re_printf *pf = arg; int err = 0; err |= re_hprintf(pf, " method=%s", stun_method_name(ct->met)); err |= re_hprintf(pf, " tid=%w", ct->tid, sizeof(ct->tid)); err |= re_hprintf(pf, " rto=%ums", stun_conf(ct->stun)->rto); err |= re_hprintf(pf, " tmr=%llu", tmr_get_expire(&ct->tmr)); err |= re_hprintf(pf, " n=%u", ct->txc); err |= re_hprintf(pf, " interval=%u", ct->ival); err |= re_hprintf(pf, "\n"); return 0 != err; } int stun_ctrans_debug(struct re_printf *pf, const struct stun *stun) { int err; if (!stun) return 0; err = re_hprintf(pf, "STUN client transactions: (%u)\n", list_count(&stun->ctl)); (void)list_apply(&stun->ctl, true, debug_handler, pf); return err; } re-1.1.0/src/stun/dnsdisc.c000066400000000000000000000150641373627245400155150ustar00rootroot00000000000000/** * @file dnsdisc.c DNS Discovery of a STUN Server * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "dnsdisc" #define DEBUG_LEVEL 5 #include /** DNS Query */ struct stun_dns { char domain[256]; /**< Cached domain name */ stun_dns_h *dnsh; /**< DNS Response handler */ void *arg; /**< Handler argument */ struct sa srv; /**< Resolved server address */ struct dnsc *dnsc; /**< DNS Client */ struct dns_query *dq; /**< Current DNS query */ int af; /**< Preferred Address family*/ uint16_t port; /**< Default Port */ }; const char *stun_proto_udp = "udp"; /**< UDP Protocol */ const char *stun_proto_tcp = "tcp"; /**< TCP Protocol */ const char *stun_usage_binding = "stun"; /**< Binding usage */ const char *stuns_usage_binding = "stuns"; /**< Binding usage TLS */ const char *stun_usage_relay = "turn"; const char *stuns_usage_relay = "turns"; const char *stun_usage_behavior = "stun-behavior"; const char *stuns_usage_behavior = "stun-behaviors"; static void resolved(const struct stun_dns *dns, int err) { stun_dns_h *dnsh = dns->dnsh; void *dnsh_arg = dns->arg; DEBUG_INFO("resolved: %J (%m)\n", &dns->srv, err); dnsh(err, &dns->srv, dnsh_arg); } static void a_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct stun_dns *dns = arg; struct dnsrr *rr; (void)hdr; (void)authl; (void)addl; /* Find A answers */ rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_A, DNS_CLASS_IN, false); if (!rr) { err = err ? err : EDESTADDRREQ; goto out; } sa_set_in(&dns->srv, rr->rdata.a.addr, sa_port(&dns->srv)); DEBUG_INFO("A answer: %j\n", &dns->srv); out: resolved(dns, err); } #ifdef HAVE_INET6 static void aaaa_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct stun_dns *dns = arg; struct dnsrr *rr; (void)hdr; (void)authl; (void)addl; /* Find A answers */ rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_AAAA, DNS_CLASS_IN, false); if (!rr) { err = err ? err : EDESTADDRREQ; goto out; } sa_set_in6(&dns->srv, rr->rdata.aaaa.addr, sa_port(&dns->srv)); DEBUG_INFO("AAAA answer: %j\n", &dns->srv); out: resolved(dns, err); } #endif static int a_or_aaaa_query(struct stun_dns *dns, const char *name) { dns->dq = mem_deref(dns->dq); switch (dns->af) { case AF_INET: return dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_A, DNS_CLASS_IN, true, a_handler, dns); #ifdef HAVE_INET6 case AF_INET6: return dnsc_query(&dns->dq, dns->dnsc, name, DNS_TYPE_AAAA, DNS_CLASS_IN, true, aaaa_handler, dns); #endif default: return EAFNOSUPPORT; } } static void srv_handler(int err, const struct dnshdr *hdr, struct list *ansl, struct list *authl, struct list *addl, void *arg) { struct stun_dns *dns = arg; struct dnsrr *rr, *arr; (void)hdr; (void)authl; dns_rrlist_sort(ansl, DNS_TYPE_SRV, (size_t)dns->arg); /* Find SRV answers */ rr = dns_rrlist_find(ansl, NULL, DNS_TYPE_SRV, DNS_CLASS_IN, false); if (!rr) { DEBUG_INFO("no SRV entry, trying A lookup on \"%s\"\n", dns->domain); sa_set_in(&dns->srv, 0, dns->port); err = a_or_aaaa_query(dns, dns->domain); if (err) goto out; return; } DEBUG_INFO("SRV answer: %s:%u\n", rr->rdata.srv.target, rr->rdata.srv.port); /* Look for Additional information */ switch (dns->af) { case AF_INET: arr = dns_rrlist_find(addl, rr->rdata.srv.target, DNS_TYPE_A, DNS_CLASS_IN, true); if (arr) { sa_set_in(&dns->srv, arr->rdata.a.addr, rr->rdata.srv.port); DEBUG_INFO("additional A: %j\n", &dns->srv); goto out; } break; #ifdef HAVE_INET6 case AF_INET6: arr = dns_rrlist_find(addl, rr->rdata.srv.target, DNS_TYPE_AAAA, DNS_CLASS_IN, true); if (arr) { sa_set_in6(&dns->srv, arr->rdata.aaaa.addr, rr->rdata.srv.port); DEBUG_INFO("additional AAAA: %j\n", &dns->srv); goto out; } break; #endif } sa_set_in(&dns->srv, 0, rr->rdata.srv.port); err = a_or_aaaa_query(dns, rr->rdata.srv.target); if (err) { DEBUG_WARNING("SRV: A lookup failed (%m)\n", err); goto out; } DEBUG_INFO("SRV handler: doing A/AAAA lookup..\n"); return; out: resolved(dns, err); } static void dnsdisc_destructor(void *data) { struct stun_dns *dns = data; mem_deref(dns->dq); } /** * Do a DNS Discovery of a STUN Server * * @param dnsp Pointer to allocated DNS Discovery object * @param dnsc DNS Client * @param service Name of service to discover (e.g. "stun") * @param proto Transport protocol (e.g. "udp") * @param af Preferred Address Family * @param domain Domain name or IP address of STUN server * @param port Port number (if 0 do SRV lookup) * @param dnsh DNS Response handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int stun_server_discover(struct stun_dns **dnsp, struct dnsc *dnsc, const char *service, const char *proto, int af, const char *domain, uint16_t port, stun_dns_h *dnsh, void *arg) { struct stun_dns *dns; int err; if (!dnsp || !service || !proto || !domain || !domain[0] || !dnsh) return EINVAL; dns = mem_zalloc(sizeof(*dns), dnsdisc_destructor); if (!dns) return ENOMEM; dns->port = service[strlen(service)-1] == 's' ? STUNS_PORT : STUN_PORT; dns->dnsh = dnsh; dns->arg = arg; dns->dnsc = dnsc; dns->af = af; /* Numeric IP address - no lookup */ if (0 == sa_set_str(&dns->srv, domain, port ? port : dns->port)) { DEBUG_INFO("IP (%s)\n", domain); resolved(dns, 0); err = 0; goto out; /* free now */ } /* Port specified - use AAAA or A lookup */ else if (port) { sa_set_in(&dns->srv, 0, port); DEBUG_INFO("resolving A query: (%s)\n", domain); err = a_or_aaaa_query(dns, domain); if (err) { DEBUG_WARNING("%s: A/AAAA lookup failed (%m)\n", domain, err); goto out; } } /* SRV lookup */ else { char q[256]; str_ncpy(dns->domain, domain, sizeof(dns->domain)); (void)re_snprintf(q, sizeof(q), "_%s._%s.%s", service, proto, domain); DEBUG_INFO("resolving SRV query: (%s)\n", q); err = dnsc_query(&dns->dq, dnsc, q, DNS_TYPE_SRV, DNS_CLASS_IN, true, srv_handler, dns); if (err) { DEBUG_WARNING("%s: SRV lookup failed (%m)\n", q, err); goto out; } } *dnsp = dns; return 0; out: mem_deref(dns); return err; } re-1.1.0/src/stun/hdr.c000066400000000000000000000034451373627245400146430ustar00rootroot00000000000000/** * @file stun/hdr.c STUN Header encoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include "stun.h" int stun_hdr_encode(struct mbuf *mb, const struct stun_hdr *hdr) { int err = 0; if (!mb || !hdr) return EINVAL; err |= mbuf_write_u16(mb, htons(hdr->type & 0x3fff)); err |= mbuf_write_u16(mb, htons(hdr->len)); err |= mbuf_write_u32(mb, htonl(hdr->cookie)); err |= mbuf_write_mem(mb, hdr->tid, sizeof(hdr->tid)); return err; } int stun_hdr_decode(struct mbuf *mb, struct stun_hdr *hdr) { if (!mb || !hdr) return EINVAL; if (mbuf_get_left(mb) < STUN_HEADER_SIZE) return EBADMSG; hdr->type = ntohs(mbuf_read_u16(mb)); if (hdr->type & 0xc000) return EBADMSG; hdr->len = ntohs(mbuf_read_u16(mb)); if (hdr->len & 0x3) return EBADMSG; hdr->cookie = ntohl(mbuf_read_u32(mb)); (void)mbuf_read_mem(mb, hdr->tid, sizeof(hdr->tid)); if (mbuf_get_left(mb) < hdr->len) return EBADMSG; return 0; } const char *stun_class_name(uint16_t class) { switch (class) { case STUN_CLASS_REQUEST: return "Request"; case STUN_CLASS_INDICATION: return "Indication"; case STUN_CLASS_SUCCESS_RESP: return "Success Response"; case STUN_CLASS_ERROR_RESP: return "Error Response"; default: return "???"; } } const char *stun_method_name(uint16_t method) { switch (method) { case STUN_METHOD_BINDING: return "Binding"; case STUN_METHOD_ALLOCATE: return "Allocate"; case STUN_METHOD_REFRESH: return "Refresh"; case STUN_METHOD_SEND: return "Send"; case STUN_METHOD_DATA: return "Data"; case STUN_METHOD_CREATEPERM: return "CreatePermission"; case STUN_METHOD_CHANBIND: return "ChannelBind"; default: return "???"; } } re-1.1.0/src/stun/ind.c000066400000000000000000000031301373627245400146270ustar00rootroot00000000000000/** * @file ind.c STUN Indication * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include "stun.h" /** * Send a STUN Indication message * * @param proto Transport Protocol * @param sock Socket; UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address * @param presz Number of bytes in preamble, if sending over TURN * @param method STUN Method * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_indication(int proto, void *sock, const struct sa *dst, size_t presz, uint16_t method, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...) { uint8_t tid[STUN_TID_SIZE]; struct mbuf *mb; va_list ap; uint32_t i; int err; if (!sock) return EINVAL; mb = mbuf_alloc(2048); if (!mb) return ENOMEM; for (i=0; ipos = presz; err = stun_msg_vencode(mb, method, STUN_CLASS_INDICATION, tid, NULL, key, keylen, fp, 0x00, attrc, ap); va_end(ap); if (err) goto out; mb->pos = presz; err = stun_send(proto, sock, dst, mb); out: mem_deref(mb); return err; } re-1.1.0/src/stun/keepalive.c000066400000000000000000000116341373627245400160320ustar00rootroot00000000000000/** * @file stun/keepalive.c STUN usage for NAT Keepalives * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #define DEBUG_MODULE "keepalive" #define DEBUG_LEVEL 5 #include /** Defines a STUN Keepalive session */ struct stun_keepalive { struct stun_ctrans *ct; /**< STUN client transaction */ struct stun *stun; /**< STUN instance */ struct udp_helper *uh; int proto; void *sock; struct sa dst; struct tmr tmr; /**< Refresh timer */ uint32_t interval; /**< Refresh interval in seconds */ stun_mapped_addr_h *mah; /**< Mapped address handler */ void *arg; /**< Handler argument */ struct sa map; /**< Mapped IP address and port */ struct sa xormap; /**< XOR-Mapped IP address and port */ struct sa curmap; /**< Currently mapped IP address and port */ }; static void timeout(void *arg); static void keepalive_destructor(void *data) { struct stun_keepalive *ska = data; tmr_cancel(&ska->tmr); mem_deref(ska->ct); mem_deref(ska->uh); mem_deref(ska->sock); mem_deref(ska->stun); } static void call_handler(struct stun_keepalive *ska, int err, const struct sa *map) { if (ska->mah) ska->mah(err, map, ska->arg); } static void stun_response_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct stun_keepalive *ska = arg; struct stun_attr *attr; (void)reason; /* Restart timer */ if (ska->interval > 0) tmr_start(&ska->tmr, ska->interval*1000, timeout, ska); if (err || scode) { /* Clear current mapped addr to force new notification */ sa_set_in(&ska->curmap, 0, 0); goto out; } attr = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); if (!attr) attr = stun_msg_attr(msg, STUN_ATTR_MAPPED_ADDR); if (!attr) { err = ENOENT; goto out; } if (!sa_cmp(&ska->curmap, &attr->v.sa, SA_ALL)) { ska->curmap = attr->v.sa; call_handler(ska, 0, &ska->curmap); } out: if (err) call_handler(ska, err, NULL); } static void timeout(void *arg) { struct stun_keepalive *ska = arg; int err; if (ska->ct) ska->ct = mem_deref(ska->ct); err = stun_request(&ska->ct, ska->stun, ska->proto, ska->sock, &ska->dst, 0, STUN_METHOD_BINDING, NULL, 0, false, stun_response_handler, ska, 1, STUN_ATTR_SOFTWARE, stun_software); if (0 == err) return; /* Restart timer */ if (ska->interval > 0) tmr_start(&ska->tmr, ska->interval*1000, timeout, ska); /* Error */ call_handler(ska, err, NULL); } static bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct stun_keepalive *ska = arg; struct stun_unknown_attr ua; struct stun_msg *msg; size_t pos = mb->pos; bool hdld; if (!sa_cmp(&ska->dst, src, SA_ALL)) return false; if (stun_msg_decode(&msg, mb, &ua)) return false; if (stun_msg_method(msg) != STUN_METHOD_BINDING) { hdld = false; mb->pos = pos; goto out; } switch (stun_msg_class(msg)) { case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(ska->stun, msg, &ua); hdld = true; break; default: hdld = false; mb->pos = pos; break; } out: mem_deref(msg); return hdld; } /** * Allocate a new STUN keepalive session * * @param skap Pointer to keepalive object * @param proto Transport protocol * @param sock Socket * @param layer Protocol layer * @param dst Destination address * @param conf Configuration * @param mah Mapped address handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int stun_keepalive_alloc(struct stun_keepalive **skap, int proto, void *sock, int layer, const struct sa *dst, const struct stun_conf *conf, stun_mapped_addr_h *mah, void *arg) { struct stun_keepalive *ska; int err; if (!skap) return EINVAL; ska = mem_zalloc(sizeof(*ska), keepalive_destructor); if (!ska) return ENOMEM; err = stun_alloc(&ska->stun, conf, NULL, NULL); if (err) goto out; tmr_init(&ska->tmr); ska->proto = proto; ska->sock = mem_ref(sock); ska->mah = mah; ska->arg = arg; if (dst) ska->dst = *dst; switch (proto) { case IPPROTO_UDP: err = udp_register_helper(&ska->uh, sock, layer, NULL, udp_recv_handler, ska); break; default: err = 0; break; } out: if (err) mem_deref(ska); else *skap = ska; return err; } /** * Enable or disable keepalive timer * * @param ska Keepalive object * @param interval Interval in seconds (0 to disable) * * @return 0 if success, otherwise errorcode */ void stun_keepalive_enable(struct stun_keepalive *ska, uint32_t interval) { if (!ska) return; ska->interval = interval; tmr_cancel(&ska->tmr); if (interval > 0) tmr_start(&ska->tmr, 1, timeout, ska); } re-1.1.0/src/stun/mod.mk000066400000000000000000000004511373627245400150240ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += stun/addr.c SRCS += stun/attr.c SRCS += stun/ctrans.c SRCS += stun/dnsdisc.c SRCS += stun/hdr.c SRCS += stun/ind.c SRCS += stun/keepalive.c SRCS += stun/msg.c SRCS += stun/rep.c SRCS += stun/req.c SRCS += stun/stun.c SRCS += stun/stunstr.c re-1.1.0/src/stun/msg.c000066400000000000000000000234111373627245400146470ustar00rootroot00000000000000/** * @file stun/msg.c STUN message encoding * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "stun.h" enum { MI_SIZE = 24, FP_SIZE = 8 }; /** Defines a STUN Message object

   .---------------------.      /|\           /|\
   | STUN Header         |       |             |
   |---------------------|       |             |
   |         ....        |       |--------.    |
   |      N Attributes   |       |        |    |----.
   |         ....        |      \|/       |    |    |
   |---------------------|                |    |    |
   |  MESSAGE-INTEGRITY  | <-(HMAC-SHA1)--'   \|/   |
   |---------------------|                          |
   |     FINGERPRINT     | <-(CRC-32)---------------'
   '---------------------'
   
*/ struct stun_msg { struct stun_hdr hdr; struct list attrl; struct mbuf *mb; size_t start; }; static uint32_t fingerprint(const uint8_t *buf, size_t len) { return (uint32_t)crc32(0, buf, (unsigned int)len) ^ 0x5354554e; } static void destructor(void *arg) { struct stun_msg *msg = arg; list_flush(&msg->attrl); mem_deref(msg->mb); } /** * Decode a buffer to a STUN Message * * @param msgpp Pointer to allocation STUN message * @param mb Buffer containing the raw STUN packet * @param ua Unknown attributes (optional) * * @return 0 if success, otherwise errorcode * * @note `mb' will be referenced */ int stun_msg_decode(struct stun_msg **msgpp, struct mbuf *mb, struct stun_unknown_attr *ua) { struct stun_msg *msg; struct stun_hdr hdr; size_t start, extra; int err; if (!msgpp || !mb) return EINVAL; start = mb->pos; err = stun_hdr_decode(mb, &hdr); if (err) { mb->pos = start; return err; } msg = mem_zalloc(sizeof(*msg), destructor); if (!msg) { mb->pos = start; return ENOMEM; } msg->hdr = hdr; msg->mb = mem_ref(mb); msg->start = start; if (ua) ua->typec = 0; /* mbuf_get_left(mb) >= hdr.len checked in stun_hdr_decode() above */ extra = mbuf_get_left(mb) - hdr.len; while (mbuf_get_left(mb) - extra >= 4) { struct stun_attr *attr; err = stun_attr_decode(&attr, mb, hdr.tid, ua); if (err) break; list_append(&msg->attrl, &attr->le, attr); } if (err) mem_deref(msg); else *msgpp = msg; mb->pos = start; return err; } /** * Get the STUN message type * * @param msg STUN Message * * @return STUN Message type */ uint16_t stun_msg_type(const struct stun_msg *msg) { return msg ? msg->hdr.type : 0; } /** * Get the STUN message class * * @param msg STUN Message * * @return STUN Message class */ uint16_t stun_msg_class(const struct stun_msg *msg) { return STUN_CLASS(stun_msg_type(msg)); } /** * Get the STUN message method * * @param msg STUN Message * * @return STUN Message method */ uint16_t stun_msg_method(const struct stun_msg *msg) { return STUN_METHOD(stun_msg_type(msg)); } /** * Get the STUN message Transaction-ID * * @param msg STUN Message * * @return STUN Message Transaction-ID */ const uint8_t *stun_msg_tid(const struct stun_msg *msg) { return msg ? msg->hdr.tid : NULL; } /** * Check if a STUN Message has the magic cookie * * @param msg STUN Message * * @return true if Magic Cookie, otherwise false */ bool stun_msg_mcookie(const struct stun_msg *msg) { return msg && (STUN_MAGIC_COOKIE == msg->hdr.cookie); } /** * Lookup a STUN attribute in a STUN message * * @param msg STUN Message * @param type STUN Attribute type * * @return STUN Attribute if found, otherwise NULL */ struct stun_attr *stun_msg_attr(const struct stun_msg *msg, uint16_t type) { struct le *le = msg ? list_head(&msg->attrl) : NULL; while (le) { struct stun_attr *attr = le->data; le = le->next; if (attr->type == type) return attr; } return NULL; } /** * Apply a function handler to all STUN attribute * * @param msg STUN Message * @param h Attribute handler * @param arg Handler argument * * @return STUN attribute if handler returned true, otherwise NULL */ struct stun_attr *stun_msg_attr_apply(const struct stun_msg *msg, stun_attr_h *h, void *arg) { struct le *le = msg ? list_head(&msg->attrl) : NULL; while (le) { struct stun_attr *attr = le->data; le = le->next; if (h && h(attr, arg)) return (attr); } return NULL; } /** * Encode a STUN message * * @param mb Buffer to encode message into * @param method STUN Method * @param class STUN Method class * @param tid Transaction ID * @param ec STUN error code (optional) * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param padding Padding byte * @param attrc Number of attributes to encode (variable arguments) * @param ap Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_msg_vencode(struct mbuf *mb, uint16_t method, uint8_t class, const uint8_t *tid, const struct stun_errcode *ec, const uint8_t *key, size_t keylen, bool fp, uint8_t padding, uint32_t attrc, va_list ap) { struct stun_hdr hdr; size_t start; int err = 0; uint32_t i; if (!mb || !tid) return EINVAL; start = mb->pos; mb->pos += STUN_HEADER_SIZE; hdr.type = STUN_TYPE(method, class); hdr.cookie = STUN_MAGIC_COOKIE; memcpy(hdr.tid, tid, STUN_TID_SIZE); if (ec) err |= stun_attr_encode(mb, STUN_ATTR_ERR_CODE, ec, NULL, padding); for (i=0; ipos - start - STUN_HEADER_SIZE + (key ? MI_SIZE : 0); mb->pos = start; err |= stun_hdr_encode(mb, &hdr); mb->pos += hdr.len - (key ? MI_SIZE : 0); if (key) { uint8_t mi[20]; mb->pos = start; hmac_sha1(key, keylen, mbuf_buf(mb), mbuf_get_left(mb), mi, sizeof(mi)); mb->pos += STUN_HEADER_SIZE + hdr.len - MI_SIZE; err |= stun_attr_encode(mb, STUN_ATTR_MSG_INTEGRITY, mi, NULL, padding); } if (fp) { uint32_t fprnt; /* header */ hdr.len = mb->pos - start - STUN_HEADER_SIZE + FP_SIZE; mb->pos = start; err |= stun_hdr_encode(mb, &hdr); mb->pos = start; fprnt = fingerprint(mbuf_buf(mb), mbuf_get_left(mb)); mb->pos += STUN_HEADER_SIZE + hdr.len - FP_SIZE; err |= stun_attr_encode(mb, STUN_ATTR_FINGERPRINT, &fprnt, NULL, padding); } return err; } /** * Encode a STUN message * * @param mb Buffer to encode message into * @param method STUN Method * @param class STUN Method class * @param tid Transaction ID * @param ec STUN error code (optional) * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param padding Padding byte * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_msg_encode(struct mbuf *mb, uint16_t method, uint8_t class, const uint8_t *tid, const struct stun_errcode *ec, const uint8_t *key, size_t keylen, bool fp, uint8_t padding, uint32_t attrc, ...) { va_list ap; int err; va_start(ap, attrc); err = stun_msg_vencode(mb, method, class, tid, ec, key, keylen, fp, padding, attrc, ap); va_end(ap); return err; } /** * Verify the Message-Integrity of a STUN message * * @param msg STUN Message * @param key Authentication key * @param keylen Number of bytes in authentication key * * @return 0 if verified, otherwise errorcode */ int stun_msg_chk_mi(const struct stun_msg *msg, const uint8_t *key, size_t keylen) { uint8_t hmac[SHA_DIGEST_LENGTH]; struct stun_attr *mi, *fp; if (!msg) return EINVAL; mi = stun_msg_attr(msg, STUN_ATTR_MSG_INTEGRITY); if (!mi) return EPROTO; msg->mb->pos = msg->start; fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT); if (fp) { ((struct stun_msg *)msg)->hdr.len -= FP_SIZE; (void)stun_hdr_encode(msg->mb, &msg->hdr); msg->mb->pos -= STUN_HEADER_SIZE; } hmac_sha1(key, keylen, mbuf_buf(msg->mb), STUN_HEADER_SIZE + msg->hdr.len - MI_SIZE, hmac, sizeof(hmac)); if (fp) { ((struct stun_msg *)msg)->hdr.len += FP_SIZE; (void)stun_hdr_encode(msg->mb, &msg->hdr); msg->mb->pos -= STUN_HEADER_SIZE; } if (memcmp(mi->v.msg_integrity, hmac, SHA_DIGEST_LENGTH)) return EBADMSG; return 0; } /** * Check the Fingerprint of a STUN message * * @param msg STUN Message * * @return 0 if fingerprint matches, otherwise errorcode */ int stun_msg_chk_fingerprint(const struct stun_msg *msg) { struct stun_attr *fp; uint32_t fprnt; if (!msg) return EINVAL; fp = stun_msg_attr(msg, STUN_ATTR_FINGERPRINT); if (!fp) return EPROTO; msg->mb->pos = msg->start; fprnt = fingerprint(mbuf_buf(msg->mb), STUN_HEADER_SIZE + msg->hdr.len - FP_SIZE); if (fprnt != fp->v.fingerprint) return EBADMSG; return 0; } static bool attr_print(const struct stun_attr *attr, void *arg) { (void)arg; stun_attr_dump(attr); return false; } /** * Print a STUN message to STDOUT * * @param msg STUN Message */ void stun_msg_dump(const struct stun_msg *msg) { if (!msg) return; (void)re_printf("%s %s (len=%u cookie=%08x tid=%w)\n", stun_method_name(stun_msg_method(msg)), stun_class_name(stun_msg_class(msg)), msg->hdr.len, msg->hdr.cookie, msg->hdr.tid, sizeof(msg->hdr.tid)); stun_msg_attr_apply(msg, attr_print, NULL); } re-1.1.0/src/stun/rep.c000066400000000000000000000060611373627245400146510ustar00rootroot00000000000000/** * @file stun/rep.c STUN reply * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include "stun.h" /** * Send a STUN response message * * @param proto Transport Protocol * @param sock Socket; UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address * @param presz Number of bytes in preamble, if sending over TURN * @param req Matching STUN request * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_reply(int proto, void *sock, const struct sa *dst, size_t presz, const struct stun_msg *req, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...) { struct mbuf *mb = NULL; int err = ENOMEM; va_list ap; if (!sock || !req) return EINVAL; mb = mbuf_alloc(256); if (!mb) goto out; va_start(ap, attrc); mb->pos = presz; err = stun_msg_vencode(mb, stun_msg_method(req), STUN_CLASS_SUCCESS_RESP, stun_msg_tid(req), NULL, key, keylen, fp, 0x00, attrc, ap); va_end(ap); if (err) goto out; mb->pos = presz; err = stun_send(proto, sock, dst, mb); out: mem_deref(mb); return err; } /** * Send a STUN error response * * @param proto Transport Protocol * @param sock Socket; UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address * @param presz Number of bytes in preamble, if sending over TURN * @param req Matching STUN request * @param scode Status code * @param reason Reason string * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_ereply(int proto, void *sock, const struct sa *dst, size_t presz, const struct stun_msg *req, uint16_t scode, const char *reason, const uint8_t *key, size_t keylen, bool fp, uint32_t attrc, ...) { struct stun_errcode ec; struct mbuf *mb = NULL; int err = ENOMEM; va_list ap; if (!sock || !req || !scode || !reason) return EINVAL; mb = mbuf_alloc(256); if (!mb) goto out; ec.code = scode; ec.reason = (char *)reason; va_start(ap, attrc); mb->pos = presz; err = stun_msg_vencode(mb, stun_msg_method(req), STUN_CLASS_ERROR_RESP, stun_msg_tid(req), &ec, key, keylen, fp, 0x00, attrc, ap); va_end(ap); if (err) goto out; mb->pos = presz; err = stun_send(proto, sock, dst, mb); out: mem_deref(mb); return err; } re-1.1.0/src/stun/req.c000066400000000000000000000036671373627245400146630ustar00rootroot00000000000000/** * @file stun/req.c STUN request * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include "stun.h" /** * Send a STUN request using a client transaction * * @param ctp Pointer to allocated client transaction (optional) * @param stun STUN Instance * @param proto Transport Protocol * @param sock Socket; UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address * @param presz Number of bytes in preamble, if sending over TURN * @param method STUN Method * @param key Authentication key (optional) * @param keylen Number of bytes in authentication key * @param fp Use STUN Fingerprint attribute * @param resph Response handler * @param arg Response handler argument * @param attrc Number of attributes to encode (variable arguments) * @param ... Variable list of attribute-tuples * Each attribute has 2 arguments, attribute type and value * * @return 0 if success, otherwise errorcode */ int stun_request(struct stun_ctrans **ctp, struct stun *stun, int proto, void *sock, const struct sa *dst, size_t presz, uint16_t method, const uint8_t *key, size_t keylen, bool fp, stun_resp_h *resph, void *arg, uint32_t attrc, ...) { uint8_t tid[STUN_TID_SIZE]; struct mbuf *mb; uint32_t i; va_list ap; int err; if (!stun) return EINVAL; mb = mbuf_alloc(512); if (!mb) return ENOMEM; for (i=0; ipos = presz; err = stun_msg_vencode(mb, method, STUN_CLASS_REQUEST, tid, NULL, key, keylen, fp, 0x00, attrc, ap); va_end(ap); if (err) goto out; mb->pos = presz; err = stun_ctrans_request(ctp, stun, proto, sock, dst, mb, tid, method, key, keylen, resph, arg); if (err) goto out; out: mem_deref(mb); return err; } re-1.1.0/src/stun/stun.c000066400000000000000000000062221373627245400150530ustar00rootroot00000000000000/** * @file stun.c STUN stack * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "stun.h" const char *stun_software = "libre v" VERSION " (" ARCH "/" OS ")"; static const struct stun_conf conf_default = { STUN_DEFAULT_RTO, STUN_DEFAULT_RC, STUN_DEFAULT_RM, STUN_DEFAULT_TI, 0x00 }; static void destructor(void *arg) { struct stun *stun = arg; stun_ctrans_close(stun); } /** * Allocate a new STUN instance * * @param stunp Pointer to allocated STUN instance * @param conf STUN configuration (optional) * @param indh STUN Indication handler (optional) * @param arg STUN Indication handler argument * * @return 0 if success, otherwise errorcode */ int stun_alloc(struct stun **stunp, const struct stun_conf *conf, stun_ind_h *indh, void *arg) { struct stun *stun; if (!stunp) return EINVAL; stun = mem_zalloc(sizeof(*stun), destructor); if (!stun) return ENOMEM; stun->conf = conf ? *conf : conf_default; stun->indh = indh; stun->arg = arg; *stunp = stun; return 0; } /** * Get STUN configuration object * * @param stun STUN Instance * * @return STUN configuration */ struct stun_conf *stun_conf(struct stun *stun) { return stun ? &stun->conf : NULL; } /** * Send a STUN message * * @param proto Transport protocol (IPPROTO_UDP or IPPROTO_TCP) * @param sock Socket, UDP (struct udp_sock) or TCP (struct tcp_conn) * @param dst Destination network address (UDP only) * @param mb Buffer containing the STUN message * * @return 0 if success, otherwise errorcode */ int stun_send(int proto, void *sock, const struct sa *dst, struct mbuf *mb) { int err; if (!sock || !mb) return EINVAL; switch (proto) { case IPPROTO_UDP: err = udp_send(sock, dst, mb); break; case IPPROTO_TCP: err = tcp_send(sock, mb); break; #ifdef USE_DTLS case STUN_TRANSP_DTLS: err = dtls_send(sock, mb); break; #endif default: err = EPROTONOSUPPORT; break; } return err; } /** * Receive a STUN message * * @param stun STUN Instance * @param mb Buffer containing STUN message * * @return 0 if success, otherwise errorcode */ int stun_recv(struct stun *stun, struct mbuf *mb) { struct stun_unknown_attr ua; struct stun_msg *msg; int err; if (!stun || !mb) return EINVAL; err = stun_msg_decode(&msg, mb, &ua); if (err) return err; switch (stun_msg_class(msg)) { case STUN_CLASS_INDICATION: if (ua.typec > 0) break; if (stun->indh) stun->indh(msg, stun->arg); break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: err = stun_ctrans_recv(stun, msg, &ua); break; default: break; } mem_deref(msg); return err; } /** * Print STUN instance debug information * * @param pf Print function * @param stun STUN Instance * * @return 0 if success, otherwise errorcode */ int stun_debug(struct re_printf *pf, const struct stun *stun) { if (!stun) return 0; return re_hprintf(pf, "STUN debug:\n%H", stun_ctrans_debug, stun); } re-1.1.0/src/stun/stun.h000066400000000000000000000035031373627245400150570ustar00rootroot00000000000000/** * @file stun.h Internal STUN interface * * Copyright (C) 2010 Creytiv.com */ /** STUN Protocol values */ enum { STUN_MAGIC_COOKIE = 0x2112a442 /**< Magic Cookie for 3489bis */ }; /** Calculate STUN message type from method and class */ #define STUN_TYPE(method, class) \ ((method)&0x0f80) << 2 | \ ((method)&0x0070) << 1 | \ ((method)&0x000f) << 0 | \ ((class)&0x2) << 7 | \ ((class)&0x1) << 4 #define STUN_CLASS(type) \ ((type >> 7 | type >> 4) & 0x3) #define STUN_METHOD(type) \ ((type&0x3e00)>>2 | (type&0x00e0)>>1 | (type&0x000f)) struct stun_hdr { uint16_t type; /**< Message type */ uint16_t len; /**< Payload length */ uint32_t cookie; /**< Magic cookie */ uint8_t tid[STUN_TID_SIZE]; /**< Transaction ID */ }; struct stun { struct list ctl; struct stun_conf conf; stun_ind_h *indh; void *arg; }; int stun_hdr_encode(struct mbuf *mb, const struct stun_hdr *hdr); int stun_hdr_decode(struct mbuf *mb, struct stun_hdr *hdr); int stun_attr_encode(struct mbuf *mb, uint16_t type, const void *v, const uint8_t *tid, uint8_t padding); int stun_attr_decode(struct stun_attr **attrp, struct mbuf *mb, const uint8_t *tid, struct stun_unknown_attr *ua); void stun_attr_dump(const struct stun_attr *a); int stun_addr_encode(struct mbuf *mb, const struct sa *addr, const uint8_t *tid); int stun_addr_decode(struct mbuf *mb, struct sa *addr, const uint8_t *tid); int stun_ctrans_request(struct stun_ctrans **ctp, struct stun *stun, int proto, void *sock, const struct sa *dst, struct mbuf *mb, const uint8_t tid[], uint16_t met, const uint8_t *key, size_t keylen, stun_resp_h *resph, void *arg); void stun_ctrans_close(struct stun *stun); int stun_ctrans_debug(struct re_printf *pf, const struct stun *stun); re-1.1.0/src/stun/stunstr.c000066400000000000000000000024251373627245400156050ustar00rootroot00000000000000/** * @file stunstr.c STUN Strings * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include /* STUN Reason Phrase */ const char *stun_reason_300 = "Try Alternate"; const char *stun_reason_400 = "Bad Request"; const char *stun_reason_401 = "Unauthorized"; const char *stun_reason_403 = "Forbidden"; const char *stun_reason_420 = "Unknown Attribute"; const char *stun_reason_437 = "Allocation Mismatch"; const char *stun_reason_438 = "Stale Nonce"; const char *stun_reason_440 = "Address Family not Supported"; const char *stun_reason_441 = "Wrong Credentials"; const char *stun_reason_442 = "Unsupported Transport Protocol"; const char *stun_reason_443 = "Peer Address Family Mismatch"; const char *stun_reason_486 = "Allocation Quota Reached"; const char *stun_reason_500 = "Server Error"; const char *stun_reason_508 = "Insufficient Capacity"; /** * Get the name of a given STUN Transport * * @param tp STUN Transport * * @return Name of the corresponding STUN Transport */ const char *stun_transp_name(enum stun_transp tp) { switch (tp) { case STUN_TRANSP_UDP: return "UDP"; case STUN_TRANSP_TCP: return "TCP"; case STUN_TRANSP_DTLS: return "DTLS"; default: return "???"; } } re-1.1.0/src/sys/000077500000000000000000000000001373627245400135415ustar00rootroot00000000000000re-1.1.0/src/sys/daemon.c000066400000000000000000000017371373627245400151600ustar00rootroot00000000000000/** * @file daemon.c Daemonize process * * Copyright (C) 2010 Creytiv.com */ #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include /** * Daemonize process * * @return 0 if success, otherwise errorcode */ int sys_daemon(void) { #ifdef HAVE_FORK pid_t pid; pid = fork(); if (-1 == pid) return errno; else if (pid > 0) exit(0); if (-1 == setsid()) return errno; (void)signal(SIGHUP, SIG_IGN); pid = fork(); if (-1 == pid) return errno; else if (pid > 0) exit(0); if (-1 == chdir("/")) return errno; (void)umask(0); /* Redirect standard files to /dev/null */ if (freopen("/dev/null", "r", stdin) == NULL) return errno; if (freopen("/dev/null", "w", stdout) == NULL) return errno; if (freopen("/dev/null", "w", stderr) == NULL) return errno; return 0; #else return ENOSYS; #endif } re-1.1.0/src/sys/endian.c000066400000000000000000000047071373627245400151530ustar00rootroot00000000000000/** * @file endian.c Endianness converting routines * * Copyright (C) 2010 Creytiv.com */ #include #include #include /* * These routes are working on both little-endian and big-endian platforms. */ /** * Convert a 16-bit value from host order to little endian * * @param v 16-bit in host order * * @return 16-bit little endian value */ uint16_t sys_htols(uint16_t v) { uint8_t *p = (uint8_t *)&v; uint16_t l = 0; l |= (uint16_t)*p++ << 0; l |= (uint16_t)*p << 8; return l; } /** * Convert a 32-bit value from host order to little endian * * @param v 32-bit in host order * * @return 32-bit little endian value */ uint32_t sys_htoll(uint32_t v) { uint8_t *p = (uint8_t *)&v; uint32_t l = 0; l |= (uint32_t)*p++ << 0; l |= (uint32_t)*p++ << 8; l |= (uint32_t)*p++ << 16; l |= (uint32_t)*p << 24; return l; } /** * Convert a 16-bit value from little endian to host order * * @param v 16-bit little endian value * * @return 16-bit value in host order */ uint16_t sys_ltohs(uint16_t v) { uint16_t s; uint8_t *p = (uint8_t *)&s; *p++ = v>>0 & 0xff; *p = v>>8 & 0xff; return s; } /** * Convert a 32-bit value from little endian to host order * * @param v 32-bit little endian value * * @return 32-bit value in host order */ uint32_t sys_ltohl(uint32_t v) { uint32_t h; uint8_t *p = (uint8_t *)&h; *p++ = v>>0 & 0xff; *p++ = v>>8 & 0xff; *p++ = v>>16 & 0xff; *p = v>>24 & 0xff; return h; } /** * Convert a 64-bit value from host to network byte-order * * @param v 64-bit host byte-order value * * @return 64-bit value in network byte-order */ uint64_t sys_htonll(uint64_t v) { uint64_t h = 0; uint8_t *p = (uint8_t *)&v; h |= (uint64_t)*p++ << 56; h |= (uint64_t)*p++ << 48; h |= (uint64_t)*p++ << 40; h |= (uint64_t)*p++ << 32; h |= (uint64_t)*p++ << 24; h |= (uint64_t)*p++ << 16; h |= (uint64_t)*p++ << 8; h |= (uint64_t)*p << 0; return h; } /** * Convert a 64-bit value from network to host byte-order * * @param v 64-bit network byte-order value * * @return 64-bit value in host byte-order */ uint64_t sys_ntohll(uint64_t v) { uint64_t h; uint8_t *p = (uint8_t *)&h; *p++ = (uint8_t) (v>>56 & 0xff); *p++ = (uint8_t) (v>>48 & 0xff); *p++ = (uint8_t) (v>>40 & 0xff); *p++ = (uint8_t) (v>>32 & 0xff); *p++ = (uint8_t) (v>>24 & 0xff); *p++ = (uint8_t) (v>>16 & 0xff); *p++ = (uint8_t) (v>>8 & 0xff); *p = (uint8_t) (v>>0 & 0xff); return h; } re-1.1.0/src/sys/fs.c000066400000000000000000000031701373627245400143160ustar00rootroot00000000000000/** * @file fs.c File-system functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_PWD_H #include #endif #ifdef WIN32 #include #include #include #include #endif #include #include #include /** * Create a directory with full path * * @param path Directory path * @param mode Access permissions * * @return 0 if success, otherwise errorcode */ int fs_mkdir(const char *path, uint16_t mode) { int ret; if (!path) return EINVAL; #if defined (WIN32) (void)mode; ret = _mkdir(path); #else ret = mkdir(path, mode); #endif if (ret < 0) return errno; return 0; } /** * Get the home directory for the current user * * @param path String to write home directory * @param sz Size of path string * * @return 0 if success, otherwise errorcode */ int fs_gethome(char *path, size_t sz) { #ifdef WIN32 char win32_path[MAX_PATH]; if (!path || !sz) return EINVAL; if (S_OK != SHGetFolderPath(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, win32_path)) { return ENOENT; } str_ncpy(path, win32_path, sz); return 0; #elif defined(HAVE_PWD_H) const char *loginname; struct passwd *pw; if (!path || !sz) return EINVAL; loginname = sys_username(); if (!loginname) return ENOENT; pw = getpwnam(loginname); if (!pw) return errno; str_ncpy(path, pw->pw_dir, sz); return 0; #else (void)path; (void)sz; return ENOSYS; #endif } re-1.1.0/src/sys/mod.mk000066400000000000000000000002451373627245400146520ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += sys/daemon.c SRCS += sys/endian.c SRCS += sys/fs.c SRCS += sys/rand.c SRCS += sys/sleep.c SRCS += sys/sys.c re-1.1.0/src/sys/rand.c000066400000000000000000000053701373627245400146360ustar00rootroot00000000000000/** * @file rand.c Random generator * * Copyright (C) 2010 Creytiv.com */ #include #ifdef USE_OPENSSL #include #include #endif #include #include #include #include #include #define DEBUG_MODULE "rand" #define DEBUG_LEVEL 5 #include #ifndef RELEASE #define RAND_DEBUG 1 /**< Enable random debugging */ #endif static const char alphanum[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789"; #if RAND_DEBUG static bool inited = false; /** Check random state */ #define RAND_CHECK \ if (!inited) { \ DEBUG_WARNING("%s: random not inited\n", __REFUNC__); \ } #else #define RAND_CHECK if (0) {} #endif /** * Initialise random number generator */ void rand_init(void) { #ifndef USE_OPENSSL srand((uint32_t) tmr_jiffies()); #endif #if RAND_DEBUG inited = true; #endif } /** * Generate an unsigned 16-bit random value * * @return 16-bit random value */ uint16_t rand_u16(void) { RAND_CHECK; /* Use higher-order bits (see man 3 rand) */ return rand_u32() >> 16; } /** * Generate an unsigned 32-bit random value * * @return 32-bit random value */ uint32_t rand_u32(void) { uint32_t v; RAND_CHECK; #ifdef USE_OPENSSL v = 0; if (RAND_bytes((unsigned char *)&v, sizeof(v)) <= 0) { DEBUG_WARNING("RAND_bytes() error: %i\n", ERR_GET_REASON(ERR_get_error())); ERR_clear_error(); } #elif defined(HAVE_ARC4RANDOM) v = arc4random(); #elif defined(WIN32) v = (rand() << 16) + rand(); /* note: 16-bit rand */ #else v = rand(); #endif return v; } /** * Generate an unsigned 64-bit random value * * @return 64-bit random value */ uint64_t rand_u64(void) { RAND_CHECK; return (uint64_t)rand_u32()<<32 | rand_u32(); } /** * Generate a random printable character * * @return Random printable character */ char rand_char(void) { char s[2]; RAND_CHECK; rand_str(s, sizeof(s)); return s[0]; } /** * Generate a string of random characters * * @param str Pointer to string * @param size Size of string */ void rand_str(char *str, size_t size) { size_t i; if (!str || !size) return; RAND_CHECK; --size; rand_bytes((uint8_t *)str, size); for (i=0; i #include #include #ifdef WIN32 #include #endif #ifdef HAVE_UNISTD_H #define _BSD_SOURCE 1 #include #endif #ifdef HAVE_SELECT_H #include #endif /** * Blocking sleep for [us] number of microseconds * * @param us Number of microseconds to sleep */ void sys_usleep(unsigned int us) { if (!us) return; #ifdef WIN32 Sleep(us / 1000); #elif defined(HAVE_SELECT) do { struct timeval tv; tv.tv_sec = us / 1000000; tv.tv_usec = us % 1000000; (void)select(0, NULL, NULL, NULL, &tv); } while (0); #else (void)usleep(us); #endif } re-1.1.0/src/sys/sys.c000066400000000000000000000073441373627245400145330ustar00rootroot00000000000000/** * @file sys.c System information * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_UNAME #include #endif #ifdef HAVE_SYS_TIME_H #include #endif #ifdef HAVE_SETRLIMIT #include #endif /** * Get system release version * * @param rel Binary encoded release * @param maj Major version number * @param min Minor version number * @param patch Patch number * * @return 0 if success, otherwise errorcode */ int sys_rel_get(uint32_t *rel, uint32_t *maj, uint32_t *min, uint32_t *patch) { #ifdef HAVE_UNAME struct utsname u; struct pl pl_mj, pl_mn, pl_p; uint32_t mj, mn, p; int err; if (0 != uname(&u)) return errno; err = re_regex(u.release, strlen(u.release), "[0-9]+.[0-9]+[.\\-]1[0-9]+", &pl_mj, &pl_mn, NULL, &pl_p); if (err) return err; mj = pl_u32(&pl_mj); mn = pl_u32(&pl_mn); p = pl_u32(&pl_p); if (rel) *rel = mj<<16 | mn<<8 | p; if (maj) *maj = mj; if (min) *min = mn; if (patch) *patch = p; return 0; #else (void)rel; (void)maj; (void)min; (void)patch; return EINVAL; #endif } /** * Get kernel name and version * * @param pf Print function for output * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int sys_kernel_get(struct re_printf *pf, void *unused) { #ifdef HAVE_UNAME struct utsname u; (void)unused; if (0 != uname(&u)) return errno; return re_hprintf(pf, "%s %s %s %s %s", u.sysname, u.nodename, u.release, u.version, u.machine); #else const char *str; (void)unused; #if defined(WIN32) str = "Win32"; #else str = "?"; #endif return re_hprintf(pf, "%s", str); #endif } /** * Get build info * * @param pf Print function for output * @param unused Unused parameter * * @return 0 if success, otherwise errorcode */ int sys_build_get(struct re_printf *pf, void *unused) { const unsigned int bus_width = 8*sizeof(void *); const char *endian = "unknown"; const uint32_t a = 0x12345678; const uint8_t b0 = ((uint8_t *)&a)[0]; const uint8_t b1 = ((uint8_t *)&a)[1]; const uint8_t b2 = ((uint8_t *)&a)[2]; const uint8_t b3 = ((uint8_t *)&a)[3]; (void)unused; if (0x12==b0 && 0x34==b1 && 0x56==b2 && 0x78==b3) endian = "big"; else if (0x12==b3 && 0x34==b2 && 0x56==b1 && 0x78==b0) endian = "little"; return re_hprintf(pf, "%u-bit %s endian", bus_width, endian); } /** * Get architecture * * @return Architecture string */ const char *sys_arch_get(void) { #ifdef ARCH return ARCH; #else return "?"; #endif } /** * Get name of Operating System * * @return Operating System string */ const char *sys_os_get(void) { #ifdef OS return OS; #else return "?"; #endif } /** * Get libre version * * @return libre version string */ const char *sys_libre_version_get(void) { #ifdef VERSION return VERSION; #else return "?"; #endif } /** * Return the username (login name) for the current user * * @return Username or NULL if not available */ const char *sys_username(void) { #ifdef HAVE_PWD_H char *login; login = getenv("LOGNAME"); if (!login) login = getenv("USER"); #ifdef HAVE_UNISTD_H if (!login) { login = getlogin(); } #endif return str_isset(login) ? login : NULL; #else return NULL; #endif } /** * Enable or disable coredump * * @param enable true to enable, false to disable coredump * * @return 0 if success, otherwise errorcode */ int sys_coredump_set(bool enable) { #ifdef HAVE_SETRLIMIT const struct rlimit rlim = { enable ? RLIM_INFINITY : 0, enable ? RLIM_INFINITY : 0 }; return 0 == setrlimit(RLIMIT_CORE, &rlim) ? 0 : errno; #else (void)enable; return ENOSYS; #endif } re-1.1.0/src/tcp/000077500000000000000000000000001373627245400135115ustar00rootroot00000000000000re-1.1.0/src/tcp/mod.mk000066400000000000000000000001321373627245400146150ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += tcp/tcp.c SRCS += tcp/tcp_high.c re-1.1.0/src/tcp/tcp.c000066400000000000000000000634021373627245400144500ustar00rootroot00000000000000/** * @file tcp.c Transport Control Protocol * * Copyright (C) 2010 Creytiv.com */ #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_IO_H #include #endif #if !defined(WIN32) #define __USE_POSIX 1 /**< Use POSIX flag */ #define __USE_XOPEN2K 1/**< Use POSIX.1:2001 code */ #define __USE_MISC 1 #include #endif #ifdef __APPLE__ #include "TargetConditionals.h" #endif #include #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "tcp" #define DEBUG_LEVEL 5 #include /** Platform independent buffer type cast */ #ifdef WIN32 #define BUF_CAST (char *) #define SOK_CAST (int) #define SIZ_CAST (int) #define close closesocket #else #define BUF_CAST #define SOK_CAST #define SIZ_CAST #endif enum { TCP_TXQSZ_DEFAULT = 524288, TCP_RXSZ_DEFAULT = 8192 }; /** Defines a listening TCP socket */ struct tcp_sock { int fd; /**< Listening file descriptor */ int fdc; /**< Cached connection file descriptor */ tcp_conn_h *connh; /**< TCP Connect handler */ void *arg; /**< Handler argument */ }; /** Defines a TCP connection */ struct tcp_conn { struct list helpers; /**< List of TCP-helpers */ struct list sendq; /**< Sending queue */ int fdc; /**< Connection file descriptor */ tcp_estab_h *estabh; /**< Connection established handler */ tcp_send_h *sendh; /**< Data send handler */ tcp_recv_h *recvh; /**< Data receive handler */ tcp_close_h *closeh; /**< Connection close handler */ void *arg; /**< Handler argument */ size_t rxsz; /**< Maximum receive chunk size */ size_t txqsz; size_t txqsz_max; bool active; /**< We are connecting flag */ bool connected; /**< Connection is connected flag */ }; /** Defines a TCP-Connection Helper */ struct tcp_helper { struct le le; int layer; tcp_helper_estab_h *estabh; tcp_helper_send_h *sendh; tcp_helper_recv_h *recvh; void *arg; }; struct tcp_qent { struct le le; struct mbuf mb; }; static void tcp_recv_handler(int flags, void *arg); static bool helper_estab_handler(int *err, bool active, void *arg) { (void)err; (void)active; (void)arg; return false; } static bool helper_send_handler(int *err, struct mbuf *mb, void *arg) { (void)err; (void)mb; (void)arg; return false; } static bool helper_recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg) { (void)err; (void)mb; (void)estab; (void)arg; return false; } static void sock_destructor(void *data) { struct tcp_sock *ts = data; if (ts->fd >= 0) { fd_close(ts->fd); (void)close(ts->fd); } if (ts->fdc >= 0) (void)close(ts->fdc); } static void conn_destructor(void *data) { struct tcp_conn *tc = data; list_flush(&tc->helpers); list_flush(&tc->sendq); if (tc->fdc >= 0) { fd_close(tc->fdc); (void)close(tc->fdc); } } static void helper_destructor(void *data) { struct tcp_helper *th = data; list_unlink(&th->le); } static void qent_destructor(void *arg) { struct tcp_qent *qe = arg; list_unlink(&qe->le); mem_deref(qe->mb.buf); } static int enqueue(struct tcp_conn *tc, struct mbuf *mb) { const size_t n = mbuf_get_left(mb); struct tcp_qent *qe; int err; if (tc->txqsz + n > tc->txqsz_max) return ENOSPC; if (!tc->sendq.head && !tc->sendh) { err = fd_listen(tc->fdc, FD_READ | FD_WRITE, tcp_recv_handler, tc); if (err) return err; } qe = mem_zalloc(sizeof(*qe), qent_destructor); if (!qe) return ENOMEM; list_append(&tc->sendq, &qe->le, qe); mbuf_init(&qe->mb); err = mbuf_write_mem(&qe->mb, mbuf_buf(mb), n); qe->mb.pos = 0; if (err) mem_deref(qe); else tc->txqsz += qe->mb.end; return err; } static int dequeue(struct tcp_conn *tc) { struct tcp_qent *qe = list_ledata(tc->sendq.head); ssize_t n; #ifdef MSG_NOSIGNAL const int flags = MSG_NOSIGNAL; /* disable SIGPIPE signal */ #else const int flags = 0; #endif if (!qe) { if (tc->sendh) tc->sendh(tc->arg); return 0; } n = send(tc->fdc, BUF_CAST mbuf_buf(&qe->mb), qe->mb.end - qe->mb.pos, flags); if (n < 0) { if (EAGAIN == errno) return 0; #ifdef WIN32 if (WSAEWOULDBLOCK == WSAGetLastError()) return 0; #endif return errno; } tc->txqsz -= n; qe->mb.pos += n; if (qe->mb.pos >= qe->mb.end) mem_deref(qe); return 0; } static void conn_close(struct tcp_conn *tc, int err) { list_flush(&tc->sendq); tc->txqsz = 0; /* Stop polling */ if (tc->fdc >= 0) { fd_close(tc->fdc); (void)close(tc->fdc); tc->fdc = -1; } if (tc->closeh) tc->closeh(err, tc->arg); } static void tcp_recv_handler(int flags, void *arg) { struct tcp_conn *tc = arg; struct mbuf *mb = NULL; bool hlp_estab = false; struct le *le; ssize_t n; int err; socklen_t err_len = sizeof(err); if (flags & FD_EXCEPT) { DEBUG_INFO("recv handler: got FD_EXCEPT on fd=%d\n", tc->fdc); } /* check for any errors */ if (-1 == getsockopt(tc->fdc, SOL_SOCKET, SO_ERROR, BUF_CAST &err, &err_len)) { DEBUG_WARNING("recv handler: getsockopt: (%m)\n", errno); return; } if (err) { conn_close(tc, err); return; } #if 0 if (EINPROGRESS != err && EALREADY != err) { DEBUG_WARNING("recv handler: Socket error (%m)\n", err); return; } #endif if (flags & FD_WRITE) { if (tc->connected) { uint32_t nrefs; mem_ref(tc); err = dequeue(tc); nrefs = mem_nrefs(tc); mem_deref(tc); /* check if connection was deref'd from send handler */ if (nrefs == 1) return; if (err) { conn_close(tc, err); return; } if (!tc->sendq.head && !tc->sendh) { err = fd_listen(tc->fdc, FD_READ, tcp_recv_handler, tc); if (err) { conn_close(tc, err); return; } } if (flags & FD_READ) goto read; return; } tc->connected = true; err = fd_listen(tc->fdc, FD_READ, tcp_recv_handler, tc); if (err) { DEBUG_WARNING("recv handler: fd_listen(): %m\n", err); conn_close(tc, err); return; } le = tc->helpers.head; while (le) { struct tcp_helper *th = le->data; le = le->next; if (th->estabh(&err, tc->active, th->arg) || err) { if (err) conn_close(tc, err); return; } } if (tc->estabh) tc->estabh(tc->arg); return; } read: mb = mbuf_alloc(tc->rxsz); if (!mb) return; n = recv(tc->fdc, BUF_CAST mb->buf, mb->size, 0); if (0 == n) { mem_deref(mb); conn_close(tc, 0); return; } else if (n < 0) { #ifdef WIN32 err = WSAGetLastError(); DEBUG_WARNING("recv handler: recv(): %d\n", err); if (err == WSAECONNRESET || err == WSAECONNABORTED) { mem_deref(mb); conn_close(tc, err); return; } #else DEBUG_WARNING("recv handler: recv(): %m\n", errno); #endif goto out; } mb->end = n; le = tc->helpers.head; while (le) { struct tcp_helper *th = le->data; bool hdld = false; le = le->next; if (hlp_estab) { hdld |= th->estabh(&err, tc->active, th->arg); if (err) { conn_close(tc, err); goto out; } } if (mb->pos < mb->end) { hdld |= th->recvh(&err, mb, &hlp_estab, th->arg); if (err) { conn_close(tc, err); goto out; } } if (hdld) goto out; } mbuf_trim(mb); if (hlp_estab && tc->estabh) { uint32_t nrefs; mem_ref(tc); tc->estabh(tc->arg); nrefs = mem_nrefs(tc); mem_deref(tc); /* check if connection was deref'ed from establish handler */ if (nrefs == 1) goto out; } if (mb->pos < mb->end && tc->recvh) { tc->recvh(mb, tc->arg); } out: mem_deref(mb); } static struct tcp_conn *conn_alloc(tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { struct tcp_conn *tc; tc = mem_zalloc(sizeof(*tc), conn_destructor); if (!tc) return NULL; list_init(&tc->helpers); tc->fdc = -1; tc->rxsz = TCP_RXSZ_DEFAULT; tc->txqsz_max = TCP_TXQSZ_DEFAULT; tc->estabh = eh; tc->recvh = rh; tc->closeh = ch; tc->arg = arg; return tc; } static void tcp_sockopt_set(int fd) { #ifdef SO_LINGER const struct linger dl = {0, 0}; int err; err = setsockopt(fd, SOL_SOCKET, SO_LINGER, BUF_CAST &dl, sizeof(dl)); if (err) { DEBUG_WARNING("sockopt: SO_LINGER (%m)\n", err); } #else (void)fd; #endif } /** * Handler for incoming TCP connections. * * @param flags Event flags. * @param arg Handler argument. */ static void tcp_conn_handler(int flags, void *arg) { struct sa peer; struct tcp_sock *ts = arg; int err; (void)flags; sa_init(&peer, AF_UNSPEC); if (ts->fdc >= 0) (void)close(ts->fdc); ts->fdc = SOK_CAST accept(ts->fd, &peer.u.sa, &peer.len); if (-1 == ts->fdc) { #if TARGET_OS_IPHONE if (EAGAIN == errno) { struct tcp_sock *ts_new; struct sa laddr; err = tcp_sock_local_get(ts, &laddr); if (err) return; if (ts->fd >= 0) { fd_close(ts->fd); (void)close(ts->fd); ts->fd = -1; } err = tcp_listen(&ts_new, &laddr, NULL, NULL); if (err) return; ts->fd = ts_new->fd; ts_new->fd = -1; mem_deref(ts_new); fd_listen(ts->fd, FD_READ, tcp_conn_handler, ts); } #endif return; } err = net_sockopt_blocking_set(ts->fdc, false); if (err) { DEBUG_WARNING("conn handler: nonblock set: %m\n", err); (void)close(ts->fdc); ts->fdc = -1; return; } tcp_sockopt_set(ts->fdc); if (ts->connh) ts->connh(&peer, ts->arg); } /** * Create a TCP Socket * * @param tsp Pointer to returned TCP Socket * @param local Local listen address (NULL for any) * @param ch Incoming connection handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_sock_alloc(struct tcp_sock **tsp, const struct sa *local, tcp_conn_h *ch, void *arg) { struct addrinfo hints, *res = NULL, *r; char addr[64] = ""; char serv[6] = "0"; struct tcp_sock *ts = NULL; int error, err; if (!tsp) return EINVAL; ts = mem_zalloc(sizeof(*ts), sock_destructor); if (!ts) return ENOMEM; ts->fd = -1; ts->fdc = -1; if (local) { (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, local); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; error = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("listen: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("listen: getaddrinfo: %s:%s error=%d (%s)\n", addr, serv, error, gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } err = EINVAL; for (r = res; r; r = r->ai_next) { int fd = -1; if (ts->fd >= 0) continue; fd = SOK_CAST socket(r->ai_family, SOCK_STREAM, IPPROTO_TCP); if (fd < 0) { err = errno; continue; } (void)net_sockopt_reuse_set(fd, true); err = net_sockopt_blocking_set(fd, false); if (err) { DEBUG_WARNING("listen: nonblock set: %m\n", err); (void)close(fd); continue; } tcp_sockopt_set(fd); /* OK */ ts->fd = fd; err = 0; break; } freeaddrinfo(res); if (-1 == ts->fd) goto out; ts->connh = ch; ts->arg = arg; out: if (err) mem_deref(ts); else *tsp = ts; return err; } /** * Duplicate TCP socket * * @param tso TCP Socket to duplicate * * @return Duplicated TCP Socket if success, otherwise NULL */ struct tcp_sock *tcp_sock_dup(struct tcp_sock *tso) { struct tcp_sock *ts; if (!tso) return NULL; ts = mem_zalloc(sizeof(*ts), sock_destructor); if (!ts) return NULL; ts->fd = -1; ts->fdc = tso->fdc; tso->fdc = -1; return ts; } /** * Bind to a TCP Socket * * @param ts TCP Socket * @param local Local bind address * * @return 0 if success, otherwise errorcode */ int tcp_sock_bind(struct tcp_sock *ts, const struct sa *local) { struct addrinfo hints, *res = NULL, *r; char addr[64] = ""; char serv[NI_MAXSERV] = "0"; int error, err; if (!ts || ts->fd<0) return EINVAL; if (local) { (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, local); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; error = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("sock_bind: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("sock_bind: getaddrinfo: %s:%s error=%d (%s)\n", addr, serv, error, gai_strerror(error)); return EADDRNOTAVAIL; } err = EINVAL; for (r = res; r; r = r->ai_next) { if (bind(ts->fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) { err = errno; DEBUG_WARNING("sock_bind: bind: %m (af=%d, %J)\n", err, r->ai_family, local); continue; } /* OK */ err = 0; break; } freeaddrinfo(res); return err; } /** * Listen on a TCP Socket * * @param ts TCP Socket * @param backlog Maximum length the queue of pending connections * * @return 0 if success, otherwise errorcode */ int tcp_sock_listen(struct tcp_sock *ts, int backlog) { int err; if (!ts) return EINVAL; if (ts->fd < 0) { DEBUG_WARNING("sock_listen: invalid fd\n"); return EBADF; } if (listen(ts->fd, backlog) < 0) { err = errno; DEBUG_WARNING("sock_listen: listen(): %m\n", err); return err; } return fd_listen(ts->fd, FD_READ, tcp_conn_handler, ts); } /** * Accept an incoming TCP Connection * * @param tcp Returned TCP Connection object * @param ts Corresponding TCP Socket * @param eh TCP Connection Established handler * @param rh TCP Connection Receive data handler * @param ch TCP Connection close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_accept(struct tcp_conn **tcp, struct tcp_sock *ts, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { struct tcp_conn *tc; int err; if (!tcp || !ts || ts->fdc < 0) return EINVAL; tc = conn_alloc(eh, rh, ch, arg); if (!tc) return ENOMEM; /* Transfer ownership to TCP connection */ tc->fdc = ts->fdc; ts->fdc = -1; err = fd_listen(tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT, tcp_recv_handler, tc); if (err) { DEBUG_WARNING("accept: fd_listen(): %m\n", err); } if (err) mem_deref(tc); else *tcp = tc; return err; } /** * Reject an incoming TCP Connection * * @param ts Corresponding TCP Socket */ void tcp_reject(struct tcp_sock *ts) { if (!ts) return; if (ts->fdc >= 0) { (void)close(ts->fdc); ts->fdc = -1; } } /** * Allocate a TCP Connection * * @param tcp Returned TCP Connection object * @param peer Network address of peer * @param eh TCP Connection Established handler * @param rh TCP Connection Receive data handler * @param ch TCP Connection close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_conn_alloc(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { struct tcp_conn *tc; struct addrinfo hints, *res = NULL, *r; char addr[64]; char serv[NI_MAXSERV] = "0"; int error, err; if (!tcp || !sa_isset(peer, SA_ALL)) return EINVAL; tc = conn_alloc(eh, rh, ch, arg); if (!tc) return ENOMEM; memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, peer); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(peer)); error = getaddrinfo(addr, serv, &hints, &res); if (error) { DEBUG_WARNING("connect: getaddrinfo(): (%s)\n", gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } err = EINVAL; for (r = res; r; r = r->ai_next) { tc->fdc = SOK_CAST socket(r->ai_family, SOCK_STREAM, IPPROTO_TCP); if (tc->fdc < 0) { err = errno; continue; } err = net_sockopt_blocking_set(tc->fdc, false); if (err) { DEBUG_WARNING("connect: nonblock set: %m\n", err); (void)close(tc->fdc); tc->fdc = -1; continue; } tcp_sockopt_set(tc->fdc); err = 0; break; } freeaddrinfo(res); out: if (err) mem_deref(tc); else *tcp = tc; return err; } /** * Bind a TCP Connection to a local address * * @param tc TCP Connection object * @param local Local bind address * * @return 0 if success, otherwise errorcode */ int tcp_conn_bind(struct tcp_conn *tc, const struct sa *local) { struct addrinfo hints, *res = NULL, *r; char addr[64] = ""; char serv[NI_MAXSERV] = "0"; int error, err; if (!tc) return EINVAL; if (local) { (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, local); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; error = getaddrinfo(addr[0] ? addr : NULL, serv, &hints, &res); if (error) { DEBUG_WARNING("conn_bind: getaddrinfo(): (%s)\n", gai_strerror(error)); return EADDRNOTAVAIL; } err = EINVAL; for (r = res; r; r = r->ai_next) { (void)net_sockopt_reuse_set(tc->fdc, true); /* bind to local address */ if (bind(tc->fdc, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) { /* Special case for mingw32/wine */ if (0 == errno) { goto ok; } err = errno; DEBUG_WARNING("conn_bind: bind(): %J: %m\n", local, err); continue; } ok: /* OK */ err = 0; break; } freeaddrinfo(res); if (err) { DEBUG_WARNING("conn_bind failed: %J (%m)\n", local, err); } return err; } /** * Connect to a remote peer * * @param tc TCP Connection object * @param peer Network address of peer * * @return 0 if success, otherwise errorcode */ int tcp_conn_connect(struct tcp_conn *tc, const struct sa *peer) { struct addrinfo hints, *res = NULL, *r; char addr[64]; char serv[NI_MAXSERV]; int error, err = 0; if (!tc || !sa_isset(peer, SA_ALL)) return EINVAL; tc->active = true; if (tc->fdc < 0) { DEBUG_WARNING("invalid fd\n"); return EBADF; } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = PF_UNSPEC; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, peer); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(peer)); error = getaddrinfo(addr, serv, &hints, &res); if (error) { DEBUG_WARNING("connect: getaddrinfo(): (%s)\n", gai_strerror(error)); return EADDRNOTAVAIL; } for (r = res; r; r = r->ai_next) { struct sockaddr *sa = r->ai_addr; again: if (0 == connect(tc->fdc, sa, SIZ_CAST r->ai_addrlen)) { err = 0; goto out; } else { #ifdef WIN32 /* Special error handling for Windows */ if (WSAEWOULDBLOCK == WSAGetLastError()) { err = 0; goto out; } #endif /* Special case for mingw32/wine */ if (0 == errno) { err = 0; goto out; } if (EINTR == errno) goto again; if (EINPROGRESS != errno && EALREADY != errno) { err = errno; DEBUG_INFO("connect: connect() %J: %m\n", peer, err); } } } out: freeaddrinfo(res); if (err) return err; return fd_listen(tc->fdc, FD_READ | FD_WRITE | FD_EXCEPT, tcp_recv_handler, tc); } static int tcp_send_internal(struct tcp_conn *tc, struct mbuf *mb, struct le *le) { int err = 0; ssize_t n; #ifdef MSG_NOSIGNAL const int flags = MSG_NOSIGNAL; /* disable SIGPIPE signal */ #else const int flags = 0; #endif if (tc->fdc < 0) return ENOTCONN; if (!mbuf_get_left(mb)) { DEBUG_WARNING("send: empty mbuf (pos=%u end=%u)\n", mb->pos, mb->end); return EINVAL; } /* call helpers in reverse order */ while (le) { struct tcp_helper *th = le->data; le = le->prev; if (th->sendh(&err, mb, th->arg) || err) return err; } if (tc->sendq.head) return enqueue(tc, mb); n = send(tc->fdc, BUF_CAST mbuf_buf(mb), mb->end - mb->pos, flags); if (n < 0) { if (EAGAIN == errno) return enqueue(tc, mb); #ifdef WIN32 if (WSAEWOULDBLOCK == WSAGetLastError()) return enqueue(tc, mb); #endif err = errno; DEBUG_WARNING("send: write(): %m (fdc=%d)\n", err, tc->fdc); #ifdef WIN32 DEBUG_WARNING("WIN32 error: %d\n", WSAGetLastError()); #endif return err; } if ((size_t)n < mb->end - mb->pos) { mb->pos += n; err = enqueue(tc, mb); mb->pos -= n; return err; } return 0; } /** * Send data on a TCP Connection to a remote peer * * @param tc TCP Connection * @param mb Buffer to send * * @return 0 if success, otherwise errorcode */ int tcp_send(struct tcp_conn *tc, struct mbuf *mb) { if (!tc || !mb) return EINVAL; return tcp_send_internal(tc, mb, tc->helpers.tail); } /** * Send data on a TCP Connection to a remote peer bypassing this * helper and the helpers above it. * * @param tc TCP Connection * @param mb Buffer to send * @param th TCP Helper * * @return 0 if success, otherwise errorcode */ int tcp_send_helper(struct tcp_conn *tc, struct mbuf *mb, struct tcp_helper *th) { if (!tc || !mb || !th) return EINVAL; return tcp_send_internal(tc, mb, th->le.prev); } /** * Set the send handler on a TCP Connection, which will be called * every time it is ready to send data * * @param tc TCP Connection * @param sendh TCP Send handler * * @return 0 if success, otherwise errorcode */ int tcp_set_send(struct tcp_conn *tc, tcp_send_h *sendh) { if (!tc) return EINVAL; tc->sendh = sendh; if (tc->sendq.head || !sendh) return 0; return fd_listen(tc->fdc, FD_READ | FD_WRITE, tcp_recv_handler, tc); } /** * Set handlers on a TCP Connection * * @param tc TCP Connection * @param eh TCP Connection Established handler * @param rh TCP Connection Receive data handler * @param ch TCP Connection Close handler * @param arg Handler argument */ void tcp_set_handlers(struct tcp_conn *tc, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { if (!tc) return; tc->estabh = eh; tc->recvh = rh; tc->closeh = ch; tc->arg = arg; } /** * Get local network address of TCP Socket * * @param ts TCP Socket * @param local Returned local network address * * @return 0 if success, otherwise errorcode */ int tcp_sock_local_get(const struct tcp_sock *ts, struct sa *local) { if (!ts || !local) return EINVAL; sa_init(local, AF_UNSPEC); if (getsockname(ts->fd, &local->u.sa, &local->len) < 0) { DEBUG_WARNING("local get: getsockname(): %m\n", errno); return errno; } return 0; } /** * Get local network address of TCP Connection * * @param tc TCP Connection * @param local Returned local network address * * @return 0 if success, otherwise errorcode */ int tcp_conn_local_get(const struct tcp_conn *tc, struct sa *local) { if (!tc || !local) return EINVAL; sa_init(local, AF_UNSPEC); if (getsockname(tc->fdc, &local->u.sa, &local->len) < 0) { DEBUG_WARNING("conn local get: getsockname(): %m\n", errno); return errno; } return 0; } /** * Get remote peer network address of TCP Connection * * @param tc TCP Connection * @param peer Returned remote peer network address * * @return 0 if success, otherwise errorcode */ int tcp_conn_peer_get(const struct tcp_conn *tc, struct sa *peer) { if (!tc || !peer) return EINVAL; sa_init(peer, AF_UNSPEC); if (getpeername(tc->fdc, &peer->u.sa, &peer->len) < 0) { DEBUG_WARNING("conn peer get: getpeername(): %m\n", errno); return errno; } return 0; } /** * Set the maximum receive chunk size on a TCP Connection * * @param tc TCP Connection * @param rxsz Maximum receive chunk size */ void tcp_conn_rxsz_set(struct tcp_conn *tc, size_t rxsz) { if (!tc) return; tc->rxsz = rxsz; } /** * Set the maximum send queue size on a TCP Connection * * @param tc TCP Connection * @param txqsz Maximum send queue size */ void tcp_conn_txqsz_set(struct tcp_conn *tc, size_t txqsz) { if (!tc) return; tc->txqsz_max = txqsz; } /** * Get the file descriptor of a TCP Connection * * @param tc TCP-Connection * * @return File destriptor, or -1 if errors */ int tcp_conn_fd(const struct tcp_conn *tc) { return tc ? tc->fdc : -1; } /** * Get the current length of the transmit queue on a TCP Connection * * @param tc TCP-Connection * * @return Current transmit queue length, or 0 if errors */ size_t tcp_conn_txqsz(const struct tcp_conn *tc) { return tc ? tc->txqsz : 0; } static bool sort_handler(struct le *le1, struct le *le2, void *arg) { struct tcp_helper *th1 = le1->data, *th2 = le2->data; (void)arg; return th1->layer <= th2->layer; } /** * Register a new TCP-helper on a TCP-Connection * * @param thp Pointer to allocated TCP helper * @param tc TCP Connection * @param layer Protocol layer; higher number means higher up in stack * @param eh Established handler * @param sh Send handler * @param rh Receive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_register_helper(struct tcp_helper **thp, struct tcp_conn *tc, int layer, tcp_helper_estab_h *eh, tcp_helper_send_h *sh, tcp_helper_recv_h *rh, void *arg) { struct tcp_helper *th; if (!tc) return EINVAL; th = mem_zalloc(sizeof(*th), helper_destructor); if (!th) return ENOMEM; list_append(&tc->helpers, &th->le, th); th->layer = layer; th->estabh = eh ? eh : helper_estab_handler; th->sendh = sh ? sh : helper_send_handler; th->recvh = rh ? rh : helper_recv_handler; th->arg = arg; list_sort(&tc->helpers, sort_handler, NULL); if (thp) *thp = th; return 0; } re-1.1.0/src/tcp/tcp_high.c000066400000000000000000000054151373627245400154470ustar00rootroot00000000000000/** * @file tcp_high.c High-level TCP functions * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include /** * Create and listen on a TCP Socket * * @param tsp Pointer to returned TCP Socket * @param local Local listen address (NULL for any) * @param ch Incoming connection handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_listen(struct tcp_sock **tsp, const struct sa *local, tcp_conn_h *ch, void *arg) { struct tcp_sock *ts = NULL; int err; if (!tsp) return EINVAL; err = tcp_sock_alloc(&ts, local, ch, arg); if (err) goto out; err = tcp_sock_bind(ts, local); if (err) goto out; err = tcp_sock_listen(ts, 5); if (err) goto out; out: if (err) ts = mem_deref(ts); else *tsp = ts; return err; } /** * Make a TCP Connection to a remote peer * * @param tcp Returned TCP Connection object * @param peer Network address of peer * @param eh TCP Connection Established handler * @param rh TCP Connection Receive data handler * @param ch TCP Connection close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_connect(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, void *arg) { struct tcp_conn *tc = NULL; int err; if (!tcp || !peer) return EINVAL; err = tcp_conn_alloc(&tc, peer, eh,rh, ch, arg); if (err) goto out; err = tcp_conn_connect(tc, peer); if (err) goto out; out: if (err) tc = mem_deref(tc); else *tcp = tc; return err; } /** * Make a TCP Connection to a remote peer * * @param tcp Returned TCP Connection object * @param peer Network address of peer * @param eh TCP Connection Established handler * @param rh TCP Connection Receive data handler * @param ch TCP Connection close handler * @param local Bind to local address * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int tcp_connect_bind(struct tcp_conn **tcp, const struct sa *peer, tcp_estab_h *eh, tcp_recv_h *rh, tcp_close_h *ch, const struct sa *local, void *arg) { struct tcp_conn *tc = NULL; int err; if (!tcp || !peer) return EINVAL; err = tcp_conn_alloc(&tc, peer, eh,rh, ch, arg); if (err) goto out; err = tcp_conn_bind(tc, local); if (err) goto out; err = tcp_conn_connect(tc, peer); if (err) goto out; out: if (err) tc = mem_deref(tc); else *tcp = tc; return err; } /** * Get local network address of TCP Socket * * @param ts TCP Socket * @param local Returned local network address * * @return 0 if success, otherwise errorcode */ int tcp_local_get(const struct tcp_sock *ts, struct sa *local) { return tcp_sock_local_get(ts, local); } re-1.1.0/src/telev/000077500000000000000000000000001373627245400140425ustar00rootroot00000000000000re-1.1.0/src/telev/mod.mk000066400000000000000000000001071373627245400151500ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += telev/telev.c re-1.1.0/src/telev/telev.c000066400000000000000000000146771373627245400153440ustar00rootroot00000000000000/** * @file telev.c Telephony Events implementation (RFC 4733) * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include /* * Implements a subset of RFC 4733, * "RTP Payload for DTMF Digits, Telephony Tones, and Telephony Signals" * * Call telev_send() to send telephony events, and telev_recv() to receive. * The function telev_poll() must be called periodically, with a minimum * and stable interval of less than or equal to packet time (e.g. 50ms) * * NOTE: RTP timestamp and sequence number is kept in application * * * Example sending: * * ms M ts seq ev dur End * * press X --> * 50 1 0 1 X 400 0 * 100 0 0 2 X 800 0 * 150 0 0 3 X 1200 0 * 200 0 0 4 X 1600 0 * ... . . . . .... . * release X --> * 250 0 0 5 X 1600 1 * 300 0 0 6 X 1600 1 * press Y --> * 350 1 2000 7 Y 400 0 * ... . .... . . ... . */ enum { TXC_DIGIT_MIN = 9, TXC_END = 3, EVENT_END = 0xff, PAYLOAD_SIZE = 4, VOLUME = 10, QUEUE_SIZE = 8192, }; enum state { IDLE, SENDING, ENDING, }; struct telev_fmt { uint8_t event; bool end; uint8_t vol; uint16_t dur; }; /** Defines a Telephony Events state */ struct telev { /* tx */ struct mbuf *mb; uint32_t ptime; uint16_t pdur; enum state state; int event; uint16_t dur; uint32_t txc; /* rx */ int rx_event; bool rx_end; }; const char telev_rtpfmt[] = "telephone-event"; static int payload_encode(struct mbuf *mb, int event, bool end, uint16_t dur) { size_t pos; int err; if (!mb) return EINVAL; pos = mb->pos; err = mbuf_write_u8(mb, event); err |= mbuf_write_u8(mb, (end ? 0x80 : 0x00) | (VOLUME & 0x3f)); err |= mbuf_write_u16(mb, htons(dur)); if (err) mb->pos = pos; return err; } static int payload_decode(struct mbuf *mb, struct telev_fmt *fmt) { uint8_t b; if (!mb || !fmt) return EINVAL; if (mbuf_get_left(mb) < PAYLOAD_SIZE) return ENOENT; fmt->event = mbuf_read_u8(mb); b = mbuf_read_u8(mb); fmt->end = (b & 0x80) == 0x80; fmt->vol = (b & 0x3f); fmt->dur = ntohs(mbuf_read_u16(mb)); return 0; } static void destructor(void *arg) { struct telev *t = arg; mem_deref(t->mb); } /** * Allocate a new Telephony Events state * * @param tp Pointer to allocated object * @param ptime Packet time in [ms] * * @return 0 if success, otherwise errorcode */ int telev_alloc(struct telev **tp, uint32_t ptime) { struct telev *t; int err = 0; if (!tp || !ptime) return EINVAL; t = mem_zalloc(sizeof(*t), destructor); if (!t) return ENOMEM; t->mb = mbuf_alloc(16); if (!t->mb) { err = ENOMEM; goto out; } t->state = IDLE; t->ptime = ptime; t->pdur = ptime * 8; t->rx_event = -1; out: if (err) mem_deref(t); else *tp = t; return err; } /** * Sets the sampling rate * * @param tel Telephony Event state * @param srate Sampling rate in [Hz] * * @return 0 if success, otherwise errorcode */ int telev_set_srate(struct telev *tel, uint32_t srate) { if (!tel || !srate) return EINVAL; tel->pdur = tel->ptime * srate / 1000; return 0; } /** * Send a Telephony Event * * @param tel Telephony Event state * @param event The Event to send * @param end End-of-event flag * * @return 0 if success, otherwise errorcode */ int telev_send(struct telev *tel, int event, bool end) { size_t pos; int err; if (!tel) return EINVAL; if (tel->mb->end >= QUEUE_SIZE) return EOVERFLOW; pos = tel->mb->pos; tel->mb->pos = tel->mb->end; err = mbuf_write_u8(tel->mb, end ? EVENT_END : event); tel->mb->pos = pos; return err; } /** * Receive a Telephony Event * * @param tel Telephony Event state * @param mb Buffer to decode * @param event The received event, set on return * @param end End-of-event flag, set on return * * @return 0 if success, otherwise errorcode */ int telev_recv(struct telev *tel, struct mbuf *mb, int *event, bool *end) { struct telev_fmt fmt; int err; if (!tel || !mb || !event || !end) return EINVAL; err = payload_decode(mb, &fmt); if (err) return err; if (fmt.end) { if (tel->rx_end) return EALREADY; *event = fmt.event; *end = true; tel->rx_event = -1; tel->rx_end = true; return 0; } if (fmt.event == tel->rx_event) return EALREADY; *event = tel->rx_event = fmt.event; *end = tel->rx_end = false; return 0; } /** * Poll a Telephony Event state for sending * * @param tel Telephony Event state * @param marker Marker bit, set on return * @param mb Buffer with encoded data to send * * @return 0 if success, otherwise errorcode */ int telev_poll(struct telev *tel, bool *marker, struct mbuf *mb) { bool mrk = false; int err = ENOENT; if (!tel || !marker || !mb) return EINVAL; switch (tel->state) { case IDLE: if (!mbuf_get_left(tel->mb)) break; mrk = true; tel->event = mbuf_read_u8(tel->mb); tel->dur = tel->pdur; tel->state = SENDING; tel->txc = 1; err = payload_encode(mb, tel->event, false, tel->dur); break; case SENDING: tel->dur += tel->pdur; err = payload_encode(mb, tel->event, false, tel->dur); if (++tel->txc < TXC_DIGIT_MIN) break; if (!mbuf_get_left(tel->mb)) break; if (mbuf_read_u8(tel->mb) != EVENT_END) tel->mb->pos--; tel->state = ENDING; tel->txc = 0; break; case ENDING: err = payload_encode(mb, tel->event, true, tel->dur); if (++tel->txc < TXC_END) break; if (!mbuf_get_left(tel->mb)) tel->mb->pos = tel->mb->end = 0; tel->state = IDLE; break; } if (!err) *marker = mrk; return err; } /** * Convert DTMF digit to Event code * * @param digit DTMF Digit * * @return Event code, or -1 if error */ int telev_digit2code(int digit) { if (isdigit(digit)) return digit - '0'; else if (digit == '*') return 10; else if (digit == '#') return 11; else if ('a' <= digit && digit <= 'd') return digit - 'a' + 12; else if ('A' <= digit && digit <= 'D') return digit - 'A' + 12; else return -1; } /** * Convert Event code to DTMF digit * * @param code Event code * * @return DTMF Digit, or -1 if error */ int telev_code2digit(int code) { static const char map[] = "0123456789*#ABCD"; if (code < 0 || code >= 16) return -1; return map[code]; } re-1.1.0/src/tls/000077500000000000000000000000001373627245400135255ustar00rootroot00000000000000re-1.1.0/src/tls/mod.mk000066400000000000000000000002451373627245400146360ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # ifneq ($(USE_OPENSSL),) SRCS += tls/openssl/tls.c SRCS += tls/openssl/tls_tcp.c SRCS += tls/openssl/tls_udp.c endif re-1.1.0/src/tls/openssl/000077500000000000000000000000001373627245400152105ustar00rootroot00000000000000re-1.1.0/src/tls/openssl/tls.c000066400000000000000000000524231373627245400161640ustar00rootroot00000000000000/** * @file openssl/tls.c TLS backend using OpenSSL * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tls.h" #define DEBUG_MODULE "tls" #define DEBUG_LEVEL 5 #include /* NOTE: shadow struct defined in tls_*.c */ struct tls_conn { SSL *ssl; }; static void destructor(void *data) { struct tls *tls = data; if (tls->ctx) SSL_CTX_free(tls->ctx); if (tls->cert) X509_free(tls->cert); mem_deref(tls->pass); } /*The password code is not thread safe*/ static int password_cb(char *buf, int size, int rwflag, void *userdata) { struct tls *tls = userdata; (void)rwflag; DEBUG_NOTICE("password callback\n"); if (size < (int)strlen(tls->pass)+1) return 0; strncpy(buf, tls->pass, size); return (int)strlen(tls->pass); } static int keytype2int(enum tls_keytype type) { switch (type) { case TLS_KEYTYPE_EC: return EVP_PKEY_EC; case TLS_KEYTYPE_RSA: return EVP_PKEY_RSA; default: return EVP_PKEY_NONE; } } /** * Allocate a new TLS context * * @param tlsp Pointer to allocated TLS context * @param method TLS method * @param keyfile Optional private key file * @param pwd Optional password * * @return 0 if success, otherwise errorcode */ int tls_alloc(struct tls **tlsp, enum tls_method method, const char *keyfile, const char *pwd) { struct tls *tls; int r, err; if (!tlsp) return EINVAL; tls = mem_zalloc(sizeof(*tls), destructor); if (!tls) return ENOMEM; switch (method) { case TLS_METHOD_SSLV23: tls->ctx = SSL_CTX_new(SSLv23_method()); break; #ifdef USE_OPENSSL_DTLS case TLS_METHOD_DTLSV1: #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) tls->ctx = SSL_CTX_new(DTLS_method()); #else tls->ctx = SSL_CTX_new(DTLSv1_method()); #endif break; #ifdef SSL_OP_NO_DTLSv1_2 /* DTLS v1.2 is available in OpenSSL 1.0.2 and later */ case TLS_METHOD_DTLS: tls->ctx = SSL_CTX_new(DTLS_method()); break; case TLS_METHOD_DTLSV1_2: #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) tls->ctx = SSL_CTX_new(DTLS_method()); #else tls->ctx = SSL_CTX_new(DTLSv1_2_method()); #endif break; #endif #endif default: DEBUG_WARNING("tls method %d not supported\n", method); err = ENOSYS; goto out; } if (!tls->ctx) { ERR_clear_error(); err = ENOMEM; goto out; } #if (OPENSSL_VERSION_NUMBER < 0x00905100L) SSL_CTX_set_verify_depth(tls->ctx, 1); #endif /* Load our keys and certificates */ if (keyfile) { if (pwd) { err = str_dup(&tls->pass, pwd); if (err) goto out; SSL_CTX_set_default_passwd_cb(tls->ctx, password_cb); SSL_CTX_set_default_passwd_cb_userdata(tls->ctx, tls); } r = SSL_CTX_use_certificate_chain_file(tls->ctx, keyfile); if (r <= 0) { DEBUG_WARNING("Can't read certificate file: %s (%d)\n", keyfile, r); ERR_clear_error(); err = EINVAL; goto out; } r = SSL_CTX_use_PrivateKey_file(tls->ctx, keyfile, SSL_FILETYPE_PEM); if (r <= 0) { DEBUG_WARNING("Can't read key file: %s (%d)\n", keyfile, r); ERR_clear_error(); err = EINVAL; goto out; } } err = 0; out: if (err) mem_deref(tls); else *tlsp = tls; return err; } /** * Set default locations for trusted CA certificates * * @param tls TLS Context * @param cafile PEM file with CA certificates * * @return 0 if success, otherwise errorcode */ int tls_add_ca(struct tls *tls, const char *cafile) { if (!tls || !cafile) return EINVAL; /* Load the CAs we trust */ if (!(SSL_CTX_load_verify_locations(tls->ctx, cafile, NULL))) { DEBUG_WARNING("Can't read CA file: %s\n", cafile); ERR_clear_error(); return EINVAL; } return 0; } /** * Set SSL verification of the certificate purpose * * @param tls TLS Context * @param purpose Certificate purpose as string * * @return int 0 if success, errorcode otherwise */ int tls_set_verify_purpose(struct tls *tls, const char *purpose) { int err; int i; X509_PURPOSE *xptmp; if (!tls || !purpose) return EINVAL; #if OPENSSL_VERSION_NUMBER >= 0x10100000L i = X509_PURPOSE_get_by_sname(purpose); #else i = X509_PURPOSE_get_by_sname((char *) purpose); #endif if (i < 0) return EINVAL; /* purpose index -> purpose object */ /* purpose object -> purpose value */ xptmp = X509_PURPOSE_get0(i); i = X509_PURPOSE_get_id(xptmp); err = SSL_CTX_set_purpose(tls->ctx, i); return err == 1 ? 0 : EINVAL; } /** * Set SSL verification of hostname * * @param tc TLS Connection * @param hostname Certificate hostname * * @return int 0 if success, errorcode otherwise */ int tls_peer_set_verify_host(struct tls_conn *tc, const char *hostname) { int err = 0; if (!tc) return EINVAL; #if OPENSSL_VERSION_NUMBER >= 0x10100000L err = SSL_set1_host(tc->ssl, hostname); #else DEBUG_WARNING("verify hostname needs openssl version 1.1.0\n"); return ENOSYS; #endif return err == 1 ? 0 : EINVAL; } /** * Convert string hostname to pl hostname * * @param tls_hostname Certificate hostname as string * @param hostname Certificate hostname as pl * * @return int 0 if success, errorcode otherwise */ int tls_set_hostname(char *tls_hostname, const struct pl *hostname) { if (!tls_hostname || !hostname) return EINVAL; #if OPENSSL_VERSION_NUMBER < 0x10100000L DEBUG_WARNING("verify hostname needs openssl version 1.1.0\n"); return ENOSYS; #endif return pl_strdup(&tls_hostname, hostname); } /** * Generate and set selfsigned certificate on TLS context * * @param tls TLS Context * @param cn Common Name * * @return 0 if success, otherwise errorcode */ int tls_set_selfsigned(struct tls *tls, const char *cn) { return tls_set_selfsigned_rsa(tls, cn, 1024); } int tls_set_selfsigned_rsa(struct tls *tls, const char *cn, size_t bits) { X509_NAME *subj = NULL; EVP_PKEY *key = NULL; X509 *cert = NULL; BIGNUM *bn = NULL; RSA *rsa = NULL; int r, err = ENOMEM; if (!tls || !cn) return EINVAL; rsa = RSA_new(); if (!rsa) goto out; bn = BN_new(); if (!bn) goto out; BN_set_word(bn, RSA_F4); if (!RSA_generate_key_ex(rsa, (int)bits, bn, NULL)) goto out; key = EVP_PKEY_new(); if (!key) goto out; if (!EVP_PKEY_set1_RSA(key, rsa)) goto out; cert = X509_new(); if (!cert) goto out; if (!X509_set_version(cert, 2)) goto out; if (!ASN1_INTEGER_set(X509_get_serialNumber(cert), rand_u32())) goto out; subj = X509_NAME_new(); if (!subj) goto out; if (!X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC, (unsigned char *)cn, (int)strlen(cn), -1, 0)) goto out; if (!X509_set_issuer_name(cert, subj) || !X509_set_subject_name(cert, subj)) goto out; #if OPENSSL_VERSION_NUMBER >= 0x10100000L if (!X509_gmtime_adj(X509_getm_notBefore(cert), -3600*24*365) || !X509_gmtime_adj(X509_getm_notAfter(cert), 3600*24*365*10)) goto out; #else if (!X509_gmtime_adj(X509_get_notBefore(cert), -3600*24*365) || !X509_gmtime_adj(X509_get_notAfter(cert), 3600*24*365*10)) goto out; #endif if (!X509_set_pubkey(cert, key)) goto out; if (!X509_sign(cert, key, EVP_sha1())) goto out; r = SSL_CTX_use_certificate(tls->ctx, cert); if (r != 1) goto out; r = SSL_CTX_use_PrivateKey(tls->ctx, key); if (r != 1) goto out; if (tls->cert) X509_free(tls->cert); tls->cert = cert; cert = NULL; err = 0; out: if (subj) X509_NAME_free(subj); if (cert) X509_free(cert); if (key) EVP_PKEY_free(key); if (rsa) RSA_free(rsa); if (bn) BN_free(bn); if (err) ERR_clear_error(); return err; } /** * Set the certificate and private key on a TLS context * * @param tls TLS Context * @param cert Certificate in PEM format * @param len_cert Length of certificate PEM string * @param key Private key in PEM format, will be read from cert if NULL * @param len_key Length of private key PEM string * * @return 0 if success, otherwise errorcode */ int tls_set_certificate_pem(struct tls *tls, const char *cert, size_t len_cert, const char *key, size_t len_key) { BIO *bio = NULL, *kbio = NULL; X509 *x509 = NULL; EVP_PKEY *pkey = NULL; int r, err = ENOMEM; if (!tls || !cert || !len_cert || (key && !len_key)) return EINVAL; if (!key) { key = cert; len_key = len_cert; } bio = BIO_new_mem_buf((char *)cert, (int)len_cert); kbio = BIO_new_mem_buf((char *)key, (int)len_key); if (!bio || !kbio) goto out; x509 = PEM_read_bio_X509(bio, NULL, 0, NULL); pkey = PEM_read_bio_PrivateKey(kbio, NULL, 0, NULL); if (!x509 || !pkey) goto out; r = SSL_CTX_use_certificate(tls->ctx, x509); if (r != 1) goto out; r = SSL_CTX_use_PrivateKey(tls->ctx, pkey); if (r != 1) { DEBUG_WARNING("set_certificate_pem: use_PrivateKey failed\n"); goto out; } if (tls->cert) X509_free(tls->cert); tls->cert = x509; x509 = NULL; err = 0; out: if (x509) X509_free(x509); if (pkey) EVP_PKEY_free(pkey); if (bio) BIO_free(bio); if (kbio) BIO_free(kbio); if (err) ERR_clear_error(); return err; } /** * Set the certificate and private key on a TLS context * * @param tls TLS Context * @param keytype Private key type * @param cert Certificate in DER format * @param len_cert Length of certificate DER bytes * @param key Private key in DER format, will be read from cert if NULL * @param len_key Length of private key DER bytes * * @return 0 if success, otherwise errorcode */ int tls_set_certificate_der(struct tls *tls, enum tls_keytype keytype, const uint8_t *cert, size_t len_cert, const uint8_t *key, size_t len_key) { const uint8_t *buf_cert; X509 *x509 = NULL; EVP_PKEY *pkey = NULL; int r, type, err = ENOMEM; if (!tls || !cert || !len_cert || (key && !len_key)) return EINVAL; type = keytype2int(keytype); if (type == EVP_PKEY_NONE) return EINVAL; buf_cert = cert; x509 = d2i_X509(NULL, &buf_cert, len_cert); if (!x509) goto out; if (!key) { key = buf_cert; len_key = len_cert - (buf_cert - cert); } pkey = d2i_PrivateKey(type, NULL, &key, len_key); if (!pkey) goto out; r = SSL_CTX_use_certificate(tls->ctx, x509); if (r != 1) goto out; r = SSL_CTX_use_PrivateKey(tls->ctx, pkey); if (r != 1) { DEBUG_WARNING("set_certificate_der: use_PrivateKey failed\n"); goto out; } if (tls->cert) X509_free(tls->cert); tls->cert = x509; x509 = NULL; err = 0; out: if (x509) X509_free(x509); if (pkey) EVP_PKEY_free(pkey); if (err) ERR_clear_error(); return err; } /** * Set the certificate and private key on a TLS context * * @param tls TLS Context * @param pem Certificate and private key in PEM format * @param len Length of PEM string * * @return 0 if success, otherwise errorcode */ int tls_set_certificate(struct tls *tls, const char *pem, size_t len) { return tls_set_certificate_pem(tls, pem, len, NULL, 0); } static int verify_handler(int ok, X509_STORE_CTX *ctx) { (void)ok; (void)ctx; return 1; /* We trust the certificate from peer */ } /** * Set TLS server context to request certificate from client * * @param tls TLS Context */ void tls_set_verify_client(struct tls *tls) { if (!tls) return; SSL_CTX_set_verify_depth(tls->ctx, 0); SSL_CTX_set_verify(tls->ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_handler); } /** * Set SRTP suites on TLS context * * @param tls TLS Context * @param suites Secure-RTP Profiles * * @return 0 if success, otherwise errorcode */ int tls_set_srtp(struct tls *tls, const char *suites) { #ifdef USE_OPENSSL_SRTP if (!tls || !suites) return EINVAL; if (0 != SSL_CTX_set_tlsext_use_srtp(tls->ctx, suites)) { ERR_clear_error(); return ENOSYS; } return 0; #else (void)tls; (void)suites; return ENOSYS; #endif } static int cert_fingerprint(X509 *cert, enum tls_fingerprint type, uint8_t *md, size_t size) { unsigned int len = (unsigned int)size; int n; switch (type) { case TLS_FINGERPRINT_SHA1: if (size < 20) return EOVERFLOW; n = X509_digest(cert, EVP_sha1(), md, &len); break; case TLS_FINGERPRINT_SHA256: if (size < 32) return EOVERFLOW; n = X509_digest(cert, EVP_sha256(), md, &len); break; default: return ENOSYS; } if (n != 1) { ERR_clear_error(); return ENOENT; } return 0; } /** * Get fingerprint of local certificate * * @param tls TLS Context * @param type Digest type * @param md Buffer for fingerprint digest * @param size Buffer size * * @return 0 if success, otherwise errorcode */ int tls_fingerprint(const struct tls *tls, enum tls_fingerprint type, uint8_t *md, size_t size) { if (!tls || !tls->cert || !md) return EINVAL; return cert_fingerprint(tls->cert, type, md, size); } /** * Get fingerprint of peer certificate of a TLS connection * * @param tc TLS Connection * @param type Digest type * @param md Buffer for fingerprint digest * @param size Buffer size * * @return 0 if success, otherwise errorcode */ int tls_peer_fingerprint(const struct tls_conn *tc, enum tls_fingerprint type, uint8_t *md, size_t size) { X509 *cert; int err; if (!tc || !md) return EINVAL; cert = SSL_get_peer_certificate(tc->ssl); if (!cert) return ENOENT; err = cert_fingerprint(cert, type, md, size); X509_free(cert); return err; } /** * Get common name of peer certificate of a TLS connection * * @param tc TLS Connection * @param cn Returned common name * @param size Size of common name * * @return 0 if success, otherwise errorcode */ int tls_peer_common_name(const struct tls_conn *tc, char *cn, size_t size) { X509 *cert; int n; if (!tc || !cn || !size) return EINVAL; cert = SSL_get_peer_certificate(tc->ssl); if (!cert) return ENOENT; n = X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, cn, (int)size); X509_free(cert); if (n < 0) { ERR_clear_error(); return ENOENT; } return 0; } /** * Verify peer certificate of a TLS connection * * @param tc TLS Connection * * @return 0 if verified, otherwise errorcode */ int tls_peer_verify(const struct tls_conn *tc) { if (!tc) return EINVAL; if (SSL_get_verify_result(tc->ssl) != X509_V_OK) return EAUTH; return 0; } /** * Get SRTP suite and keying material of a TLS connection * * @param tc TLS Connection * @param suite Returned SRTP suite * @param cli_key Client key * @param cli_key_size Client key size * @param srv_key Server key * @param srv_key_size Server key size * * @return 0 if success, otherwise errorcode */ int tls_srtp_keyinfo(const struct tls_conn *tc, enum srtp_suite *suite, uint8_t *cli_key, size_t cli_key_size, uint8_t *srv_key, size_t srv_key_size) { #ifdef USE_OPENSSL_SRTP static const char *label = "EXTRACTOR-dtls_srtp"; size_t key_size, salt_size, size; SRTP_PROTECTION_PROFILE *sel; uint8_t keymat[256], *p; if (!tc || !suite || !cli_key || !srv_key) return EINVAL; sel = SSL_get_selected_srtp_profile(tc->ssl); if (!sel) return ENOENT; switch (sel->id) { case SRTP_AES128_CM_SHA1_80: *suite = SRTP_AES_CM_128_HMAC_SHA1_80; key_size = 16; salt_size = 14; break; case SRTP_AES128_CM_SHA1_32: *suite = SRTP_AES_CM_128_HMAC_SHA1_32; key_size = 16; salt_size = 14; break; #ifdef SRTP_AEAD_AES_128_GCM case SRTP_AEAD_AES_128_GCM: *suite = SRTP_AES_128_GCM; key_size = 16; salt_size = 12; break; #endif #ifdef SRTP_AEAD_AES_256_GCM case SRTP_AEAD_AES_256_GCM: *suite = SRTP_AES_256_GCM; key_size = 32; salt_size = 12; break; #endif default: return ENOSYS; } size = key_size + salt_size; if (cli_key_size < size || srv_key_size < size) return EOVERFLOW; if (sizeof(keymat) < 2*size) return EOVERFLOW; if (1 != SSL_export_keying_material(tc->ssl, keymat, 2*size, label, strlen(label), NULL, 0, 0)) { ERR_clear_error(); return ENOENT; } p = keymat; memcpy(cli_key, p, key_size); p += key_size; memcpy(srv_key, p, key_size); p += key_size; memcpy(cli_key + key_size, p, salt_size); p += salt_size; memcpy(srv_key + key_size, p, salt_size); return 0; #else (void)tc; (void)suite; (void)cli_key; (void)cli_key_size; (void)srv_key; (void)srv_key_size; return ENOSYS; #endif } /** * Get cipher name of a TLS connection * * @param tc TLS Connection * * @return name of cipher actually used. */ const char *tls_cipher_name(const struct tls_conn *tc) { if (!tc) return NULL; return SSL_get_cipher_name(tc->ssl); } /** * Set the ciphers to use for this TLS context * * @param tls TLS Context * @param cipherv Vector of cipher names, in order of priority * @param count Number of cipher names in the vector * * @return 0 if success, otherwise errorcode */ int tls_set_ciphers(struct tls *tls, const char *cipherv[], size_t count) { struct mbuf *mb; int r, err; size_t i; if (!tls || !cipherv || !count) return EINVAL; mb = mbuf_alloc(32 * count); if (!mb) return ENOMEM; for (i=0; i0 ? ":" : "", cipherv[i]); if (err) goto out; } err = mbuf_write_u8(mb, '\0'); if (err) goto out; r = SSL_CTX_set_cipher_list(tls->ctx, (char *)mb->buf); if (r <= 0) { ERR_clear_error(); err = EPROTO; goto out; } out: mem_deref(mb); return err; } /** * Set the server name on a TLS Connection, using TLS SNI extension. * * @param tc TLS Connection * @param servername Server name * * @return 0 if success, otherwise errorcode */ int tls_set_servername(struct tls_conn *tc, const char *servername) { if (!tc || !servername) return EINVAL; if (1 != SSL_set_tlsext_host_name(tc->ssl, servername)) { DEBUG_WARNING("tls: SSL_set_tlsext_host_name error\n"); ERR_clear_error(); return EPROTO; } return 0; } /** * Enable verification of server certificate and hostname * * @param tc TLS Connection * @param host Server hostname * * @return 0 if success, otherwise errorcode */ int tls_set_verify_server(struct tls_conn *tc, const char *host) { #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) if (!tc || !host) return EINVAL; SSL_set_hostflags(tc->ssl, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); if (!SSL_set1_host(tc->ssl, host)) { ERR_clear_error(); return EPROTO; } SSL_set_verify(tc->ssl, SSL_VERIFY_PEER, NULL); return 0; #else (void)tc; (void)host; return ENOSYS; #endif } static int print_error(const char *str, size_t len, void *unused) { (void)unused; DEBUG_WARNING("%b", str, len); return 1; } void tls_flush_error(void) { ERR_print_errors_cb(print_error, NULL); } /** * Get the backend-specific (OpenSSL) context * * @param tls Generic TLS Context * * @return OpenSSL context */ struct ssl_ctx_st *tls_openssl_context(const struct tls *tls) { return tls ? tls->ctx : NULL; } /** * Convert a X509_NAME object into a human-readable form placed in an mbuf * * @param field X509_NAME of Cert field * @param mb Memorybuffer to store the readable format * @param flags X509_NAME_print_ex flags * * @return 0 if success, otherwise errorcode */ static int convert_X509_NAME_to_mbuf(X509_NAME *field, struct mbuf *mb, unsigned long flags) { BIO *outbio; char *p; long size; int err = ENOMEM; if (!field || !mb) return EINVAL; outbio = BIO_new(BIO_s_mem()); if (!outbio) return ENOMEM; if (X509_NAME_print_ex(outbio, field, 1, flags) <= 0) goto out; if (BIO_eof(outbio)) goto out; size = BIO_get_mem_data(outbio, &p); err = mbuf_write_mem(mb, (uint8_t *)p, size); if (err) goto out; err = 0; out: if (outbio) BIO_free(outbio); return err; } /** * Extract a X509 certficate issuer/subject and write the result into an mbuf * * @param tls TLS Object * @param mb Memory buffer * @param field_getter Functionpointer to the X509 getter functon * @param flags X509_NAME_print_ex flags * * @return 0 if success, othewise errorcode */ static int tls_get_ca_chain_field(struct tls *tls, struct mbuf *mb, tls_get_certfield_h *field_getter, unsigned long flags) { STACK_OF(X509) *certstack; X509 *cert; X509_NAME *field; int err = EINVAL; if (!field_getter) return EINVAL; if (!SSL_CTX_get0_chain_certs(tls->ctx, &certstack) || !certstack) goto out; for (int i = 0; i < sk_X509_num(certstack); i++) { cert = sk_X509_value(certstack, i); if (!cert) goto out; field = field_getter(cert); if (!field) goto out; err = convert_X509_NAME_to_mbuf(field, mb, flags); if (err) goto out; } err = 0; out: return err; } /** * Get the issuers fields of a certificate chain * * @param tls TLS Object * @param mb Memory Buffer * * @return 0 if success, otherwise errorcode */ int tls_get_issuer(struct tls *tls, struct mbuf *mb) { if (!tls || !tls->ctx || !mb) return EINVAL; return tls_get_ca_chain_field(tls, mb, &X509_get_issuer_name, XN_FLAG_RFC2253); } /** * Get the subject fields of a certificate chain * * @param tls TLS Object * @param mb Memory Buffer * * @return 0 if success, otherwise errorcode */ int tls_get_subject(struct tls *tls, struct mbuf *mb) { if (!tls || !tls->ctx || !mb) return EINVAL; return tls_get_ca_chain_field(tls, mb, &X509_get_subject_name, XN_FLAG_RFC2253); } re-1.1.0/src/tls/openssl/tls.h000066400000000000000000000014321373627245400161630ustar00rootroot00000000000000/** * @file openssl/tls.h TLS backend using OpenSSL (Internal API) * * Copyright (C) 2010 Creytiv.com */ /* also defined by wincrypt.h */ #ifdef WIN32 #undef X509_NAME #endif /* * Mapping of feature macros */ #if OPENSSL_VERSION_NUMBER >= 0x10100000L #define TLS_BIO_OPAQUE 1 #endif #if defined (LIBRESSL_VERSION_NUMBER) #undef TLS_BIO_OPAQUE #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L && \ !defined(LIBRESSL_VERSION_NUMBER) #define SSL_state SSL_get_state #define SSL_ST_OK TLS_ST_OK #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L typedef X509_NAME*(tls_get_certfield_h)(const X509 *); #else typedef X509_NAME*(tls_get_certfield_h)(X509 *); #endif struct tls { SSL_CTX *ctx; X509 *cert; char *pass; /* password for private key */ }; void tls_flush_error(void); re-1.1.0/src/tls/openssl/tls_tcp.c000066400000000000000000000151541373627245400170320ustar00rootroot00000000000000/** * @file openssl/tls_tcp.c TLS/TCP backend using OpenSSL * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include "tls.h" #define DEBUG_MODULE "tls" #define DEBUG_LEVEL 5 #include /* NOTE: shadow struct defined in tls_*.c */ struct tls_conn { SSL *ssl; #ifdef TLS_BIO_OPAQUE BIO_METHOD *biomet; #endif BIO *sbio_out; BIO *sbio_in; struct tcp_helper *th; struct tcp_conn *tcp; bool active; bool up; }; static void destructor(void *arg) { struct tls_conn *tc = arg; if (tc->ssl) { int r = SSL_shutdown(tc->ssl); if (r <= 0) ERR_clear_error(); SSL_free(tc->ssl); } #ifdef TLS_BIO_OPAQUE if (tc->biomet) BIO_meth_free(tc->biomet); #endif mem_deref(tc->th); mem_deref(tc->tcp); } static int bio_create(BIO *b) { #ifdef TLS_BIO_OPAQUE BIO_set_init(b, 1); BIO_set_data(b, NULL); BIO_set_flags(b, 0); #else b->init = 1; b->num = 0; b->ptr = NULL; b->flags = 0; #endif return 1; } static int bio_destroy(BIO *b) { if (!b) return 0; #ifdef TLS_BIO_OPAQUE BIO_set_init(b, 0); BIO_set_data(b, NULL); BIO_set_flags(b, 0); #else b->ptr = NULL; b->init = 0; b->flags = 0; #endif return 1; } static int bio_write(BIO *b, const char *buf, int len) { #ifdef TLS_BIO_OPAQUE struct tls_conn *tc = BIO_get_data(b); #else struct tls_conn *tc = b->ptr; #endif struct mbuf mb; int err; mb.buf = (void *)buf; mb.pos = 0; mb.end = mb.size = len; err = tcp_send_helper(tc->tcp, &mb, tc->th); if (err) return -1; return len; } static long bio_ctrl(BIO *b, int cmd, long num, void *ptr) { (void)b; (void)num; (void)ptr; if (cmd == BIO_CTRL_FLUSH) { /* The OpenSSL library needs this */ return 1; } return 0; } #ifdef TLS_BIO_OPAQUE static BIO_METHOD *bio_method_tcp(void) { BIO_METHOD *method; method = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "tcp_send"); if (!method) { DEBUG_WARNING("alloc: BIO_meth_new() failed\n"); ERR_clear_error(); return NULL; } BIO_meth_set_write(method, bio_write); BIO_meth_set_ctrl(method, bio_ctrl); BIO_meth_set_create(method, bio_create); BIO_meth_set_destroy(method, bio_destroy); return method; } #else static struct bio_method_st bio_tcp_send = { BIO_TYPE_SOURCE_SINK, "tcp_send", bio_write, 0, 0, 0, bio_ctrl, bio_create, bio_destroy, 0 }; #endif static int tls_connect(struct tls_conn *tc) { int err = 0, r; ERR_clear_error(); r = SSL_connect(tc->ssl); if (r <= 0) { const int ssl_err = SSL_get_error(tc->ssl, r); ERR_clear_error(); switch (ssl_err) { case SSL_ERROR_WANT_READ: break; default: DEBUG_WARNING("connect: error (r=%d, ssl_err=%d)\n", r, ssl_err); err = EPROTO; break; } } return err; } static int tls_accept(struct tls_conn *tc) { int err = 0, r; ERR_clear_error(); r = SSL_accept(tc->ssl); if (r <= 0) { const int ssl_err = SSL_get_error(tc->ssl, r); ERR_clear_error(); switch (ssl_err) { case SSL_ERROR_WANT_READ: break; default: DEBUG_WARNING("accept error: (r=%d, ssl_err=%d)\n", r, ssl_err); err = EPROTO; break; } } return err; } static bool estab_handler(int *err, bool active, void *arg) { struct tls_conn *tc = arg; DEBUG_INFO("tcp established (active=%u)\n", active); if (!active) return true; tc->active = true; *err = tls_connect(tc); return true; } static bool recv_handler(int *err, struct mbuf *mb, bool *estab, void *arg) { struct tls_conn *tc = arg; int r; /* feed SSL data to the BIO */ r = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb)); if (r <= 0) { DEBUG_WARNING("recv: BIO_write %d\n", r); ERR_clear_error(); *err = ENOMEM; return true; } if (SSL_state(tc->ssl) != SSL_ST_OK) { if (tc->up) { *err = EPROTO; return true; } if (tc->active) { *err = tls_connect(tc); } else { *err = tls_accept(tc); } DEBUG_INFO("state=0x%04x\n", SSL_state(tc->ssl)); /* TLS connection is established */ if (SSL_state(tc->ssl) != SSL_ST_OK) return true; *estab = true; tc->up = true; } mbuf_set_pos(mb, 0); for (;;) { int n; if (mbuf_get_space(mb) < 4096) { *err = mbuf_resize(mb, mb->size + 8192); if (*err) return true; } ERR_clear_error(); n = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb)); if (n <= 0) { const int ssl_err = SSL_get_error(tc->ssl, n); ERR_clear_error(); switch (ssl_err) { case SSL_ERROR_ZERO_RETURN: case SSL_ERROR_WANT_READ: break; default: *err = EPROTO; return true; } break; } mb->pos += n; } mbuf_set_end(mb, mb->pos); mbuf_set_pos(mb, 0); return false; } static bool send_handler(int *err, struct mbuf *mb, void *arg) { struct tls_conn *tc = arg; int r; ERR_clear_error(); r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb)); if (r <= 0) { DEBUG_WARNING("SSL_write: %d\n", SSL_get_error(tc->ssl, r)); ERR_clear_error(); *err = EPROTO; } return true; } /** * Start TLS on a TCP-connection * * @param ptc Pointer to allocated TLS connectioon * @param tls TLS Context * @param tcp TCP Connection * @param layer Protocol stack layer * * @return 0 if success, otherwise errorcode */ int tls_start_tcp(struct tls_conn **ptc, struct tls *tls, struct tcp_conn *tcp, int layer) { struct tls_conn *tc; int err; if (!ptc || !tls || !tcp) return EINVAL; tc = mem_zalloc(sizeof(*tc), destructor); if (!tc) return ENOMEM; err = tcp_register_helper(&tc->th, tcp, layer, estab_handler, send_handler, recv_handler, tc); if (err) goto out; tc->tcp = mem_ref(tcp); #ifdef TLS_BIO_OPAQUE tc->biomet = bio_method_tcp(); if (!tc->biomet) { err = ENOMEM; goto out; } #endif err = ENOMEM; /* Connect the SSL socket */ tc->ssl = SSL_new(tls->ctx); if (!tc->ssl) { DEBUG_WARNING("alloc: SSL_new() failed (ctx=%p)\n", tls->ctx); ERR_clear_error(); goto out; } tc->sbio_in = BIO_new(BIO_s_mem()); if (!tc->sbio_in) { DEBUG_WARNING("alloc: BIO_new() failed\n"); ERR_clear_error(); goto out; } #ifdef TLS_BIO_OPAQUE tc->sbio_out = BIO_new(tc->biomet); #else tc->sbio_out = BIO_new(&bio_tcp_send); #endif if (!tc->sbio_out) { DEBUG_WARNING("alloc: BIO_new_socket() failed\n"); ERR_clear_error(); BIO_free(tc->sbio_in); goto out; } #ifdef TLS_BIO_OPAQUE BIO_set_data(tc->sbio_out, tc); #else tc->sbio_out->ptr = tc; #endif SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out); err = 0; out: if (err) mem_deref(tc); else *ptc = tc; return err; } re-1.1.0/src/tls/openssl/tls_udp.c000066400000000000000000000344451373627245400170400ustar00rootroot00000000000000/** * @file openssl/tls_udp.c DTLS backend using OpenSSL * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tls.h" #define DEBUG_MODULE "dtls" #define DEBUG_LEVEL 5 #include enum { MTU_DEFAULT = 1400, MTU_FALLBACK = 548, }; struct dtls_sock { struct sa peer; struct udp_helper *uh; struct udp_sock *us; struct hash *ht; struct mbuf *mb; dtls_conn_h *connh; void *arg; size_t mtu; }; /* NOTE: shadow struct defined in tls_*.c */ struct tls_conn { SSL *ssl; /* inheritance */ #ifdef TLS_BIO_OPAQUE BIO_METHOD *biomet; #endif BIO *sbio_out; BIO *sbio_in; struct tmr tmr; struct sa peer; struct le he; struct dtls_sock *sock; dtls_estab_h *estabh; dtls_recv_h *recvh; dtls_close_h *closeh; void *arg; bool active; bool up; }; static int bio_create(BIO *b) { #ifdef TLS_BIO_OPAQUE BIO_set_init(b, 1); BIO_set_data(b, NULL); BIO_set_flags(b, 0); #else b->init = 1; b->num = 0; b->ptr = NULL; b->flags = 0; #endif return 1; } static int bio_destroy(BIO *b) { if (!b) return 0; #ifdef TLS_BIO_OPAQUE BIO_set_init(b, 0); BIO_set_data(b, NULL); BIO_set_flags(b, 0); #else b->ptr = NULL; b->init = 0; b->flags = 0; #endif return 1; } static int bio_write(BIO *b, const char *buf, int len) { #ifdef TLS_BIO_OPAQUE struct tls_conn *tc = BIO_get_data(b); #else struct tls_conn *tc = b->ptr; #endif struct mbuf *mb; enum {SPACE = 4}; int err; mb = mbuf_alloc(SPACE + len); if (!mb) return -1; mb->pos = SPACE; (void)mbuf_write_mem(mb, (void *)buf, len); mb->pos = SPACE; err = udp_send_helper(tc->sock->us, &tc->peer, mb, tc->sock->uh); mem_deref(mb); return err ? -1 : len; } static long bio_ctrl(BIO *b, int cmd, long num, void *ptr) { #ifdef TLS_BIO_OPAQUE struct tls_conn *tc = BIO_get_data(b); #else struct tls_conn *tc = b->ptr; #endif (void)num; (void)ptr; switch (cmd) { case BIO_CTRL_FLUSH: /* The OpenSSL library needs this */ return 1; #if defined (BIO_CTRL_DGRAM_QUERY_MTU) case BIO_CTRL_DGRAM_QUERY_MTU: return tc ? tc->sock->mtu : MTU_DEFAULT; #endif #if defined (BIO_CTRL_DGRAM_GET_FALLBACK_MTU) case BIO_CTRL_DGRAM_GET_FALLBACK_MTU: return MTU_FALLBACK; #endif } return 0; } #ifdef TLS_BIO_OPAQUE static BIO_METHOD *bio_method_udp(void) { BIO_METHOD *method; method = BIO_meth_new(BIO_TYPE_SOURCE_SINK, "udp_send"); if (!method) { DEBUG_WARNING("alloc: BIO_meth_new() failed\n"); ERR_clear_error(); return NULL; } BIO_meth_set_write(method, bio_write); BIO_meth_set_ctrl(method, bio_ctrl); BIO_meth_set_create(method, bio_create); BIO_meth_set_destroy(method, bio_destroy); return method; } #else static struct bio_method_st bio_udp_send = { BIO_TYPE_SOURCE_SINK, "udp_send", bio_write, 0, 0, 0, bio_ctrl, bio_create, bio_destroy, 0 }; #endif static void tls_close(struct tls_conn *tc) { int r; if (!tc->ssl) return; r = SSL_shutdown(tc->ssl); if (r <= 0) ERR_clear_error(); SSL_free(tc->ssl); tc->ssl = NULL; } static void conn_destructor(void *arg) { struct tls_conn *tc = arg; hash_unlink(&tc->he); tmr_cancel(&tc->tmr); tls_close(tc); #ifdef TLS_BIO_OPAQUE if (tc->biomet) BIO_meth_free(tc->biomet); #endif mem_deref(tc->sock); } static void conn_close(struct tls_conn *tc, int err) { tmr_cancel(&tc->tmr); tls_close(tc); tc->up = false; if (tc->closeh) tc->closeh(err, tc->arg); } #if defined (DTLS_CTRL_HANDLE_TIMEOUT) && defined(DTLS_CTRL_GET_TIMEOUT) static void check_timer(struct tls_conn *tc); static void timeout(void *arg) { struct tls_conn *tc = arg; DEBUG_INFO("timeout\n"); if (0 <= DTLSv1_handle_timeout(tc->ssl)) { check_timer(tc); } else { ERR_clear_error(); conn_close(tc, ETIMEDOUT); } } static void check_timer(struct tls_conn *tc) { struct timeval tv = {0, 0}; if (1 == DTLSv1_get_timeout(tc->ssl, &tv)) { tmr_start(&tc->tmr, tv.tv_sec * 1000 + tv.tv_usec / 1000, timeout, tc); } else { tmr_cancel(&tc->tmr); } } #else static void check_timer(struct tls_conn *tc) { (void)tc; } #endif static int tls_connect(struct tls_conn *tc) { int r; ERR_clear_error(); r = SSL_connect(tc->ssl); if (r <= 0) { const int ssl_err = SSL_get_error(tc->ssl, r); tls_flush_error(); switch (ssl_err) { case SSL_ERROR_WANT_READ: break; default: DEBUG_WARNING("connect error: %i\n", ssl_err); return EPROTO; } } check_timer(tc); return 0; } static int tls_accept(struct tls_conn *tc) { int r; ERR_clear_error(); r = SSL_accept(tc->ssl); if (r <= 0) { const int ssl_err = SSL_get_error(tc->ssl, r); tls_flush_error(); switch (ssl_err) { case SSL_ERROR_WANT_READ: break; default: DEBUG_WARNING("accept error: %i\n", ssl_err); return EPROTO; } } check_timer(tc); return 0; } static void conn_recv(struct tls_conn *tc, struct mbuf *mb) { int err, r; if (!tc->ssl) return; /* feed SSL data to the BIO */ r = BIO_write(tc->sbio_in, mbuf_buf(mb), (int)mbuf_get_left(mb)); if (r <= 0) { DEBUG_WARNING("receive bio write error: %i\n", r); ERR_clear_error(); conn_close(tc, ENOMEM); return; } if (SSL_state(tc->ssl) != SSL_ST_OK) { if (tc->up) { conn_close(tc, EPROTO); return; } if (tc->active) { err = tls_connect(tc); } else { err = tls_accept(tc); } if (err) { conn_close(tc, err); return; } DEBUG_INFO("%s: state=0x%04x\n", tc->active ? "client" : "server", SSL_state(tc->ssl)); /* TLS connection is established */ if (SSL_state(tc->ssl) != SSL_ST_OK) return; tc->up = true; if (tc->estabh) { uint32_t nrefs; mem_ref(tc); tc->estabh(tc->arg); nrefs = mem_nrefs(tc); mem_deref(tc); /* check if connection was deref'd from handler */ if (nrefs == 1) return; } } mbuf_set_pos(mb, 0); for (;;) { int n; if (mbuf_get_space(mb) < 4096) { err = mbuf_resize(mb, mb->size + 8192); if (err) { conn_close(tc, err); return; } } ERR_clear_error(); n = SSL_read(tc->ssl, mbuf_buf(mb), (int)mbuf_get_space(mb)); if (n <= 0) { const int ssl_err = SSL_get_error(tc->ssl, n); ERR_clear_error(); switch (ssl_err) { case SSL_ERROR_WANT_READ: break; case SSL_ERROR_ZERO_RETURN: conn_close(tc, ECONNRESET); return; default: DEBUG_WARNING("read error: %i\n", ssl_err); conn_close(tc, EPROTO); return; } break; } mb->pos += n; } mbuf_set_end(mb, mb->pos); mbuf_set_pos(mb, 0); if (tc->recvh && mbuf_get_left(mb) > 0) tc->recvh(mb, tc->arg); return; } static int conn_alloc(struct tls_conn **ptc, struct tls *tls, struct dtls_sock *sock, const struct sa *peer, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg) { struct tls_conn *tc; int err = 0; tc = mem_zalloc(sizeof(*tc), conn_destructor); if (!tc) return ENOMEM; hash_append(sock->ht, sa_hash(peer, SA_ALL), &tc->he, tc); tc->sock = mem_ref(sock); tc->peer = *peer; tc->estabh = estabh; tc->recvh = recvh; tc->closeh = closeh; tc->arg = arg; #ifdef TLS_BIO_OPAQUE tc->biomet = bio_method_udp(); if (!tc->biomet) { err = ENOMEM; goto out; } #endif /* Connect the SSL socket */ tc->ssl = SSL_new(tls->ctx); if (!tc->ssl) { DEBUG_WARNING("ssl new failed: %i\n", ERR_GET_REASON(ERR_get_error())); ERR_clear_error(); err = ENOMEM; goto out; } tc->sbio_in = BIO_new(BIO_s_mem()); if (!tc->sbio_in) { ERR_clear_error(); err = ENOMEM; goto out; } #ifdef TLS_BIO_OPAQUE tc->sbio_out = BIO_new(tc->biomet); #else tc->sbio_out = BIO_new(&bio_udp_send); #endif if (!tc->sbio_out) { ERR_clear_error(); BIO_free(tc->sbio_in); err = ENOMEM; goto out; } #ifdef TLS_BIO_OPAQUE BIO_set_data(tc->sbio_out, tc); #else tc->sbio_out->ptr = tc; #endif SSL_set_bio(tc->ssl, tc->sbio_in, tc->sbio_out); SSL_set_read_ahead(tc->ssl, 1); out: if (err) mem_deref(tc); else *ptc = tc; return err; } /** * DTLS Connect * * @param ptc Pointer to allocated DTLS connection * @param tls TLS Context * @param sock DTLS Socket * @param peer Peer address * @param estabh Establish handler * @param recvh Receive handler * @param closeh Close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int dtls_connect(struct tls_conn **ptc, struct tls *tls, struct dtls_sock *sock, const struct sa *peer, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg) { struct tls_conn *tc; int err; if (!ptc || !tls || !sock || !peer) return EINVAL; err = conn_alloc(&tc, tls, sock, peer, estabh, recvh, closeh, arg); if (err) return err; tc->active = true; err = tls_connect(tc); if (err) goto out; out: if (err) mem_deref(tc); else *ptc = tc; return err; } /** * DTLS Accept * * @param ptc Pointer to allocated DTLS connection * @param tls TLS Context * @param sock DTLS Socket * @param estabh Establish handler * @param recvh Receive handler * @param closeh Close handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int dtls_accept(struct tls_conn **ptc, struct tls *tls, struct dtls_sock *sock, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg) { struct tls_conn *tc; int err, r; if (!ptc || !tls || !sock || !sock->mb) return EINVAL; err = conn_alloc(&tc, tls, sock, &sock->peer, estabh, recvh, closeh, arg); if (err) return err; tc->active = false; r = BIO_write(tc->sbio_in, mbuf_buf(sock->mb), (int)mbuf_get_left(sock->mb)); if (r <= 0) { DEBUG_WARNING("accept bio write error: %i\n", r); ERR_clear_error(); err = ENOMEM; goto out; } err = tls_accept(tc); if (err) goto out; sock->mb = mem_deref(sock->mb); out: if (err) mem_deref(tc); else *ptc = tc; return err; } /** * Send data on a DTLS connection * * @param tc DTLS connection * @param mb Buffer to send * * @return 0 if success, otherwise errorcode */ int dtls_send(struct tls_conn *tc, struct mbuf *mb) { int r; if (!tc || !mb) return EINVAL; if (!tc->up || !tc->ssl) return ENOTCONN; ERR_clear_error(); r = SSL_write(tc->ssl, mbuf_buf(mb), (int)mbuf_get_left(mb)); if (r <= 0) { DEBUG_WARNING("write error: %i\n", SSL_get_error(tc->ssl, r)); ERR_clear_error(); return EPROTO; } return 0; } /** * Set handlers on a DTLS Connection * * @param tc DTLS Connection * @param estabh DTLS Connection Established handler * @param recvh DTLS Connection Receive data handler * @param closeh DTLS Connection Close handler * @param arg Handler argument */ void dtls_set_handlers(struct tls_conn *tc, dtls_estab_h *estabh, dtls_recv_h *recvh, dtls_close_h *closeh, void *arg) { if (!tc) return; tc->estabh = estabh; tc->recvh = recvh; tc->closeh = closeh; tc->arg = arg; } /** * Get the remote peer of a DTLS Connection * * @param tc DTLS Connection * * @return Remote peer */ const struct sa *dtls_peer(const struct tls_conn *tc) { return tc ? &tc->peer : NULL; } /** * Set the remote peer of a DTLS Connection * * @param tc DTLS Connection * @param peer Peer address */ void dtls_set_peer(struct tls_conn *tc, const struct sa *peer) { if (!tc || !peer) return; hash_unlink(&tc->he); hash_append(tc->sock->ht, sa_hash(peer, SA_ALL), &tc->he, tc); tc->peer = *peer; } static void sock_destructor(void *arg) { struct dtls_sock *sock = arg; hash_clear(sock->ht); mem_deref(sock->uh); mem_deref(sock->us); mem_deref(sock->ht); mem_deref(sock->mb); } static bool cmp_handler(struct le *le, void *arg) { struct tls_conn *tc = le->data; return sa_cmp(&tc->peer, arg, SA_ALL); } static struct tls_conn *conn_lookup(struct dtls_sock *sock, const struct sa *peer) { return list_ledata(hash_lookup(sock->ht, sa_hash(peer, SA_ALL), cmp_handler, (void *)peer)); } static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct dtls_sock *sock = arg; struct tls_conn *tc; uint8_t b; if (mbuf_get_left(mb) < 1) return false; b = mb->buf[mb->pos]; if (b < 20 || b > 63) return false; tc = conn_lookup(sock, src); if (tc) { conn_recv(tc, mb); return true; } if (sock->connh) { mem_deref(sock->mb); sock->mb = mem_ref(mb); sock->peer = *src; sock->connh(src, sock->arg); } return true; } /** * Create DTLS Socket * * @param sockp Pointer to returned DTLS Socket * @param laddr Local listen address (optional) * @param us External UDP socket (optional) * @param htsize Connection hash table size * @param layer UDP protocol layer * @param connh Connect handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int dtls_listen(struct dtls_sock **sockp, const struct sa *laddr, struct udp_sock *us, uint32_t htsize, int layer, dtls_conn_h *connh, void *arg) { struct dtls_sock *sock; int err; if (!sockp) return EINVAL; sock = mem_zalloc(sizeof(*sock), sock_destructor); if (!sock) return ENOMEM; if (us) { sock->us = mem_ref(us); } else { err = udp_listen(&sock->us, laddr, NULL, NULL); if (err) goto out; } err = udp_register_helper(&sock->uh, sock->us, layer, NULL, recv_handler, sock); if (err) goto out; err = hash_alloc(&sock->ht, hash_valid_size(htsize)); if (err) goto out; sock->mtu = MTU_DEFAULT; sock->connh = connh; sock->arg = arg; out: if (err) mem_deref(sock); else *sockp = sock; return err; } /** * Get the underlying UDP socket of a DTLS Socket * * @param sock DTLS Socket * * @return UDP Socket */ struct udp_sock *dtls_udp_sock(struct dtls_sock *sock) { return sock ? sock->us : NULL; } /** * Set MTU on a DTLS Socket * * @param sock DTLS Socket * @param mtu MTU value */ void dtls_set_mtu(struct dtls_sock *sock, size_t mtu) { if (!sock) return; sock->mtu = mtu; } void dtls_recv_packet(struct dtls_sock *sock, const struct sa *src, struct mbuf *mb) { struct sa addr; if (!sock || !src || !mb) return; addr = *src; recv_handler(&addr, mb, sock); } re-1.1.0/src/tmr/000077500000000000000000000000001373627245400135255ustar00rootroot00000000000000re-1.1.0/src/tmr/mod.mk000066400000000000000000000001031373627245400146270ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += tmr/tmr.c re-1.1.0/src/tmr/tmr.c000066400000000000000000000114061373627245400144750ustar00rootroot00000000000000/** * @file tmr.c Timer implementation * * Copyright (C) 2010 Creytiv.com */ #include #ifdef HAVE_SYS_TIME_H #include #endif #ifdef WIN32 #include #else #include #endif #ifdef HAVE_PTHREAD #include #include #endif #include #include #include #include #include #define DEBUG_MODULE "tmr" #define DEBUG_LEVEL 5 #include #if !defined (RELEASE) && !defined (TMR_DEBUG) #define TMR_DEBUG 1 /**< Timer debugging (0 or 1) */ #endif /** Timer values */ enum { MAX_BLOCKING = 100 /**< Maximum time spent in handler [ms] */ }; extern struct list *tmrl_get(void); static bool inspos_handler(struct le *le, void *arg) { struct tmr *tmr = le->data; const uint64_t now = *(uint64_t *)arg; return tmr->jfs <= now; } static bool inspos_handler_0(struct le *le, void *arg) { struct tmr *tmr = le->data; const uint64_t now = *(uint64_t *)arg; return tmr->jfs > now; } #if TMR_DEBUG static void call_handler(tmr_h *th, void *arg) { const uint64_t tick = tmr_jiffies(); uint32_t diff; /* Call handler */ th(arg); diff = (uint32_t)(tmr_jiffies() - tick); if (diff > MAX_BLOCKING) { DEBUG_WARNING("long async blocking: %u>%u ms (h=%p arg=%p)\n", diff, MAX_BLOCKING, th, arg); } } #endif /** * Poll all timers in the current thread * * @param tmrl Timer list */ void tmr_poll(struct list *tmrl) { const uint64_t jfs = tmr_jiffies(); for (;;) { struct tmr *tmr; tmr_h *th; void *th_arg; tmr = list_ledata(tmrl->head); if (!tmr || (tmr->jfs > jfs)) { break; } th = tmr->th; th_arg = tmr->arg; tmr->th = NULL; list_unlink(&tmr->le); if (!th) continue; #if TMR_DEBUG call_handler(th, th_arg); #else th(th_arg); #endif } } /** * Get the timer jiffies in milliseconds * * @return Jiffies in [ms] */ uint64_t tmr_jiffies(void) { uint64_t jfs; #if defined(WIN32) FILETIME ft; ULARGE_INTEGER li; GetSystemTimeAsFileTime(&ft); li.LowPart = ft.dwLowDateTime; li.HighPart = ft.dwHighDateTime; jfs = li.QuadPart/10/1000; #else struct timeval now; if (0 != gettimeofday(&now, NULL)) { DEBUG_WARNING("jiffies: gettimeofday() failed (%m)\n", errno); return 0; } jfs = (long)now.tv_sec * (uint64_t)1000; jfs += now.tv_usec / 1000; #endif return jfs; } /** * Get number of milliseconds until the next timer expires * * @param tmrl Timer-list * * @return Number of [ms], or 0 if no active timers */ uint64_t tmr_next_timeout(struct list *tmrl) { const uint64_t jif = tmr_jiffies(); const struct tmr *tmr; tmr = list_ledata(tmrl->head); if (!tmr) return 0; if (tmr->jfs <= jif) return 1; else return tmr->jfs - jif; } int tmr_status(struct re_printf *pf, void *unused) { struct list *tmrl = tmrl_get(); struct le *le; uint32_t n; int err; (void)unused; n = list_count(tmrl); if (!n) return 0; err = re_hprintf(pf, "Timers (%u):\n", n); for (le = tmrl->head; le; le = le->next) { const struct tmr *tmr = le->data; err |= re_hprintf(pf, " %p: th=%p expire=%llums\n", tmr, tmr->th, (unsigned long long)tmr_get_expire(tmr)); } if (n > 100) err |= re_hprintf(pf, " (Dumped Timers: %u)\n", n); return err; } /** * Print timer debug info to stderr */ void tmr_debug(void) { if (!list_isempty(tmrl_get())) (void)re_fprintf(stderr, "%H", tmr_status, NULL); } /** * Initialise a timer object * * @param tmr Timer to initialise */ void tmr_init(struct tmr *tmr) { if (!tmr) return; memset(tmr, 0, sizeof(*tmr)); } /** * Start a timer * * @param tmr Timer to start * @param delay Timer delay in [ms] * @param th Timeout handler * @param arg Handler argument */ void tmr_start(struct tmr *tmr, uint64_t delay, tmr_h *th, void *arg) { struct list *tmrl = tmrl_get(); struct le *le; if (!tmr) return; if (tmr->th) { list_unlink(&tmr->le); } tmr->th = th; tmr->arg = arg; if (!th) return; tmr->jfs = delay + tmr_jiffies(); if (delay == 0) { le = list_apply(tmrl, true, inspos_handler_0, &tmr->jfs); if (le) { list_insert_before(tmrl, le, &tmr->le, tmr); } else { list_append(tmrl, &tmr->le, tmr); } } else { le = list_apply(tmrl, false, inspos_handler, &tmr->jfs); if (le) { list_insert_after(tmrl, le, &tmr->le, tmr); } else { list_prepend(tmrl, &tmr->le, tmr); } } } /** * Cancel an active timer * * @param tmr Timer to cancel */ void tmr_cancel(struct tmr *tmr) { tmr_start(tmr, 0, NULL, NULL); } /** * Get the time left until timer expires * * @param tmr Timer object * * @return Time in [ms] until expiration */ uint64_t tmr_get_expire(const struct tmr *tmr) { uint64_t jfs; if (!tmr || !tmr->th) return 0; jfs = tmr_jiffies(); return (tmr->jfs > jfs) ? (tmr->jfs - jfs) : 0; } re-1.1.0/src/turn/000077500000000000000000000000001373627245400137135ustar00rootroot00000000000000re-1.1.0/src/turn/chan.c000066400000000000000000000125061373627245400147740ustar00rootroot00000000000000/** * @file chan.c TURN Channels handling * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "turnc.h" enum { CHAN_LIFETIME = 600, CHAN_REFRESH = 250, CHAN_NUMB_MIN = 0x4000, CHAN_NUMB_MAX = 0x7fff }; struct channels { struct hash *ht_numb; struct hash *ht_peer; uint16_t nr; }; struct chan { struct le he_numb; struct le he_peer; struct loop_state ls; uint16_t nr; struct sa peer; struct tmr tmr; struct turnc *turnc; struct stun_ctrans *ct; turnc_chan_h *ch; void *arg; }; static int chanbind_request(struct chan *chan, bool reset_ls); static void channels_destructor(void *data) { struct channels *c = data; /* flush from primary hash */ hash_flush(c->ht_numb); mem_deref(c->ht_numb); mem_deref(c->ht_peer); } static void chan_destructor(void *data) { struct chan *chan = data; tmr_cancel(&chan->tmr); mem_deref(chan->ct); hash_unlink(&chan->he_numb); hash_unlink(&chan->he_peer); } static bool numb_hash_cmp_handler(struct le *le, void *arg) { const struct chan *chan = le->data; const uint16_t *nr = arg; return chan->nr == *nr; } static bool peer_hash_cmp_handler(struct le *le, void *arg) { const struct chan *chan = le->data; return sa_cmp(&chan->peer, arg, SA_ALL); } static void timeout(void *arg) { struct chan *chan = arg; int err; err = chanbind_request(chan, true); if (err) chan->turnc->th(err, 0, NULL, NULL, NULL, NULL, chan->turnc->arg); } static void chanbind_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct chan *chan = arg; if (err || turnc_request_loops(&chan->ls, scode)) goto out; switch (scode) { case 0: tmr_start(&chan->tmr, CHAN_REFRESH * 1000, timeout, chan); if (chan->ch) { chan->ch(chan->arg); chan->ch = NULL; chan->arg = NULL; } return; case 401: case 438: err = turnc_keygen(chan->turnc, msg); if (err) break; err = chanbind_request(chan, false); if (err) break; return; default: break; } out: chan->turnc->th(err, scode, reason, NULL, NULL, msg, chan->turnc->arg); } static int chanbind_request(struct chan *chan, bool reset_ls) { struct turnc *t = chan->turnc; if (reset_ls) turnc_loopstate_reset(&chan->ls); return stun_request(&chan->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_CHANBIND, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, chanbind_resp_handler, chan, 6, STUN_ATTR_CHANNEL_NUMBER, &chan->nr, STUN_ATTR_XOR_PEER_ADDR, &chan->peer, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); } /** * Add a TURN Channel for a peer * * @param turnc TURN Client * @param peer Peer IP-address * @param ch Channel handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int turnc_add_chan(struct turnc *turnc, const struct sa *peer, turnc_chan_h *ch, void *arg) { struct chan *chan; int err; if (!turnc || !peer) return EINVAL; if (turnc->chans->nr >= CHAN_NUMB_MAX) return ERANGE; if (turnc_chan_find_peer(turnc, peer)) return 0; chan = mem_zalloc(sizeof(*chan), chan_destructor); if (!chan) return ENOMEM; chan->nr = turnc->chans->nr++; chan->peer = *peer; hash_append(turnc->chans->ht_numb, chan->nr, &chan->he_numb, chan); hash_append(turnc->chans->ht_peer, sa_hash(peer, SA_ALL), &chan->he_peer, chan); tmr_init(&chan->tmr); chan->turnc = turnc; chan->ch = ch; chan->arg = arg; err = chanbind_request(chan, true); if (err) mem_deref(chan); return err; } int turnc_chan_hash_alloc(struct channels **cp, uint32_t bsize) { struct channels *c; int err; if (!cp) return EINVAL; c = mem_zalloc(sizeof(*c), channels_destructor); if (!c) return ENOMEM; err = hash_alloc(&c->ht_numb, bsize); if (err) goto out; err = hash_alloc(&c->ht_peer, bsize); if (err) goto out; c->nr = CHAN_NUMB_MIN; out: if (err) mem_deref(c); else *cp = c; return err; } struct chan *turnc_chan_find_numb(const struct turnc *turnc, uint16_t nr) { if (!turnc) return NULL; return list_ledata(hash_lookup(turnc->chans->ht_numb, nr, numb_hash_cmp_handler, &nr)); } struct chan *turnc_chan_find_peer(const struct turnc *turnc, const struct sa *peer) { if (!turnc) return NULL; return list_ledata(hash_lookup(turnc->chans->ht_peer, sa_hash(peer, SA_ALL), peer_hash_cmp_handler, (void *)peer)); } uint16_t turnc_chan_numb(const struct chan *chan) { return chan ? chan->nr : 0; } const struct sa *turnc_chan_peer(const struct chan *chan) { return chan ? &chan->peer : NULL; } int turnc_chan_hdr_encode(const struct chan_hdr *hdr, struct mbuf *mb) { int err; if (!hdr || !mb) return EINVAL; err = mbuf_write_u16(mb, htons(hdr->nr)); err |= mbuf_write_u16(mb, htons(hdr->len)); return err; } int turnc_chan_hdr_decode(struct chan_hdr *hdr, struct mbuf *mb) { if (!hdr || !mb) return EINVAL; if (mbuf_get_left(mb) < sizeof(*hdr)) return ENOENT; hdr->nr = ntohs(mbuf_read_u16(mb)); hdr->len = ntohs(mbuf_read_u16(mb)); return 0; } re-1.1.0/src/turn/mod.mk000066400000000000000000000001561373627245400150250ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += turn/chan.c SRCS += turn/perm.c SRCS += turn/turnc.c re-1.1.0/src/turn/perm.c000066400000000000000000000065401373627245400150270ustar00rootroot00000000000000/** * @file perm.c TURN permission handling * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include "turnc.h" enum { PERM_LIFETIME = 300, PERM_REFRESH = 250, }; struct perm { struct le he; struct loop_state ls; struct sa peer; struct tmr tmr; struct turnc *turnc; struct stun_ctrans *ct; turnc_perm_h *ph; void *arg; }; static int createperm_request(struct perm *perm, bool reset_ls); static void destructor(void *arg) { struct perm *perm = arg; tmr_cancel(&perm->tmr); mem_deref(perm->ct); hash_unlink(&perm->he); } static bool hash_cmp_handler(struct le *le, void *arg) { const struct perm *perm = le->data; return sa_cmp(&perm->peer, arg, SA_ADDR); } static struct perm *perm_find(const struct turnc *turnc, const struct sa *peer) { return list_ledata(hash_lookup(turnc->perms, sa_hash(peer, SA_ADDR), hash_cmp_handler, (void *)peer)); } static void timeout(void *arg) { struct perm *perm = arg; int err; err = createperm_request(perm, true); if (err) perm->turnc->th(err, 0, NULL, NULL, NULL, NULL, perm->turnc->arg); } static void createperm_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct perm *perm = arg; if (err || turnc_request_loops(&perm->ls, scode)) goto out; switch (scode) { case 0: tmr_start(&perm->tmr, PERM_REFRESH * 1000, timeout, perm); if (perm->ph) { perm->ph(perm->arg); perm->ph = NULL; perm->arg = NULL; } return; case 401: case 438: err = turnc_keygen(perm->turnc, msg); if (err) break; err = createperm_request(perm, false); if (err) break; return; default: break; } out: perm->turnc->th(err, scode, reason, NULL, NULL, msg, perm->turnc->arg); } static int createperm_request(struct perm *perm, bool reset_ls) { struct turnc *t = perm->turnc; if (reset_ls) turnc_loopstate_reset(&perm->ls); return stun_request(&perm->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_CREATEPERM, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, createperm_resp_handler, perm, 5, STUN_ATTR_XOR_PEER_ADDR, &perm->peer, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); } /** * Add TURN Permission for a peer * * @param turnc TURN Client * @param peer Peer IP-address * @param ph Permission handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int turnc_add_perm(struct turnc *turnc, const struct sa *peer, turnc_perm_h *ph, void *arg) { struct perm *perm; int err; if (!turnc || !peer) return EINVAL; if (perm_find(turnc, peer)) return 0; perm = mem_zalloc(sizeof(*perm), destructor); if (!perm) return ENOMEM; hash_append(turnc->perms, sa_hash(peer, SA_ADDR), &perm->he, perm); tmr_init(&perm->tmr); perm->peer = *peer; perm->turnc = turnc; perm->ph = ph; perm->arg = arg; err = createperm_request(perm, true); if (err) mem_deref(perm); return err; } int turnc_perm_hash_alloc(struct hash **ht, uint32_t bsize) { return hash_alloc(ht, bsize); } re-1.1.0/src/turn/turnc.c000066400000000000000000000311211373627245400152100ustar00rootroot00000000000000/** * @file turnc.c TURN Client implementation * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "turnc.h" #define DEBUG_MODULE "turnc" #define DEBUG_LEVEL 5 #include /** TURN Client protocol values */ enum { PERM_HASH_SIZE = 16, CHAN_HASH_SIZE = 16, FAILC_MAX = 16, /**< Maximum number of request errors for loopcheck. */ STUN_ATTR_ADDR4_SIZE = 8, STUN_ATTR_ADDR6_SIZE = 20, }; static const uint8_t sendind_tid[STUN_TID_SIZE]; static int allocate_request(struct turnc *t); static int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls, stun_resp_h *resph, void *arg); static void refresh_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg); static void destructor(void *arg) { struct turnc *turnc = arg; if (turnc->allocated) (void)refresh_request(turnc, 0, true, NULL, NULL); tmr_cancel(&turnc->tmr); mem_deref(turnc->ct); hash_flush(turnc->perms); mem_deref(turnc->perms); mem_deref(turnc->chans); mem_deref(turnc->username); mem_deref(turnc->password); mem_deref(turnc->nonce); mem_deref(turnc->realm); mem_deref(turnc->stun); mem_deref(turnc->uh); mem_deref(turnc->sock); } static void timeout(void *arg) { struct turnc *turnc = arg; int err; err = refresh_request(turnc, turnc->lifetime, true, refresh_resp_handler, turnc); if (err) turnc->th(err, 0, NULL, NULL, NULL, NULL, turnc->arg); } static void refresh_timer(struct turnc *turnc) { const uint32_t t = turnc->lifetime*1000*3/4; DEBUG_INFO("Start refresh timer.. %u seconds\n", t/1000); tmr_start(&turnc->tmr, t, timeout, turnc); } static void allocate_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct stun_attr *map = NULL, *rel = NULL, *ltm, *alt; struct turnc *turnc = arg; if (err || turnc_request_loops(&turnc->ls, scode)) goto out; switch (scode) { case 0: map = stun_msg_attr(msg, STUN_ATTR_XOR_MAPPED_ADDR); rel = stun_msg_attr(msg, STUN_ATTR_XOR_RELAY_ADDR); ltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME); if (!rel || !map) { DEBUG_WARNING("xor_mapped/relay addr attr missing\n"); err = EPROTO; break; } if (ltm) turnc->lifetime = ltm->v.lifetime; turnc->allocated = true; refresh_timer(turnc); break; case 300: if (turnc->proto == IPPROTO_TCP || turnc->proto == STUN_TRANSP_DTLS) break; alt = stun_msg_attr(msg, STUN_ATTR_ALT_SERVER); if (!alt) break; turnc->psrv = turnc->srv; turnc->srv = alt->v.alt_server; err = allocate_request(turnc); if (err) break; return; case 401: case 438: err = turnc_keygen(turnc, msg); if (err) break; err = allocate_request(turnc); if (err) break; return; default: break; } out: turnc->th(err, scode, reason, rel ? &rel->v.xor_relay_addr : NULL, map ? &map->v.xor_mapped_addr : NULL, msg, turnc->arg); } static int allocate_request(struct turnc *t) { const uint8_t proto = IPPROTO_UDP; return stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_ALLOCATE, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, allocate_resp_handler, t, 6, STUN_ATTR_LIFETIME, &t->lifetime, STUN_ATTR_REQ_TRANSPORT, &proto, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); } static void refresh_resp_handler(int err, uint16_t scode, const char *reason, const struct stun_msg *msg, void *arg) { struct turnc *turnc = arg; struct stun_attr *ltm; if (err || turnc_request_loops(&turnc->ls, scode)) goto out; switch (scode) { case 0: ltm = stun_msg_attr(msg, STUN_ATTR_LIFETIME); if (ltm) turnc->lifetime = ltm->v.lifetime; refresh_timer(turnc); return; case 401: case 438: err = turnc_keygen(turnc, msg); if (err) break; err = refresh_request(turnc, turnc->lifetime, false, refresh_resp_handler, turnc); if (err) break; return; default: break; } out: turnc->th(err, scode, reason, NULL, NULL, msg, turnc->arg); } static int refresh_request(struct turnc *t, uint32_t lifetime, bool reset_ls, stun_resp_h *resph, void *arg) { if (!t) return EINVAL; if (reset_ls) turnc_loopstate_reset(&t->ls); if (t->ct) t->ct = mem_deref(t->ct); return stun_request(&t->ct, t->stun, t->proto, t->sock, &t->srv, 0, STUN_METHOD_REFRESH, t->realm ? t->md5_hash : NULL, sizeof(t->md5_hash), false, resph, arg, 5, STUN_ATTR_LIFETIME, &lifetime, STUN_ATTR_USERNAME, t->realm ? t->username : NULL, STUN_ATTR_REALM, t->realm, STUN_ATTR_NONCE, t->nonce, STUN_ATTR_SOFTWARE, stun_software); } static inline size_t stun_indlen(const struct sa *sa) { size_t len = STUN_HEADER_SIZE + STUN_ATTR_HEADER_SIZE * 2; switch (sa_af(sa)) { case AF_INET: len += STUN_ATTR_ADDR4_SIZE; break; #ifdef HAVE_INET6 case AF_INET6: len += STUN_ATTR_ADDR6_SIZE; break; #endif } return len; } static bool udp_send_handler(int *err, struct sa *dst, struct mbuf *mb, void *arg) { struct turnc *turnc = arg; size_t pos, indlen; struct chan *chan; if (mb->pos < CHAN_HDR_SIZE) return false; chan = turnc_chan_find_peer(turnc, dst); if (chan) { struct chan_hdr hdr; hdr.nr = turnc_chan_numb(chan); hdr.len = mbuf_get_left(mb); mb->pos -= CHAN_HDR_SIZE; *err = turnc_chan_hdr_encode(&hdr, mb); mb->pos -= CHAN_HDR_SIZE; *dst = turnc->srv; return false; } indlen = stun_indlen(dst); if (mb->pos < indlen) return false; mb->pos -= indlen; pos = mb->pos; *err = stun_msg_encode(mb, STUN_METHOD_SEND, STUN_CLASS_INDICATION, sendind_tid, NULL, NULL, 0, false, 0x00, 2, STUN_ATTR_XOR_PEER_ADDR, dst, STUN_ATTR_DATA, mb); mb->pos = pos; *dst = turnc->srv; return false; } static bool udp_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct stun_attr *peer, *data; struct stun_unknown_attr ua; struct turnc *turnc = arg; struct stun_msg *msg; bool hdld = true; if (!sa_cmp(&turnc->srv, src, SA_ALL) && !sa_cmp(&turnc->psrv, src, SA_ALL)) return false; if (stun_msg_decode(&msg, mb, &ua)) { struct chan_hdr hdr; struct chan *chan; if (turnc_chan_hdr_decode(&hdr, mb)) return true; if (mbuf_get_left(mb) < hdr.len) return true; chan = turnc_chan_find_numb(turnc, hdr.nr); if (!chan) return true; *src = *turnc_chan_peer(chan); return false; } switch (stun_msg_class(msg)) { case STUN_CLASS_INDICATION: if (ua.typec > 0) break; if (stun_msg_method(msg) != STUN_METHOD_DATA) break; peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) break; *src = peer->v.xor_peer_addr; mb->pos = data->v.data.pos; mb->end = data->v.data.end; hdld = false; break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(turnc->stun, msg, &ua); break; default: break; } mem_deref(msg); return hdld; } /** * Allocate a TURN Client * * @param turncp Pointer to allocated TURN Client * @param conf Optional STUN Configuration * @param proto Transport Protocol * @param sock Transport socket * @param layer Transport layer * @param srv TURN Server IP-address * @param username Authentication username * @param password Authentication password * @param lifetime Allocate lifetime in [seconds] * @param th TURN handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int turnc_alloc(struct turnc **turncp, const struct stun_conf *conf, int proto, void *sock, int layer, const struct sa *srv, const char *username, const char *password, uint32_t lifetime, turnc_h *th, void *arg) { struct turnc *turnc; int err; if (!turncp || !sock || !srv || !username || !password || !th) return EINVAL; turnc = mem_zalloc(sizeof(*turnc), destructor); if (!turnc) return ENOMEM; err = stun_alloc(&turnc->stun, conf, NULL, NULL); if (err) goto out; err = str_dup(&turnc->username, username); if (err) goto out; err = str_dup(&turnc->password, password); if (err) goto out; err = turnc_perm_hash_alloc(&turnc->perms, PERM_HASH_SIZE); if (err) goto out; err = turnc_chan_hash_alloc(&turnc->chans, CHAN_HASH_SIZE); if (err) goto out; tmr_init(&turnc->tmr); turnc->proto = proto; turnc->sock = mem_ref(sock); turnc->psrv = *srv; turnc->srv = *srv; turnc->lifetime = lifetime; turnc->th = th; turnc->arg = arg; switch (proto) { case IPPROTO_UDP: err = udp_register_helper(&turnc->uh, sock, layer, udp_send_handler, udp_recv_handler, turnc); break; default: err = 0; break; } if (err) goto out; err = allocate_request(turnc); if (err) goto out; out: if (err) mem_deref(turnc); else *turncp = turnc; return err; } int turnc_send(struct turnc *turnc, const struct sa *dst, struct mbuf *mb) { size_t pos, indlen; struct chan *chan; int err; if (!turnc || !dst || !mb) return EINVAL; chan = turnc_chan_find_peer(turnc, dst); if (chan) { struct chan_hdr hdr; if (mb->pos < CHAN_HDR_SIZE) return EINVAL; hdr.nr = turnc_chan_numb(chan); hdr.len = mbuf_get_left(mb); mb->pos -= CHAN_HDR_SIZE; pos = mb->pos; err = turnc_chan_hdr_encode(&hdr, mb); if (err) return err; if (turnc->proto == IPPROTO_TCP) { mb->pos = mb->end; /* padding */ while (hdr.len++ & 0x03) { err = mbuf_write_u8(mb, 0x00); if (err) return err; } } mb->pos = pos; } else { indlen = stun_indlen(dst); if (mb->pos < indlen) return EINVAL; mb->pos -= indlen; pos = mb->pos; err = stun_msg_encode(mb, STUN_METHOD_SEND, STUN_CLASS_INDICATION, sendind_tid, NULL, NULL, 0, false, 0x00, 2, STUN_ATTR_XOR_PEER_ADDR, dst, STUN_ATTR_DATA, mb); if (err) return err; mb->pos = pos; } switch (turnc->proto) { case IPPROTO_UDP: err = udp_send(turnc->sock, &turnc->srv, mb); break; case IPPROTO_TCP: err = tcp_send(turnc->sock, mb); break; #ifdef USE_DTLS case STUN_TRANSP_DTLS: err = dtls_send(turnc->sock, mb); break; #endif default: err = EPROTONOSUPPORT; break; } return err; } int turnc_recv(struct turnc *turnc, struct sa *src, struct mbuf *mb) { struct stun_attr *peer, *data; struct stun_unknown_attr ua; struct stun_msg *msg; int err = 0; if (!turnc || !src || !mb) return EINVAL; if (stun_msg_decode(&msg, mb, &ua)) { struct chan_hdr hdr; struct chan *chan; if (turnc_chan_hdr_decode(&hdr, mb)) return EBADMSG; if (mbuf_get_left(mb) < hdr.len) return EBADMSG; chan = turnc_chan_find_numb(turnc, hdr.nr); if (!chan) return EBADMSG; *src = *turnc_chan_peer(chan); return 0; } switch (stun_msg_class(msg)) { case STUN_CLASS_INDICATION: if (ua.typec > 0) { err = ENOSYS; break; } if (stun_msg_method(msg) != STUN_METHOD_DATA) { err = ENOSYS; break; } peer = stun_msg_attr(msg, STUN_ATTR_XOR_PEER_ADDR); data = stun_msg_attr(msg, STUN_ATTR_DATA); if (!peer || !data) { err = EPROTO; break; } *src = peer->v.xor_peer_addr; mb->pos = data->v.data.pos; mb->end = data->v.data.end; break; case STUN_CLASS_ERROR_RESP: case STUN_CLASS_SUCCESS_RESP: (void)stun_ctrans_recv(turnc->stun, msg, &ua); mb->pos = mb->end; break; default: err = ENOSYS; break; } mem_deref(msg); return err; } bool turnc_request_loops(struct loop_state *ls, uint16_t scode) { bool loop = false; switch (scode) { case 0: ls->failc = 0; break; default: if (ls->last_scode == scode) loop = true; /*@fallthrough@*/ case 300: if (++ls->failc >= FAILC_MAX) loop = true; break; } ls->last_scode = scode; return loop; } void turnc_loopstate_reset(struct loop_state *ls) { if (!ls) return; ls->last_scode = 0; ls->failc = 0; } int turnc_keygen(struct turnc *turnc, const struct stun_msg *msg) { struct stun_attr *realm, *nonce; realm = stun_msg_attr(msg, STUN_ATTR_REALM); nonce = stun_msg_attr(msg, STUN_ATTR_NONCE); if (!realm || !nonce) return EPROTO; mem_deref(turnc->realm); mem_deref(turnc->nonce); turnc->realm = mem_ref(realm->v.realm); turnc->nonce = mem_ref(nonce->v.nonce); return md5_printf(turnc->md5_hash, "%s:%s:%s", turnc->username, turnc->realm, turnc->password); } re-1.1.0/src/turn/turnc.h000066400000000000000000000047601373627245400152260ustar00rootroot00000000000000/** * @file turnc.h Internal TURN interface * * Copyright (C) 2010 Creytiv.com */ #include struct loop_state { uint32_t failc; uint16_t last_scode; }; struct channels; /** Defines a TURN Client */ struct turnc { struct loop_state ls; /**< Loop state */ struct udp_helper *uh; /**< UDP Helper for the TURN Socket */ struct stun_ctrans *ct; /**< Pending STUN Client Transaction */ char *username; /**< Authentication username */ char *password; /**< Authentication password */ struct sa psrv; /**< Previous TURN Server address */ struct sa srv; /**< TURN Server address */ void *sock; /**< Transport socket */ int proto; /**< Transport protocol */ struct stun *stun; /**< STUN Instance */ uint32_t lifetime; /**< Allocation lifetime in [seconds]*/ struct tmr tmr; /**< Allocation refresh timer */ turnc_h *th; /**< Turn client handler */ void *arg; /**< Handler argument */ uint8_t md5_hash[MD5_SIZE]; /**< Cached MD5-sum of credentials */ char *nonce; /**< Saved NONCE value from server */ char *realm; /**< Saved REALM value from server */ struct hash *perms; /**< Hash-table of permissions */ struct channels *chans; /**< TURN Channels */ bool allocated; /**< Allocation was done flag */ }; /* Util */ bool turnc_request_loops(struct loop_state *ls, uint16_t scode); void turnc_loopstate_reset(struct loop_state *ls); int turnc_keygen(struct turnc *turnc, const struct stun_msg *msg); /* Permission */ int turnc_perm_hash_alloc(struct hash **ht, uint32_t bsize); /* Channels */ enum { CHAN_HDR_SIZE = 4, }; struct chan_hdr { uint16_t nr; uint16_t len; }; struct chan; int turnc_chan_hash_alloc(struct channels **cp, uint32_t bsize); struct chan *turnc_chan_find_numb(const struct turnc *turnc, uint16_t nr); struct chan *turnc_chan_find_peer(const struct turnc *turnc, const struct sa *peer); uint16_t turnc_chan_numb(const struct chan *chan); const struct sa *turnc_chan_peer(const struct chan *chan); int turnc_chan_hdr_encode(const struct chan_hdr *hdr, struct mbuf *mb); int turnc_chan_hdr_decode(struct chan_hdr *hdr, struct mbuf *mb); re-1.1.0/src/udp/000077500000000000000000000000001373627245400135135ustar00rootroot00000000000000re-1.1.0/src/udp/mcast.c000066400000000000000000000023561373627245400147740ustar00rootroot00000000000000/** * @file mcast.c UDP Multicast * * Copyright (C) 2010 Creytiv.com */ #define _BSD_SOURCE 1 #define _DEFAULT_SOURCE 1 #include #include #include #include static int multicast_update(struct udp_sock *us, const struct sa *group, bool join) { struct ip_mreq mreq; #ifdef HAVE_INET6 struct ipv6_mreq mreq6; #endif int err; if (!us || !group) return EINVAL; switch (sa_af(group)) { case AF_INET: mreq.imr_multiaddr = group->u.in.sin_addr; mreq.imr_interface.s_addr = 0; err = udp_setsockopt(us, IPPROTO_IP, join ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)); break; #ifdef HAVE_INET6 case AF_INET6: mreq6.ipv6mr_multiaddr = group->u.in6.sin6_addr; mreq6.ipv6mr_interface = 0; err = udp_setsockopt(us, IPPROTO_IPV6, join ? IPV6_JOIN_GROUP : IPV6_LEAVE_GROUP, &mreq6, sizeof(mreq6)); break; #endif default: return EAFNOSUPPORT; } return err; } int udp_multicast_join(struct udp_sock *us, const struct sa *group) { return multicast_update(us, group, true); } int udp_multicast_leave(struct udp_sock *us, const struct sa *group) { return multicast_update(us, group, false); } re-1.1.0/src/udp/mod.mk000066400000000000000000000001271373627245400146230ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += udp/udp.c SRCS += udp/mcast.c re-1.1.0/src/udp/udp.c000066400000000000000000000351551373627245400144600ustar00rootroot00000000000000/** * @file udp.c User Datagram Protocol * * Copyright (C) 2010 Creytiv.com */ #include #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_IO_H #include #endif #if !defined(WIN32) #define __USE_POSIX 1 /**< Use POSIX flag */ #define __USE_XOPEN2K 1/**< Use POSIX.1:2001 code */ #include #endif #include #ifdef HAVE_STRINGS_H #include #endif #ifdef __APPLE__ #include "TargetConditionals.h" #endif #include #include #include #include #include #include #include #include #include #define DEBUG_MODULE "udp" #define DEBUG_LEVEL 5 #include /** Platform independent buffer type cast */ #ifdef WIN32 #define BUF_CAST (char *) #define SOK_CAST (int) #define SIZ_CAST (int) #define close closesocket #else #define BUF_CAST #define SOK_CAST #define SIZ_CAST #endif enum { UDP_RXSZ_DEFAULT = 8192 }; /** Defines a UDP socket */ struct udp_sock { struct list helpers; /**< List of UDP Helpers */ udp_recv_h *rh; /**< Receive handler */ udp_error_h *eh; /**< Error handler */ void *arg; /**< Handler argument */ int fd; /**< Socket file descriptor */ int fd6; /**< IPv6 socket file descriptor */ bool conn; /**< Connected socket flag */ size_t rxsz; /**< Maximum receive chunk size */ size_t rx_presz; /**< Preallocated rx buffer size */ }; /** Defines a UDP helper */ struct udp_helper { struct le le; int layer; udp_helper_send_h *sendh; udp_helper_recv_h *recvh; void *arg; }; static void dummy_udp_recv_handler(const struct sa *src, struct mbuf *mb, void *arg) { (void)src; (void)mb; (void)arg; } static bool helper_send_handler(int *err, struct sa *dst, struct mbuf *mb, void *arg) { (void)err; (void)dst; (void)mb; (void)arg; return false; } static bool helper_recv_handler(struct sa *src, struct mbuf *mb, void *arg) { (void)src; (void)mb; (void)arg; return false; } static void udp_destructor(void *data) { struct udp_sock *us = data; list_flush(&us->helpers); if (-1 != us->fd) { fd_close(us->fd); (void)close(us->fd); } if (-1 != us->fd6) { fd_close(us->fd6); (void)close(us->fd6); } } static void udp_read(struct udp_sock *us, int fd) { struct mbuf *mb = mbuf_alloc(us->rxsz); struct sa src; struct le *le; int err = 0; ssize_t n; if (!mb) return; src.len = sizeof(src.u); n = recvfrom(fd, BUF_CAST mb->buf + us->rx_presz, mb->size - us->rx_presz, 0, &src.u.sa, &src.len); if (n < 0) { err = errno; if (EAGAIN == err) goto out; #ifdef EWOULDBLOCK if (EWOULDBLOCK == err) goto out; #endif #if TARGET_OS_IPHONE if (ENOTCONN == err) { struct udp_sock *us_new; struct sa laddr; err = udp_local_get(us, &laddr); if (err) goto out; if (-1 != us->fd) { fd_close(us->fd); (void)close(us->fd); us->fd = -1; } if (-1 != us->fd6) { fd_close(us->fd6); (void)close(us->fd6); us->fd6 = -1; } err = udp_listen(&us_new, &laddr, NULL, NULL); if (err) goto out; us->fd = us_new->fd; us->fd6 = us_new->fd6; us_new->fd = -1; us_new->fd6 = -1; mem_deref(us_new); udp_thread_attach(us); goto out; } #endif if (us->eh) us->eh(err, us->arg); goto out; } mb->pos = us->rx_presz; mb->end = n + us->rx_presz; (void)mbuf_resize(mb, mb->end); /* call helpers */ le = us->helpers.head; while (le) { struct udp_helper *uh = le->data; bool hdld; le = le->next; hdld = uh->recvh(&src, mb, uh->arg); if (hdld) goto out; } us->rh(&src, mb, us->arg); out: mem_deref(mb); } static void udp_read_handler(int flags, void *arg) { struct udp_sock *us = arg; (void)flags; udp_read(us, us->fd); } static void udp_read_handler6(int flags, void *arg) { struct udp_sock *us = arg; (void)flags; udp_read(us, us->fd6); } /** * Create and listen on a UDP Socket * * @param usp Pointer to returned UDP Socket * @param local Local network address * @param rh Receive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int udp_listen(struct udp_sock **usp, const struct sa *local, udp_recv_h *rh, void *arg) { struct addrinfo hints, *res = NULL, *r; struct udp_sock *us = NULL; char addr[64]; char serv[6] = "0"; int af, error, err = 0; if (!usp) return EINVAL; us = mem_zalloc(sizeof(*us), udp_destructor); if (!us) return ENOMEM; list_init(&us->helpers); us->fd = -1; us->fd6 = -1; if (local) { af = sa_af(local); (void)re_snprintf(addr, sizeof(addr), "%H", sa_print_addr, local); (void)re_snprintf(serv, sizeof(serv), "%u", sa_port(local)); } else { #ifdef HAVE_INET6 af = AF_UNSPEC; #else af = AF_INET; #endif } memset(&hints, 0, sizeof(hints)); /* set-up hints structure */ hints.ai_family = af; hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = IPPROTO_UDP; error = getaddrinfo(local ? addr : NULL, serv, &hints, &res); if (error) { #ifdef WIN32 DEBUG_WARNING("listen: getaddrinfo: wsaerr=%d\n", WSAGetLastError()); #endif DEBUG_WARNING("listen: getaddrinfo: %s:%s (%s)\n", addr, serv, gai_strerror(error)); err = EADDRNOTAVAIL; goto out; } for (r = res; r; r = r->ai_next) { int fd = -1; if (us->fd > 0) continue; DEBUG_INFO("listen: for: af=%d addr=%j\n", r->ai_family, r->ai_addr); fd = SOK_CAST socket(r->ai_family, SOCK_DGRAM, IPPROTO_UDP); if (fd < 0) { err = errno; continue; } err = net_sockopt_blocking_set(fd, false); if (err) { DEBUG_WARNING("udp listen: nonblock set: %m\n", err); (void)close(fd); continue; } if (bind(fd, r->ai_addr, SIZ_CAST r->ai_addrlen) < 0) { err = errno; DEBUG_INFO("listen: bind(): %m (%J)\n", err, local); (void)close(fd); continue; } /* Can we do both IPv4 and IPv6 on same socket? */ if (AF_INET6 == r->ai_family) { struct sa sa; int on = 1; /* assume v6only */ #if defined (IPPROTO_IPV6) && defined (IPV6_V6ONLY) socklen_t on_len = sizeof(on); if (0 != getsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&on, &on_len)) { on = 1; } #endif /* Extra check for unspec addr - MAC OS X/Solaris */ if (0==sa_set_sa(&sa, r->ai_addr) && sa_is_any(&sa)) { on = 1; } DEBUG_INFO("socket %d: IPV6_V6ONLY is %d\n", fd, on); if (on) { us->fd6 = fd; continue; } } /* OK */ us->fd = fd; break; } freeaddrinfo(res); /* We must have at least one socket */ if (-1 == us->fd && -1 == us->fd6) { if (0 == err) err = EADDRNOTAVAIL; goto out; } err = udp_thread_attach(us); if (err) goto out; us->rh = rh ? rh : dummy_udp_recv_handler; us->arg = arg; us->rxsz = UDP_RXSZ_DEFAULT; out: if (err) mem_deref(us); else *usp = us; return err; } /** * Connect a UDP Socket to a specific peer. * When connected, this UDP Socket will only receive data from that peer. * * @param us UDP Socket * @param peer Peer network address * * @return 0 if success, otherwise errorcode */ int udp_connect(struct udp_sock *us, const struct sa *peer) { int fd; if (!us || !peer) return EINVAL; /* choose a socket */ if (AF_INET6 == sa_af(peer) && -1 != us->fd6) fd = us->fd6; else fd = us->fd; if (0 != connect(fd, &peer->u.sa, peer->len)) return errno; us->conn = true; return 0; } static int udp_send_internal(struct udp_sock *us, const struct sa *dst, struct mbuf *mb, struct le *le) { struct sa hdst; int err = 0, fd; /* choose a socket */ if (AF_INET6 == sa_af(dst) && -1 != us->fd6) fd = us->fd6; else fd = us->fd; /* call helpers in reverse order */ while (le) { struct udp_helper *uh = le->data; le = le->prev; if (dst != &hdst) { sa_cpy(&hdst, dst); dst = &hdst; } if (uh->sendh(&err, &hdst, mb, uh->arg) || err) return err; } /* Connected socket? */ if (us->conn) { if (send(fd, BUF_CAST mb->buf + mb->pos, mb->end - mb->pos, 0) < 0) return errno; } else { if (sendto(fd, BUF_CAST mb->buf + mb->pos, mb->end - mb->pos, 0, &dst->u.sa, dst->len) < 0) return errno; } return 0; } /** * Send a UDP Datagram to a peer * * @param us UDP Socket * @param dst Destination network address * @param mb Buffer to send * * @return 0 if success, otherwise errorcode */ int udp_send(struct udp_sock *us, const struct sa *dst, struct mbuf *mb) { if (!us || !dst || !mb) return EINVAL; return udp_send_internal(us, dst, mb, us->helpers.tail); } /** * Send an anonymous UDP Datagram to a peer * * @param dst Destination network address * @param mb Buffer to send * * @return 0 if success, otherwise errorcode */ int udp_send_anon(const struct sa *dst, struct mbuf *mb) { struct udp_sock *us; int err; if (!dst || !mb) return EINVAL; err = udp_listen(&us, NULL, NULL, NULL); if (err) return err; err = udp_send_internal(us, dst, mb, NULL); mem_deref(us); return err; } /** * Get the local network address on the UDP Socket * * @param us UDP Socket * @param local The returned local network address * * @return 0 if success, otherwise errorcode * * @todo bug no way to specify AF */ int udp_local_get(const struct udp_sock *us, struct sa *local) { if (!us || !local) return EINVAL; local->len = sizeof(local->u); if (0 == getsockname(us->fd, &local->u.sa, &local->len)) return 0; if (0 == getsockname(us->fd6, &local->u.sa, &local->len)) return 0; return errno; } /** * Set socket options on the UDP Socket * * @param us UDP Socket * @param level Socket level * @param optname Option name * @param optval Option value * @param optlen Option length * * @return 0 if success, otherwise errorcode */ int udp_setsockopt(struct udp_sock *us, int level, int optname, const void *optval, uint32_t optlen) { int err = 0; if (!us) return EINVAL; if (-1 != us->fd) { if (0 != setsockopt(us->fd, level, optname, BUF_CAST optval, optlen)) err |= errno; } if (-1 != us->fd6) { if (0 != setsockopt(us->fd6, level, optname, BUF_CAST optval, optlen)) err |= errno; } return err; } /** * Set the send/receive buffer size on a UDP Socket * * @param us UDP Socket * @param size Buffer size in bytes * * @return 0 if success, otherwise errorcode */ int udp_sockbuf_set(struct udp_sock *us, int size) { int err = 0; if (!us) return EINVAL; err |= udp_setsockopt(us, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)); err |= udp_setsockopt(us, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)); return err; } /** * Set the maximum receive chunk size on a UDP Socket * * @param us UDP Socket * @param rxsz Maximum receive chunk size */ void udp_rxsz_set(struct udp_sock *us, size_t rxsz) { if (!us) return; us->rxsz = rxsz; } /** * Set preallocated space on receive buffer. * * @param us UDP Socket * @param rx_presz Size of preallocate space. */ void udp_rxbuf_presz_set(struct udp_sock *us, size_t rx_presz) { if (!us) return; us->rx_presz = rx_presz; } /** * Set receive handler on a UDP Socket * * @param us UDP Socket * @param rh Receive handler * @param arg Handler argument */ void udp_handler_set(struct udp_sock *us, udp_recv_h *rh, void *arg) { if (!us) return; us->rh = rh ? rh : dummy_udp_recv_handler; us->arg = arg; } /** * Set error handler on a UDP Socket * * @param us UDP Socket * @param eh Error handler */ void udp_error_handler_set(struct udp_sock *us, udp_error_h *eh) { if (!us) return; us->eh = eh; } /** * Get the File Descriptor from a UDP Socket * * @param us UDP Socket * @param af Address Family * * @return File Descriptor, or -1 for errors */ int udp_sock_fd(const struct udp_sock *us, int af) { if (!us) return -1; switch (af) { default: case AF_INET: return us->fd; case AF_INET6: return (us->fd6 != -1) ? us->fd6 : us->fd; } } /** * Attach the current thread to the UDP Socket * * @param us UDP Socket * * @return 0 if success, otherwise errorcode */ int udp_thread_attach(struct udp_sock *us) { int err = 0; if (!us) return EINVAL; if (-1 != us->fd) { err = fd_listen(us->fd, FD_READ, udp_read_handler, us); if (err) goto out; } if (-1 != us->fd6) { err = fd_listen(us->fd6, FD_READ, udp_read_handler6, us); if (err) goto out; } out: if (err) udp_thread_detach(us); return err; } /** * Detach the current thread from the UDP Socket * * @param us UDP Socket */ void udp_thread_detach(struct udp_sock *us) { if (!us) return; if (-1 != us->fd) fd_close(us->fd); if (-1 != us->fd6) fd_close(us->fd6); } static void helper_destructor(void *data) { struct udp_helper *uh = data; list_unlink(&uh->le); } static bool sort_handler(struct le *le1, struct le *le2, void *arg) { struct udp_helper *uh1 = le1->data, *uh2 = le2->data; (void)arg; return uh1->layer <= uh2->layer; } /** * Register a UDP protocol stack helper * * @param uhp Pointer to allocated UDP helper object * @param us UDP socket * @param layer Layer number; higher number means higher up in stack * @param sh Send handler * @param rh Receive handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode */ int udp_register_helper(struct udp_helper **uhp, struct udp_sock *us, int layer, udp_helper_send_h *sh, udp_helper_recv_h *rh, void *arg) { struct udp_helper *uh; if (!us) return EINVAL; uh = mem_zalloc(sizeof(*uh), helper_destructor); if (!uh) return ENOMEM; list_append(&us->helpers, &uh->le, uh); uh->layer = layer; uh->sendh = sh ? sh : helper_send_handler; uh->recvh = rh ? rh : helper_recv_handler; uh->arg = arg; list_sort(&us->helpers, sort_handler, NULL); if (uhp) *uhp = uh; return 0; } /** * Send a UDP Datagram to a remote peer bypassing this helper and * the helpers above it. * * @param us UDP Socket * @param dst Destination network address * @param mb Buffer to send * @param uh UDP Helper * * @return 0 if success, otherwise errorcode */ int udp_send_helper(struct udp_sock *us, const struct sa *dst, struct mbuf *mb, struct udp_helper *uh) { if (!us || !dst || !mb || !uh) return EINVAL; return udp_send_internal(us, dst, mb, uh->le.prev); } /** * Find a UDP-helper on a UDP socket * * @param us UDP socket * @param layer Layer number * * @return UDP-helper if found, NULL if not found */ struct udp_helper *udp_helper_find(const struct udp_sock *us, int layer) { struct le *le; if (!us) return NULL; for (le = us->helpers.head; le; le = le->next) { struct udp_helper *uh = le->data; if (layer == uh->layer) return uh; } return NULL; } re-1.1.0/src/uri/000077500000000000000000000000001373627245400135225ustar00rootroot00000000000000re-1.1.0/src/uri/mod.mk000066400000000000000000000001261373627245400146310ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += uri/uri.c SRCS += uri/uric.c re-1.1.0/src/uri/uri.c000066400000000000000000000125551373627245400144750ustar00rootroot00000000000000/** * @file uri.c Uniform Resource Identifier (URI) module * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include /** * Encode a URI object * * @param pf Print function to encode into * @param uri URI object * * @return 0 if success, otherwise errorcode */ int uri_encode(struct re_printf *pf, const struct uri *uri) { int err; if (!uri) return 0; if (!pl_isset(&uri->scheme) || !pl_isset(&uri->host)) return EINVAL; err = re_hprintf(pf, "%r:", &uri->scheme); if (err) return err; if (pl_isset(&uri->user)) { err = re_hprintf(pf, "%r", &uri->user); if (pl_isset(&uri->password)) err |= re_hprintf(pf, ":%r", &uri->password); err |= pf->vph("@", 1, pf->arg); if (err) return err; } /* The IPv6 address is delimited by '[' and ']' */ switch (uri->af) { #ifdef HAVE_INET6 case AF_INET6: err = re_hprintf(pf, "[%r]", &uri->host); break; #endif default: err = re_hprintf(pf, "%r", &uri->host); break; } if (err) return err; if (uri->port) err = re_hprintf(pf, ":%u", uri->port); err |= re_hprintf(pf, "%r%r%r", &uri->path, &uri->params, &uri->headers); return err; } /** * Decode host-port portion of a URI (if present) * * @param hostport Host and port input string * @param host Decoded host portion * @param port Decoded port portion * * @return 0 if success, otherwise errorcode */ int uri_decode_hostport(const struct pl *hostport, struct pl *host, struct pl *port) { if (!hostport || !host || !port) return EINVAL; /* Try IPv6 first */ if (!re_regex(hostport->p, hostport->l, "\\[[0-9a-f:]+\\][:]*[0-9]*", host, NULL, port)) return 0; /* Then non-IPv6 host */ return re_regex(hostport->p, hostport->l, "[^:]+[:]*[0-9]*", host, NULL, port); } /** * Decode a pointer-length object into a URI object * * @param uri URI object * @param pl Pointer-length object to decode from * * @return 0 if success, otherwise errorcode */ int uri_decode(struct uri *uri, const struct pl *pl) { struct sa addr; struct pl port = PL_INIT; struct pl hostport; int err; if (!uri || !pl) return EINVAL; memset(uri, 0, sizeof(*uri)); if (0 == re_regex(pl->p, pl->l, "[^:]+:[^@:]*[:]*[^@]*@[^/;? ]+[^;? ]*[^?]*[^]*", &uri->scheme, &uri->user, NULL, &uri->password, &hostport, &uri->path, &uri->params, &uri->headers)) { if (0 == uri_decode_hostport(&hostport, &uri->host, &port)) goto out; } memset(uri, 0, sizeof(*uri)); err = re_regex(pl->p, pl->l, "[^:]+:[^/;? ]+[^;? ]*[^?]*[^]*", &uri->scheme, &hostport, &uri->path, &uri->params, &uri->headers); if (0 == err) { err = uri_decode_hostport(&hostport, &uri->host, &port); if (0 == err) goto out; } return err; out: /* Cache host address family */ if (0 == sa_set(&addr, &uri->host, 0)) uri->af = sa_af(&addr); else uri->af = AF_UNSPEC; if (pl_isset(&port)) uri->port = (uint16_t)pl_u32(&port); return 0; } /** * Get a URI parameter and possibly the value of it * * @param pl Pointer-length string containing parameters * @param pname URI Parameter name * @param pvalue Returned URI Parameter value * * @return 0 if success, otherwise errorcode */ int uri_param_get(const struct pl *pl, const struct pl *pname, struct pl *pvalue) { char expr[128]; if (!pl || !pname || !pvalue) return EINVAL; (void)re_snprintf(expr, sizeof(expr), ";%r[=]*[^;]*", pname); return re_regex(pl->p, pl->l, expr, NULL, pvalue); } /** * Call the apply handler for each URI Parameter * * @param pl Pointer-length string containing parameters * @param ah Apply handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode (returned from handler) */ int uri_params_apply(const struct pl *pl, uri_apply_h *ah, void *arg) { struct pl plr, pname, eq, pvalue; int err = 0; if (!pl || !ah) return EINVAL; plr = *pl; while (plr.l > 0) { err = re_regex(plr.p, plr.l, ";[^;=]+[=]*[^;]*", &pname, &eq, &pvalue); if (err) break; pl_advance(&plr, 1 + pname.l + eq.l + pvalue.l); err = ah(&pname, &pvalue, arg); if (err) break; } return err; } /** * Get a URI header and possibly the value of it * * @param pl Pointer-length string containing URI Headers * @param hname URI Header name * @param hvalue Returned URI Header value * * @return 0 if success, otherwise errorcode */ int uri_header_get(const struct pl *pl, const struct pl *hname, struct pl *hvalue) { char expr[128]; if (!pl || !hname || !hvalue) return EINVAL; (void)re_snprintf(expr, sizeof(expr), "[?&]1%r=[^&]+", hname); return re_regex(pl->p, pl->l, expr, NULL, hvalue); } /** * Call the apply handler for each URI Header * * @param pl Pointer-length string containing URI Headers * @param ah Apply handler * @param arg Handler argument * * @return 0 if success, otherwise errorcode (returned from handler) */ int uri_headers_apply(const struct pl *pl, uri_apply_h *ah, void *arg) { struct pl plr, sep, hname, hvalue; int err = 0; if (!pl || !ah) return EINVAL; plr = *pl; while (plr.l > 0) { err = re_regex(plr.p, plr.l, "[?&]1[^=]+=[^&]+", &sep, &hname, &hvalue); if (err) break; pl_advance(&plr, sep.l + hname.l + 1 + hvalue.l); err = ah(&hname, &hvalue, arg); if (err) break; } return err; } re-1.1.0/src/uri/uric.c000066400000000000000000000111551373627245400146330ustar00rootroot00000000000000/** * @file uric.c URI component escaping/unescaping * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #define DEBUG_MODULE "uric" #define DEBUG_LEVEL 5 #include /** Defines the URI escape handler */ typedef bool (esc_h)(char c); static bool is_mark(int c) { switch (c) { case '-': case '_': case '.': case '!': case '~': case '*': case '\'': case '(': case ')': return true; default: return false; } } static bool is_unreserved(char c) { return isalnum(c) || is_mark(c); } static bool is_user_unreserved(int c) { switch (c) { case '&': case '=': case '+': case '$': case ',': case ';': case '?': case '/': return true; default: return false; } } static bool is_hnv_unreserved(char c) { switch (c) { case '[': case ']': case '/': case '?': case ':': case '+': case '$': return true; default: return false; } } static bool is_user(char c) { return is_unreserved(c) || is_user_unreserved(c); } static bool is_password(char c) { switch (c) { case '&': case '=': case '+': case '$': case ',': return true; default: return is_unreserved(c); } } static bool is_param_unreserved(char c) { switch (c) { case '[': case ']': case '/': case ':': case '&': case '+': case '$': return true; default: return false; } } static bool is_paramchar(char c) { return is_param_unreserved(c) || is_unreserved(c); } static bool is_hvalue(char c) { return is_hnv_unreserved(c) || is_unreserved(c); } static int comp_escape(struct re_printf *pf, const struct pl *pl, esc_h *eh) { size_t i; int err = 0; if (!pf || !pl || !eh) return EINVAL; for (i=0; il && !err; i++) { const char c = pl->p[i]; if (eh(c)) { err = pf->vph(&c, 1, pf->arg); } else { err = re_hprintf(pf, "%%%02X", c); } } return err; } static int comp_unescape(struct re_printf *pf, const struct pl *pl, esc_h *eh) { size_t i; int err = 0; if (!pf || !pl || !eh) return EINVAL; for (i=0; il && !err; i++) { const char c = pl->p[i]; if (eh(c)) { err = pf->vph(&c, 1, pf->arg); continue; } if ('%' == c) { if (i+2 < pl->l) { const uint8_t hi = ch_hex(pl->p[++i]); const uint8_t lo = ch_hex(pl->p[++i]); const char b = hi<<4 | lo; err = pf->vph(&b, 1, pf->arg); } else { DEBUG_WARNING("unescape: short uri (%u)\n", i); return EBADMSG; } } else { DEBUG_WARNING("unescape: illegal '%c' in %r\n", c, pl); return EINVAL; } } return err; } /** * Escape a URI user component * * @param pf Print function * @param pl String to escape * * @return 0 if success, otherwise errorcode */ int uri_user_escape(struct re_printf *pf, const struct pl *pl) { return comp_escape(pf, pl, is_user); } /** * Unescape a URI user component * * @param pf Print function * @param pl String to unescape * * @return 0 if success, otherwise errorcode */ int uri_user_unescape(struct re_printf *pf, const struct pl *pl) { return comp_unescape(pf, pl, is_user); } /** * Escape a URI password component * * @param pf Print function * @param pl String to escape * * @return 0 if success, otherwise errorcode */ int uri_password_escape(struct re_printf *pf, const struct pl *pl) { return comp_escape(pf, pl, is_password); } /** * Unescape a URI password component * * @param pf Print function * @param pl String to unescape * * @return 0 if success, otherwise errorcode */ int uri_password_unescape(struct re_printf *pf, const struct pl *pl) { return comp_unescape(pf, pl, is_password); } /** * Escape one URI Parameter value * * @param pf Print function * @param pl String to escape * * @return 0 if success, otherwise errorcode */ int uri_param_escape(struct re_printf *pf, const struct pl *pl) { return comp_escape(pf, pl, is_paramchar); } /** * Unescape one URI Parameter value * * @param pf Print function * @param pl String to unescape * * @return 0 if success, otherwise errorcode */ int uri_param_unescape(struct re_printf *pf, const struct pl *pl) { return comp_unescape(pf, pl, is_paramchar); } /** * Escape one URI Header name/value * * @param pf Print function * @param pl String to escape * * @return 0 if success, otherwise errorcode */ int uri_header_escape(struct re_printf *pf, const struct pl *pl) { return comp_escape(pf, pl, is_hvalue); } /** * Unescape one URI Header name/value * * @param pf Print function * @param pl String to unescape * * @return 0 if success, otherwise errorcode */ int uri_header_unescape(struct re_printf *pf, const struct pl *pl) { return comp_unescape(pf, pl, is_hvalue); } re-1.1.0/src/websock/000077500000000000000000000000001373627245400143605ustar00rootroot00000000000000re-1.1.0/src/websock/mod.mk000066400000000000000000000001131373627245400154630ustar00rootroot00000000000000# # mod.mk # # Copyright (C) 2010 Creytiv.com # SRCS += websock/websock.c re-1.1.0/src/websock/websock.c000066400000000000000000000322461373627245400161700ustar00rootroot00000000000000/** * @file websock.c Implementation of The WebSocket Protocol * * Copyright (C) 2010 Creytiv.com */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include enum { TIMEOUT_CLOSE = 10000, BUFSIZE_MAX = 131072, }; enum websock_state { ACCEPTING = 0, CONNECTING, OPEN, CLOSING, CLOSED, }; struct websock { websock_shutdown_h *shuth; void *arg; bool shutdown; }; struct websock_conn { struct tmr tmr; struct sa peer; char nonce[24]; struct websock *sock; struct tcp_conn *tc; struct tls_conn *sc; struct mbuf *mb; struct http_req *req; websock_estab_h *estabh; websock_recv_h *recvh; websock_close_h *closeh; void *arg; enum websock_state state; unsigned kaint; bool active; }; static const char magic[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; static void timeout_handler(void *arg); static void dummy_recv_handler(const struct websock_hdr *hdr, struct mbuf *mb, void *arg) { (void)hdr; (void)mb; (void)arg; } static void internal_close_handler(int err, void *arg) { struct websock_conn *conn = arg; (void)err; mem_deref(conn); } static void sock_destructor(void *arg) { struct websock *sock = arg; if (sock->shutdown) { sock->shutdown = false; mem_ref(sock); if (sock->shuth) sock->shuth(sock->arg); return; } } static void conn_destructor(void *arg) { struct websock_conn *conn = arg; if (conn->state == OPEN) (void)websock_close(conn, WEBSOCK_GOING_AWAY, "Going Away"); if (conn->state == CLOSING) { conn->recvh = dummy_recv_handler; conn->closeh = internal_close_handler; conn->arg = conn; tmr_start(&conn->tmr, TIMEOUT_CLOSE, timeout_handler, conn); /* important: the hack below depends on this */ mem_ref(conn); return; } tmr_cancel(&conn->tmr); mem_deref(conn->sc); mem_deref(conn->tc); mem_deref(conn->mb); mem_deref(conn->req); mem_deref(conn->sock); } static void conn_close(struct websock_conn *conn, int err) { tmr_cancel(&conn->tmr); conn->sc = mem_deref(conn->sc); conn->tc = mem_deref(conn->tc); conn->state = CLOSED; conn->closeh(err, conn->arg); } static void timeout_handler(void *arg) { struct websock_conn *conn = arg; conn_close(conn, ETIMEDOUT); } static void keepalive_handler(void *arg) { struct websock_conn *conn = arg; tmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn); (void)websock_send(conn, WEBSOCK_PING, NULL); } static enum websock_scode websock_err2scode(int err) { switch (err) { case EOVERFLOW: return WEBSOCK_MESSAGE_TOO_BIG; case EPROTO: return WEBSOCK_PROTOCOL_ERROR; case EBADMSG: return WEBSOCK_PROTOCOL_ERROR; default: return WEBSOCK_INTERNAL_ERROR; } } static int websock_decode(struct websock_hdr *hdr, struct mbuf *mb) { uint8_t v, *p; size_t i; if (mbuf_get_left(mb) < 2) return ENODATA; v = mbuf_read_u8(mb); hdr->fin = v>>7 & 0x1; hdr->rsv1 = v>>6 & 0x1; hdr->rsv2 = v>>5 & 0x1; hdr->rsv3 = v>>4 & 0x1; hdr->opcode = v & 0x0f; v = mbuf_read_u8(mb); hdr->mask = v>>7 & 0x1; hdr->len = v & 0x7f; if (hdr->len == 126) { if (mbuf_get_left(mb) < 2) return ENODATA; hdr->len = ntohs(mbuf_read_u16(mb)); } else if (hdr->len == 127) { if (mbuf_get_left(mb) < 8) return ENODATA; hdr->len = sys_ntohll(mbuf_read_u64(mb)); } if (hdr->mask) { if (mbuf_get_left(mb) < (4 + hdr->len)) return ENODATA; hdr->mkey[0] = mbuf_read_u8(mb); hdr->mkey[1] = mbuf_read_u8(mb); hdr->mkey[2] = mbuf_read_u8(mb); hdr->mkey[3] = mbuf_read_u8(mb); for (i=0, p=mbuf_buf(mb); ilen; i++) p[i] = p[i] ^ hdr->mkey[i%4]; } else { if (mbuf_get_left(mb) < hdr->len) return ENODATA; } return 0; } static void recv_handler(struct mbuf *mb, void *arg) { struct websock_conn *conn = arg; int err = 0; if (conn->mb) { const size_t len = mbuf_get_left(mb), pos = conn->mb->pos; if ((mbuf_get_left(conn->mb) + len) > BUFSIZE_MAX) { err = EOVERFLOW; goto out; } conn->mb->pos = conn->mb->end; err = mbuf_write_mem(conn->mb, mbuf_buf(mb), len); if (err) goto out; conn->mb->pos = pos; } else { conn->mb = mem_ref(mb); } while (conn->mb) { struct websock_hdr hdr; size_t pos, end; pos = conn->mb->pos; err = websock_decode(&hdr, conn->mb); if (err) { if (err == ENODATA) { conn->mb->pos = pos; err = 0; break; } goto out; } if (conn->active == hdr.mask) { err = EPROTO; goto out; } if (hdr.rsv1 || hdr.rsv2 || hdr.rsv3) { err = EPROTO; goto out; } mb = conn->mb; end = mb->end; mb->end = mb->pos + (size_t)hdr.len; if (end > mb->end) { struct mbuf *mbn = mbuf_alloc(end - mb->end); if (!mbn) { err = ENOMEM; goto out; } (void)mbuf_write_mem(mbn, mb->buf + mb->end, end - mb->end); mbn->pos = 0; conn->mb = mbn; } else { conn->mb = NULL; } switch (hdr.opcode) { case WEBSOCK_CONT: case WEBSOCK_TEXT: case WEBSOCK_BIN: mem_ref(conn); conn->recvh(&hdr, mb, conn->arg); if (mem_nrefs(conn) == 1) { if (conn->state == OPEN) (void)websock_close(conn, WEBSOCK_GOING_AWAY, "Going Away"); /* * This is a hack. We enforce CLOSING * state so we know the connection will * continue to live. */ conn->state = CLOSING; } mem_deref(conn); break; case WEBSOCK_CLOSE: if (conn->state == OPEN) (void)websock_send(conn, WEBSOCK_CLOSE, "%b", mbuf_buf(mb), mbuf_get_left(mb)); conn_close(conn, 0); mem_deref(mb); return; case WEBSOCK_PING: (void)websock_send(conn, WEBSOCK_PONG, "%b", mbuf_buf(mb), mbuf_get_left(mb)); break; case WEBSOCK_PONG: break; default: mem_deref(mb); err = EPROTO; goto out; } mem_deref(mb); } out: if (err) { (void)websock_close(conn, websock_err2scode(err), NULL); conn_close(conn, err); } } static void close_handler(int err, void *arg) { struct websock_conn *conn = arg; conn_close(conn, err); } static int accept_print(struct re_printf *pf, const struct pl *key) { uint8_t digest[SHA_DIGEST_LENGTH]; SHA_CTX ctx; SHA1_Init(&ctx); SHA1_Update(&ctx, key->p, key->l); SHA1_Update(&ctx, magic, sizeof(magic)-1); SHA1_Final(digest, &ctx); return base64_print(pf, digest, sizeof(digest)); } static void http_resp_handler(int err, const struct http_msg *msg, void *arg) { struct websock_conn *conn = arg; const struct http_hdr *hdr; struct pl key; char buf[32]; if (err || msg->scode != 101) goto fail; if (!http_msg_hdr_has_value(msg, HTTP_HDR_UPGRADE, "websocket")) goto fail; if (!http_msg_hdr_has_value(msg, HTTP_HDR_CONNECTION, "Upgrade")) goto fail; hdr = http_msg_hdr(msg, HTTP_HDR_SEC_WEBSOCKET_ACCEPT); if (!hdr) goto fail; key.p = conn->nonce; key.l = sizeof(conn->nonce); if (re_snprintf(buf, sizeof(buf), "%H", accept_print, &key) < 0) goto fail; if (pl_strcmp(&hdr->val, buf)) goto fail; /* here we are ok */ conn->state = OPEN; (void)tcp_conn_peer_get(conn->tc, &conn->peer); if (conn->kaint) tmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn); conn->estabh(conn->arg); return; fail: conn_close(conn, err ? err : EPROTO); } static void http_conn_handler(struct tcp_conn *tc, struct tls_conn *sc, void *arg) { struct websock_conn *conn = arg; conn->tc = mem_ref(tc); conn->sc = mem_ref(sc); tcp_set_handlers(conn->tc, NULL, recv_handler, close_handler, conn); } int websock_connect(struct websock_conn **connp, struct websock *sock, struct http_cli *cli, const char *uri, unsigned kaint, websock_estab_h *estabh, websock_recv_h *recvh, websock_close_h *closeh, void *arg, const char *fmt, ...) { struct websock_conn *conn; uint8_t nonce[16]; va_list ap; size_t len; int err; if (!connp || !sock || !cli || !uri || !estabh || !recvh || !closeh) return EINVAL; conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return ENOMEM; /* The nonce MUST be selected randomly for each connection */ rand_bytes(nonce, sizeof(nonce)); len = sizeof(conn->nonce); err = base64_encode(nonce, sizeof(nonce), conn->nonce, &len); if (err) goto out; conn->sock = mem_ref(sock); conn->kaint = kaint; conn->estabh = estabh; conn->recvh = recvh; conn->closeh = closeh; conn->arg = arg; conn->state = CONNECTING; conn->active = true; /* Protocol Handshake */ va_start(ap, fmt); err = http_request(&conn->req, cli, "GET", uri, http_resp_handler, NULL, conn, "Upgrade: websocket\r\n" "Connection: upgrade\r\n" "Sec-WebSocket-Key: %b\r\n" "Sec-WebSocket-Version: 13\r\n" "%v" "\r\n", conn->nonce, sizeof(conn->nonce), fmt, &ap); va_end(ap); if (err) goto out; http_req_set_conn_handler(conn->req, http_conn_handler); out: if (err) mem_deref(conn); else *connp = conn; return err; } int websock_accept(struct websock_conn **connp, struct websock *sock, struct http_conn *htconn, const struct http_msg *msg, unsigned kaint, websock_recv_h *recvh, websock_close_h *closeh, void *arg) { const struct http_hdr *key; struct websock_conn *conn; int err; if (!connp || !sock || !htconn || !msg || !recvh || !closeh) return EINVAL; if (!http_msg_hdr_has_value(msg, HTTP_HDR_UPGRADE, "websocket")) return EBADMSG; if (!http_msg_hdr_has_value(msg, HTTP_HDR_CONNECTION, "Upgrade")) return EBADMSG; if (!http_msg_hdr_has_value(msg, HTTP_HDR_SEC_WEBSOCKET_VERSION, "13")) return EBADMSG; key = http_msg_hdr(msg, HTTP_HDR_SEC_WEBSOCKET_KEY); if (!key) return EBADMSG; conn = mem_zalloc(sizeof(*conn), conn_destructor); if (!conn) return ENOMEM; err = http_reply(htconn, 101, "Switching Protocols", "Upgrade: websocket\r\n" "Connection: Upgrade\r\n" "Sec-WebSocket-Accept: %H\r\n" "\r\n", accept_print, &key->val); if (err) goto out; sa_cpy(&conn->peer, http_conn_peer(htconn)); conn->sock = mem_ref(sock); conn->tc = mem_ref(http_conn_tcp(htconn)); conn->sc = mem_ref(http_conn_tls(htconn)); conn->kaint = kaint; conn->recvh = recvh; conn->closeh = closeh; conn->arg = arg; conn->state = OPEN; conn->active = false; tcp_set_handlers(conn->tc, NULL, recv_handler, close_handler, conn); http_conn_close(htconn); if (conn->kaint) tmr_start(&conn->tmr, conn->kaint, keepalive_handler, conn); out: if (err) mem_deref(conn); else *connp = conn; return err; } static int websock_encode(struct mbuf *mb, bool fin, enum websock_opcode opcode, bool mask, size_t len) { int err; err = mbuf_write_u8(mb, (fin<<7) | (opcode & 0x0f)); if (len > 0xffff) { err |= mbuf_write_u8(mb, (mask<<7) | 127); err |= mbuf_write_u64(mb, sys_htonll(len)); } else if (len > 125) { err |= mbuf_write_u8(mb, (mask<<7) | 126); err |= mbuf_write_u16(mb, htons(len)); } else { err |= mbuf_write_u8(mb, (mask<<7) | len); } if (mask) { uint8_t mkey[4]; uint8_t *p; size_t i; rand_bytes(mkey, sizeof(mkey)); err |= mbuf_write_mem(mb, mkey, sizeof(mkey)); for (i=0, p=mbuf_buf(mb); iactive ? 14 : 10; size_t len, start; struct mbuf *mb; int err = 0; if (conn->state != OPEN) return ENOTCONN; mb = mbuf_alloc(2048); if (!mb) return ENOMEM; mb->pos = hsz; if (scode) err |= mbuf_write_u16(mb, htons(scode)); if (fmt) err |= mbuf_vprintf(mb, fmt, ap); if (err) goto out; len = mb->pos - hsz; if (len > 0xffff) start = mb->pos = 0; else if (len > 125) start = mb->pos = 6; else start = mb->pos = 8; err = websock_encode(mb, true, opcode, conn->active, len); if (err) goto out; mb->pos = start; err = tcp_send(conn->tc, mb); if (err) goto out; out: mem_deref(mb); return err; } int websock_send(struct websock_conn *conn, enum websock_opcode opcode, const char *fmt, ...) { va_list ap; int err; if (!conn) return EINVAL; va_start(ap, fmt); err = websock_vsend(conn, opcode, 0, fmt, ap); va_end(ap); return err; } int websock_close(struct websock_conn *conn, enum websock_scode scode, const char *fmt, ...) { va_list ap; int err; if (!conn) return EINVAL; if (!scode) fmt = NULL; va_start(ap, fmt); err = websock_vsend(conn, WEBSOCK_CLOSE, scode, fmt, ap); va_end(ap); if (!err) conn->state = CLOSING; return err; } const struct sa *websock_peer(const struct websock_conn *conn) { return conn ? &conn->peer : NULL; } struct tcp_conn *websock_tcp(const struct websock_conn *conn) { return conn ? conn->tc : NULL; } int websock_alloc(struct websock **sockp, websock_shutdown_h *shuth, void *arg) { struct websock *sock; if (!sockp) return EINVAL; sock = mem_zalloc(sizeof(*sock), sock_destructor); if (!sock) return ENOMEM; sock->shuth = shuth; sock->arg = arg; *sockp = sock; return 0; } void websock_shutdown(struct websock *sock) { if (!sock || sock->shutdown) return; sock->shutdown = true; mem_deref(sock); }