├── .gitignore ├── COPYING ├── Makefile.am ├── NEWS ├── README ├── autogen.sh ├── configure.ac ├── docs ├── libwfd.pc.in └── libwfd.sym ├── src ├── libwfd.h ├── rtsp_decoder.c ├── rtsp_tokenizer.c ├── shl_llog.h ├── shl_macro.h ├── shl_ring.c ├── shl_ring.h ├── shl_util.c ├── shl_util.h ├── wpa_ctrl.c └── wpa_parser.c └── test ├── test_common.h ├── test_rtsp.c └── test_wpa.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.la 2 | *.lo 3 | *.log 4 | *.o 5 | *.swp 6 | *.tar.xz 7 | *.trs 8 | .deps/ 9 | .dirstamp 10 | .libs/ 11 | Makefile 12 | Makefile.in 13 | aclocal.m4 14 | autom4te.cache/ 15 | build-aux/ 16 | config.h 17 | config.h.in 18 | config.h.in~ 19 | config.log 20 | config.status 21 | configure 22 | docs/libwfd.pc 23 | libtool 24 | m4/ 25 | stamp-h1 26 | test-suite.log 27 | test_rtsp 28 | test_wpa 29 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | = Authors = 2 | 3 | This software was written by: 4 | David Herrmann 5 | 6 | = Copyright Notice = 7 | 8 | This software is licensed under the terms of the MIT license. Please see each 9 | source file for the related copyright notice and license. 10 | 11 | If a file does not contain a copright notice, the following license shall 12 | apply: 13 | 14 | Copyright (c) 2013-2014 David Herrmann 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining 17 | a copy of this software and associated documentation files 18 | (the "Software"), to deal in the Software without restriction, including 19 | without limitation the rights to use, copy, modify, merge, publish, 20 | distribute, sublicense, and/or sell copies of the Software, and to 21 | permit persons to whom the Software is furnished to do so, subject to 22 | the following conditions: 23 | 24 | The above copyright notice and this permission notice shall be included 25 | in all copies or substantial portions of the Software. 26 | 27 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 28 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 29 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 30 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 31 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 32 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 33 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | 35 | == Third-Party Source == 36 | 37 | - none so far - 38 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # 2 | # libwfd - Global Makefile 3 | # Copyright (c) 2013-2014 David Herrmann 4 | # 5 | 6 | # 7 | # Library Version Numbers 8 | # 9 | 10 | LIBWFD_CURRENT = 0 11 | LIBWFD_REVISION = 0 12 | LIBWFD_AGE = 0 13 | 14 | # 15 | # Global Configurations and Initializations 16 | # 17 | 18 | ACLOCAL_AMFLAGS = -I m4 ${ACLOCAL_FLAGS} 19 | AM_MAKEFLAGS = --no-print-directory 20 | AUTOMAKE_OPTIONS = color-tests 21 | 22 | SUBDIRS = . 23 | 24 | .DELETE_ON_ERROR: 25 | 26 | include_HEADERS = 27 | EXTRA_DIST = \ 28 | README \ 29 | COPYING \ 30 | NEWS \ 31 | docs/libwfd.pc.in \ 32 | docs/libwfd.sym 33 | CLEANFILES = 34 | pkgconfigdir = $(libdir)/pkgconfig 35 | pkgconfig_DATA = 36 | TPHONY = 37 | 38 | TESTS = 39 | check_PROGRAMS = 40 | lib_LTLIBRARIES = 41 | noinst_LTLIBRARIES = 42 | 43 | # 44 | # Default CFlags 45 | # Make all files include "config.h" by default. This shouldn't cause any 46 | # problems and we cannot forget to include it anymore. 47 | # Also make the linker discard all unused symbols. 48 | # 49 | 50 | AM_CFLAGS = \ 51 | -Wall \ 52 | -pipe \ 53 | -fno-common \ 54 | -ffast-math \ 55 | -fdiagnostics-show-option \ 56 | -fno-strict-aliasing \ 57 | -fvisibility=hidden \ 58 | -ffunction-sections \ 59 | -fdata-sections 60 | AM_CPPFLAGS = \ 61 | -include $(top_builddir)/config.h \ 62 | -I $(srcdir)/src 63 | AM_LDFLAGS = \ 64 | -Wl,--as-needed \ 65 | -Wl,--gc-sections \ 66 | -Wl,-z,relro \ 67 | -Wl,-z,now 68 | 69 | # 70 | # SHL - Static Helper Library 71 | # The SHL subsystem contains several small code pieces used all over libwfd and 72 | # other applications. 73 | # 74 | 75 | noinst_LTLIBRARIES += libshl.la 76 | 77 | libshl_la_SOURCES = \ 78 | src/shl_llog.h \ 79 | src/shl_macro.h \ 80 | src/shl_ring.h \ 81 | src/shl_ring.c \ 82 | src/shl_util.h \ 83 | src/shl_util.c 84 | libshl_la_CPPFLAGS = $(AM_CPPFLAGS) 85 | libshl_la_LDFLAGS = $(AM_LDFLAGS) 86 | libshl_la_LIBADD = $(AM_LIBADD) 87 | 88 | # 89 | # libwfd 90 | # Main library build instructions 91 | # 92 | 93 | lib_LTLIBRARIES += libwfd.la 94 | include_HEADERS += src/libwfd.h 95 | pkgconfig_DATA += docs/libwfd.pc 96 | 97 | libwfd_la_SOURCES = \ 98 | src/libwfd.h \ 99 | src/rtsp_decoder.c \ 100 | src/rtsp_tokenizer.c \ 101 | src/wpa_ctrl.c \ 102 | src/wpa_parser.c 103 | libwfd_la_CPPFLAGS = $(AM_CPPFLAGS) 104 | libwfd_la_LIBADD = libshl.la 105 | EXTRA_libwfd_la_DEPENDENCIES = $(top_srcdir)/docs/libwfd.sym 106 | libwfd_la_LDFLAGS = \ 107 | $(AM_LDFLAGS) \ 108 | -version-info $(LIBWFD_CURRENT):$(LIBWFD_REVISION):$(LIBWFD_AGE) \ 109 | -Wl,--version-script="$(top_srcdir)/docs/libwfd.sym" 110 | 111 | # 112 | # Tests 113 | # 114 | 115 | tests = \ 116 | test_rtsp \ 117 | test_wpa 118 | 119 | if BUILD_HAVE_CHECK 120 | check_PROGRAMS += $(tests) 121 | TESTS += $(tests) 122 | endif 123 | 124 | test_sources = \ 125 | test/test_common.h 126 | test_libs = \ 127 | libwfd.la \ 128 | libshl.la \ 129 | $(CHECK_LIBS) 130 | test_cflags = \ 131 | $(AM_CPPFLAGS) \ 132 | $(CHECK_CFLAGS) 133 | test_lflags = \ 134 | $(AM_LDFLAGS) 135 | 136 | test_rtsp_SOURCES = test/test_rtsp.c $(test_sources) 137 | test_rtsp_CPPFLAGS = $(test_cflags) 138 | test_rtsp_LDADD = $(test_libs) 139 | test_rtsp_LDFLAGS = $(test_lflags) 140 | 141 | test_wpa_SOURCES = test/test_wpa.c $(test_sources) 142 | test_wpa_CPPFLAGS = $(test_cflags) 143 | test_wpa_LDADD = $(test_libs) 144 | test_wpa_LDFLAGS = $(test_lflags) 145 | 146 | # 147 | # Phony targets 148 | # 149 | 150 | .PHONY: $(TPHONY) 151 | 152 | # 153 | # Empty .SECONDARY target causes alle intermediate files to be treated as 154 | # secondary files. That is, they don't get deleted after make finished. 155 | # 156 | 157 | .SECONDARY: 158 | -------------------------------------------------------------------------------- /NEWS: -------------------------------------------------------------------------------- 1 | = libwfd Release News = 2 | 3 | CHANGES WITH 1: 4 | * TODO 5 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | = libwfd - Wifi-Display/Miracast Protocol Implementation = 2 | 3 | TODO 4 | 5 | Website: 6 | http://www.freedesktop.org/wiki/Software/miraclecast 7 | 8 | == Requirements == 9 | 10 | libwfd has no runtime requirements other than a ISO-C compatible C library. 11 | 12 | == Download == 13 | 14 | Released tarballs can be found at: 15 | http://www.freedesktop.org/software/miraclecast/releases 16 | 17 | == Install == 18 | 19 | To compile libwfd, run the standard autotools commands: 20 | $ test -f ./configure || NOCONFIGURE=1 ./autogen.sh 21 | $ ./configure --prefix=/usr 22 | $ make 23 | $ sudo make install 24 | To compile and run the test applications, use: 25 | $ make check 26 | 27 | == Documentation == 28 | 29 | WIP 30 | 31 | == License == 32 | 33 | This software is licensed under the terms of an MIT-like license. Please see 34 | ./COPYING for further information. 35 | 36 | == Contact == 37 | 38 | This software is maintained by: 39 | David Herrmann 40 | If you have any questions, do not hesitate to contact one of the maintainers. 41 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | srcdir=`dirname $0` 5 | test -z "$srcdir" && srcdir=. 6 | 7 | origdir=`pwd` 8 | cd $srcdir 9 | 10 | mkdir -p m4 11 | autoreconf -is --force 12 | 13 | cd $origdir 14 | 15 | if test -z "$NOCONFIGURE" ; then 16 | exec $srcdir/configure "$@" 17 | fi 18 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # 2 | # libwfd - build configuration script 3 | # Copyright (c) 2013-2014 David Herrmann 4 | # 5 | 6 | AC_PREREQ(2.68) 7 | 8 | AC_INIT([libwfd], 9 | [1], 10 | [http://www.freedesktop.org/wiki/Software/miraclecast], 11 | [libwfd], 12 | [http://www.freedesktop.org/wiki/Software/miraclecast]) 13 | AC_CONFIG_SRCDIR([src/libwfd.h]) 14 | AC_CONFIG_AUX_DIR([build-aux]) 15 | AC_CONFIG_MACRO_DIR([m4]) 16 | AC_CONFIG_HEADER(config.h) 17 | AC_USE_SYSTEM_EXTENSIONS 18 | AC_SYS_LARGEFILE 19 | AC_CANONICAL_HOST 20 | 21 | AM_INIT_AUTOMAKE([foreign 1.11 subdir-objects dist-xz no-dist-gzip tar-pax -Wall -Werror -Wno-portability]) 22 | AM_SILENT_RULES([yes]) 23 | 24 | AC_SUBST(PACKAGE_DESCRIPTION, ["Wifi-Display/Miracast Protocol Implementation"]) 25 | 26 | AC_PROG_CC 27 | AC_PROG_CC_C99 28 | AM_PROG_CC_C_O 29 | m4_ifdef([AM_PROG_AR], [AM_PROG_AR]) 30 | AC_PROG_SED 31 | AC_PROG_MKDIR_P 32 | AC_PROG_LN_S 33 | AC_PROG_GREP 34 | AC_PROG_AWK 35 | 36 | LT_PREREQ(2.2) 37 | LT_INIT 38 | 39 | # 40 | # Test for "check" which we use for our test-suite. If not found, we disable 41 | # all tests. 42 | # 43 | 44 | PKG_CHECK_MODULES([CHECK], [check], 45 | [have_check=yes], [have_check=no]) 46 | AC_SUBST(CHECK_CFLAGS) 47 | AC_SUBST(CHECK_LIBS) 48 | AM_CONDITIONAL([BUILD_HAVE_CHECK], [test "x$have_check" = "xyes"]) 49 | 50 | # 51 | # Makefile vars 52 | # After everything is configured, we create all makefiles. 53 | # 54 | 55 | AC_CONFIG_FILES([Makefile 56 | docs/libwfd.pc]) 57 | AC_OUTPUT 58 | 59 | # 60 | # Configuration output 61 | # Show configuration to the user so they can check whether everything was 62 | # configured as expected. 63 | # 64 | 65 | AC_MSG_NOTICE([Build configuration: 66 | 67 | prefix: $prefix 68 | exec-prefix: $exec_prefix 69 | libdir: $libdir 70 | includedir: $includedir 71 | 72 | Miscellaneous Options: 73 | building tests: $have_check 74 | 75 | Run "${MAKE-make}" to start compilation process]) 76 | -------------------------------------------------------------------------------- /docs/libwfd.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: libwfd 7 | Description: @PACKAGE_DESCRIPTION@ 8 | URL: @PACKAGE_URL@ 9 | Version: @PACKAGE_VERSION@ 10 | Libs: -L${libdir} -lwfd 11 | Cflags: -I${includedir} 12 | -------------------------------------------------------------------------------- /docs/libwfd.sym: -------------------------------------------------------------------------------- 1 | LIBWFD_1 { 2 | global: 3 | wfd_rtsp_tokenize; 4 | 5 | wfd_rtsp_method_get_name; 6 | wfd_rtsp_method_from_name; 7 | wfd_rtsp_status_is_valid; 8 | wfd_rtsp_status_get_base; 9 | wfd_rtsp_status_get_description; 10 | wfd_rtsp_header_get_name; 11 | wfd_rtsp_header_from_name; 12 | wfd_rtsp_header_from_name_n; 13 | 14 | wfd_rtsp_decoder_new; 15 | wfd_rtsp_decoder_free; 16 | wfd_rtsp_decoder_reset; 17 | wfd_rtsp_decoder_set_data; 18 | wfd_rtsp_decoder_get_data; 19 | wfd_rtsp_decoder_feed; 20 | 21 | wfd_wpa_ctrl_new; 22 | wfd_wpa_ctrl_ref; 23 | wfd_wpa_ctrl_unref; 24 | wfd_wpa_ctrl_set_data; 25 | wfd_wpa_ctrl_get_data; 26 | wfd_wpa_ctrl_open; 27 | wfd_wpa_ctrl_close; 28 | wfd_wpa_ctrl_is_open; 29 | wfd_wpa_ctrl_get_fd; 30 | wfd_wpa_ctrl_set_sigmask; 31 | wfd_wpa_ctrl_dispatch; 32 | wfd_wpa_ctrl_request; 33 | wfd_wpa_ctrl_request_ok; 34 | 35 | wfd_wpa_event_init; 36 | wfd_wpa_event_reset; 37 | wfd_wpa_event_parse; 38 | wfd_wpa_event_name; 39 | local: 40 | *; 41 | }; 42 | -------------------------------------------------------------------------------- /src/libwfd.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #ifndef WFD_LIBWFD_H 27 | #define WFD_LIBWFD_H 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #ifdef __cplusplus 35 | extern "C" { 36 | #endif 37 | 38 | #define WFD__PACKED __attribute__((__packed__)) 39 | 40 | /** 41 | * @defgroup wfd_p2p Wifi-Display Definitions for wifi-p2p 42 | * Definitions from the Wifi-Display specification for Wifi-P2P 43 | * 44 | * This section contains definitions and constants from the Wifi-Display 45 | * specification regarding Wifi-P2P. 46 | * 47 | * @{ 48 | */ 49 | 50 | /* 51 | * TODO: WFD 5.2.7 defines service-discovery frames. wpa-supplicant currently 52 | * does not support custom OUI fields. Fix this and then add support for 53 | * WFD service discovery. 54 | */ 55 | 56 | /* 57 | * IE elements 58 | */ 59 | 60 | #define WFD_IE_ID 0xdd 61 | #define WFD_IE_OUI_1_0 0x506f9a0a 62 | #define WFD_IE_DATA_MAX 251 63 | 64 | struct wfd_ie { 65 | uint8_t element_id; 66 | uint8_t length; 67 | uint32_t oui; 68 | uint8_t data[]; 69 | } WFD__PACKED; 70 | 71 | /* 72 | * IE subelements 73 | */ 74 | 75 | enum wfd_ie_sub_type { 76 | WFD_IE_SUB_DEV_INFO = 0, 77 | WFD_IE_SUB_ASSOC_BSSID = 1, 78 | WFD_IE_SUB_AUDIO_FORMATS = 2, 79 | WFD_IE_SUB_VIDEO_FORMATS = 3, 80 | WFD_IE_SUB_3D_FORMATS = 4, 81 | WFD_IE_SUB_CONTENT_PROTECT = 5, 82 | WFD_IE_SUB_COUPLED_SINK = 6, 83 | WFD_IE_SUB_EXT_CAP = 7, 84 | WFD_IE_SUB_LOCAL_IP = 8, 85 | WFD_IE_SUB_SESSION_INFO = 9, 86 | WFD_IE_SUB_ALT_MAC = 10, 87 | WFD_IE_SUB_NUM 88 | }; 89 | 90 | struct wfd_ie_sub { 91 | uint8_t subelement_id; 92 | uint16_t length; 93 | uint8_t data[]; 94 | } WFD__PACKED; 95 | 96 | /* 97 | * IE subelement device information 98 | */ 99 | 100 | /* role */ 101 | #define WFD_IE_SUB_DEV_INFO_ROLE_MASK 0x0003 102 | #define WFD_IE_SUB_DEV_INFO_SOURCE 0x0000 103 | #define WFD_IE_SUB_DEV_INFO_PRIMARY_SINK 0x0001 104 | #define WFD_IE_SUB_DEV_INFO_SECONDARY_SINK 0x0002 105 | #define WFD_IE_SUB_DEV_INFO_DUAL_ROLE 0x0003 106 | 107 | /* coupled sink as source */ 108 | #define WFD_IE_SUB_DEV_INFO_SRC_COUPLED_SINK_MASK 0x0004 109 | #define WFD_IE_SUB_DEV_INFO_SRC_NO_COUPLED_SINK 0x0000 110 | #define WFD_IE_SUB_DEV_INFO_SRC_CAN_COUPLED_SINK 0x0004 111 | 112 | /* coupled sink as sink */ 113 | #define WFD_IE_SUB_DEV_INFO_SINK_COUPLED_SINK_MASK 0x0008 114 | #define WFD_IE_SUB_DEV_INFO_SINK_NO_COUPLED_SINK 0x0000 115 | #define WFD_IE_SUB_DEV_INFO_SINK_CAN_COUPLED_SINK 0x0008 116 | 117 | /* availability for session establishment */ 118 | #define WFD_IE_SUB_DEV_INFO_AVAILABLE_MASK 0x0030 119 | #define WFD_IE_SUB_DEV_INFO_NOT_AVAILABLE 0x0000 120 | #define WFD_IE_SUB_DEV_INFO_AVAILABLE 0x0010 121 | 122 | /* WFD service discovery */ 123 | #define WFD_IE_SUB_DEV_INFO_WSD_MASK 0x0040 124 | #define WFD_IE_SUB_DEV_INFO_NO_WSD 0x0000 125 | #define WFD_IE_SUB_DEV_INFO_CAN_WSD 0x0040 126 | 127 | /* preferred connectivity */ 128 | #define WFD_IE_SUB_DEV_INFO_PC_MASK 0x0080 129 | #define WFD_IE_SUB_DEV_INFO_PREFER_P2P 0x0000 130 | #define WFD_IE_SUB_DEV_INFO_PREFER_TDLS 0x0080 131 | 132 | /* content protection */ 133 | #define WFD_IE_SUB_DEV_INFO_CP_MASK 0x0100 134 | #define WFD_IE_SUB_DEV_INFO_NO_CP 0x0000 135 | #define WFD_IE_SUB_DEV_INFO_CAN_CP 0x0100 136 | 137 | /* separate time-sync */ 138 | #define WFD_IE_SUB_DEV_INFO_TIME_SYNC_MASK 0x0200 139 | #define WFD_IE_SUB_DEV_INFO_NO_TIME_SYNC 0x0000 140 | #define WFD_IE_SUB_DEV_INFO_CAN_TIME_SYNC 0x0200 141 | 142 | /* no audio */ 143 | #define WFD_IE_SUB_DEV_INFO_NO_AUDIO_MASK 0x0400 144 | #define WFD_IE_SUB_DEV_INFO_CAN_AUDIO 0x0000 145 | #define WFD_IE_SUB_DEV_INFO_NO_AUDIO 0x0400 146 | 147 | /* audio only */ 148 | #define WFD_IE_SUB_DEV_INFO_AUDIO_ONLY_MASK 0x0800 149 | #define WFD_IE_SUB_DEV_INFO_NO_AUDIO_ONLY 0x0000 150 | #define WFD_IE_SUB_DEV_INFO_AUDIO_ONLY 0x0800 151 | 152 | /* persistent TLDS */ 153 | #define WFD_IE_SUB_DEV_INFO_PERSIST_TLDS_MASK 0x1000 154 | #define WFD_IE_SUB_DEV_INFO_NO_PERSIST_TLDS 0x0000 155 | #define WFD_IE_SUB_DEV_INFO_PERSIST_TLDS 0x1000 156 | 157 | /* persistent TLDS group re-invoke */ 158 | #define WFD_IE_SUB_DEV_INFO_TLDS_REINVOKE_MASK 0x2000 159 | #define WFD_IE_SUB_DEV_INFO_NO_TLDS_REINVOKE 0x0000 160 | #define WFD_IE_SUB_DEV_INFO_TLDS_REINVOKE 0x2000 161 | 162 | #define WFD_IE_SUB_DEV_INFO_DEFAULT_PORT 7236 163 | 164 | struct wfd_ie_sub_dev_info { 165 | uint16_t dev_info; 166 | uint16_t ctrl_port; 167 | uint16_t max_throughput; 168 | } WFD__PACKED; 169 | 170 | /* 171 | * IE subelement associated BSSID 172 | */ 173 | 174 | struct wfd_ie_sub_assoc_bssid { 175 | uint8_t bssid[6]; 176 | } WFD__PACKED; 177 | 178 | /* 179 | * IE subelement audio formats 180 | */ 181 | 182 | /* lpcm modes; 2C_16_48000 is mandatory */ 183 | #define WFD_IE_SUB_AUDIO_FORMATS_LPCM_2C_16_44100 0x00000001 184 | #define WFD_IE_SUB_AUDIO_FORMATS_LPCM_2C_16_48000 0x00000002 185 | 186 | /* aac modes */ 187 | #define WFD_IE_SUB_AUDIO_FORMATS_AAC_2C_16_48000 0x00000001 188 | #define WFD_IE_SUB_AUDIO_FORMATS_AAC_4C_16_48000 0x00000002 189 | #define WFD_IE_SUB_AUDIO_FORMATS_AAC_6C_16_48000 0x00000004 190 | #define WFD_IE_SUB_AUDIO_FORMATS_AAC_8C_16_48000 0x00000008 191 | 192 | /* ac3 modes */ 193 | #define WFD_IE_SUB_AUDIO_FORMATS_AC3_2C_16_48000 0x00000001 194 | #define WFD_IE_SUB_AUDIO_FORMATS_AC3_4C_16_48000 0x00000002 195 | #define WFD_IE_SUB_AUDIO_FORMATS_AC3_6C_16_48000 0x00000004 196 | 197 | /* audio latency; encoded in multiples of 5ms */ 198 | #define WFD_IE_SUB_AUDIO_FORMATS_UNKNOWN_LATENCY 0x00 199 | #define WFD_IE_SUB_AUDIO_FORMATS_LATENCY_FROM_MS(_ms) \ 200 | (((_ms) + 4ULL) / 5ULL) 201 | 202 | struct wfd_ie_sub_audio_formats { 203 | uint32_t lpcm_modes; 204 | uint8_t lpcm_latency; 205 | uint32_t aac_modes; 206 | uint8_t aac_latency; 207 | uint32_t ac3_modes; 208 | uint8_t ac3_latency; 209 | } WFD__PACKED; 210 | 211 | /* 212 | * IE subelement video formats 213 | * Multiple video-subelements are allowed, one for each supported 264 profile. 214 | */ 215 | 216 | /* cea modes; required cea modes; 640x480@p60 is always required; if you 217 | * support higher resolutions at p60 or p50, you also must support 720x480@p60 218 | * or 720x576@p50 respectively */ 219 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_640_480_P60 0x00000001 220 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_720_480_P60 0x00000002 221 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_720_480_I60 0x00000004 222 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_720_576_P50 0x00000008 223 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_720_576_I50 0x00000010 224 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1280_720_P30 0x00000020 225 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1280_720_P60 0x00000040 226 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_P30 0x00000080 227 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_P60 0x00000100 228 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_I60 0x00000200 229 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1280_720_P25 0x00000400 230 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1280_720_P50 0x00000800 231 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_P25 0x00001000 232 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_P50 0x00002000 233 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_I50 0x00004000 234 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1280_720_P24 0x00008000 235 | #define WFD_IE_SUB_VIDEO_FORMATS_CEA_1920_1080_P24 0x00010000 236 | 237 | /* vesa modes; if you support higher refresh-rates, you must also support 238 | * *all* lower rates of the same mode */ 239 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_800_600_P30 0x00000001 240 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_800_600_P60 0x00000002 241 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1024_768_P30 0x00000004 242 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1024_768_P60 0x00000008 243 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1152_864_P30 0x00000010 244 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1152_864_P60 0x00000020 245 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1280_768_P30 0x00000040 246 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1280_768_P60 0x00000080 247 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1280_800_P30 0x00000100 248 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1280_800_P60 0x00000200 249 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1360_768_P30 0x00000400 250 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1360_768_P60 0x00000800 251 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1366_768_P30 0x00001000 252 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1366_768_P60 0x00002000 253 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1280_1024_P30 0x00004000 254 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1280_1024_P60 0x00008000 255 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1400_1050_P30 0x00010000 256 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1400_1050_P60 0x00020000 257 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1440_900_P30 0x00040000 258 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1440_900_P60 0x00080000 259 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1600_900_P30 0x00100000 260 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1600_900_P60 0x00200000 261 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1600_1200_P30 0x00400000 262 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1600_1200_P60 0x00800000 263 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1680_1024_P30 0x01000000 264 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1680_1024_P60 0x02000000 265 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1680_1050_P30 0x04000000 266 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1680_1050_P60 0x08000000 267 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1920_1200_P30 0x10000000 268 | #define WFD_IE_SUB_VIDEO_FORMATS_VESA_1920_1200_P60 0x20000000 269 | 270 | /* hh modes (handheld devices) */ 271 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_800_480_P30 0x00000000 272 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_800_480_P60 0x00000000 273 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_854_480_P30 0x00000000 274 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_854_480_P60 0x00000000 275 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_864_480_P30 0x00000000 276 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_864_480_P60 0x00000000 277 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_640_360_P30 0x00000000 278 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_640_360_P60 0x00000000 279 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_960_540_P30 0x00000000 280 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_960_540_P60 0x00000000 281 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_848_480_P30 0x00000000 282 | #define WFD_IE_SUB_VIDEO_FORMATS_HH_848_480_P60 0x00000000 283 | 284 | /* native mode; table */ 285 | #define WFD_IE_SUB_VIDEO_FORMATS_NATIVE_MODE_TABLE_MASK 0x03 286 | #define WFD_IE_SUB_VIDEO_FORMATS_NATIVE_MODE_CEA_TABLE 0x00 287 | #define WFD_IE_SUB_VIDEO_FORMATS_NATIVE_MODE_VESA_TABLE 0x01 288 | #define WFD_IE_SUB_VIDEO_FORMATS_NATIVE_MODE_HH_TABLE 0x02 289 | 290 | /* native mode; index */ 291 | #define WFD_IE_SUB_VIDEO_FORMATS_NATIVE_MODE_IDX_MASK 0xfc 292 | #define WFD_IE_SUB_VIDEO_FORMATS_NATIVE_MODE_IDX_SHIFT 3 293 | 294 | /* h264 profiles; base-profile / high-profile; mostly only one bit allowed */ 295 | #define WFD_IE_SUB_VIDEO_FORMATS_PROFILE_CBP 0x01 296 | #define WFD_IE_SUB_VIDEO_FORMATS_PROFILE_CHP 0x02 297 | 298 | /* max h264 level; mostly only one bit allowed */ 299 | #define WFD_IE_SUB_VIDEO_FORMATS_H264_LEVEL_3_1 0x01 300 | #define WFD_IE_SUB_VIDEO_FORMATS_H264_LEVEL_3_2 0x02 301 | #define WFD_IE_SUB_VIDEO_FORMATS_H264_LEVEL_4_0 0x04 302 | #define WFD_IE_SUB_VIDEO_FORMATS_H264_LEVEL_4_1 0x08 303 | #define WFD_IE_SUB_VIDEO_FORMATS_H264_LEVEL_4_2 0x10 304 | 305 | /* display latency; encoded in multiples of 5ms */ 306 | #define WFD_IE_SUB_VIDEO_FORMATS_UNKNOWN_LATENCY 0x00 307 | #define WFD_IE_SUB_VIDEO_FORMATS_LATENCY_FROM_MS(_ms) \ 308 | (((_ms) + 4ULL) / 5ULL) 309 | 310 | /* smallest slice size expressed in number of macro-blocks or 0x0 */ 311 | #define WFD_IE_SUB_VIDEO_FORMATS_NO_SLICES 0x0000 312 | 313 | /* if no slices allowed, this can be set on slice_env */ 314 | #define WFD_IE_SUB_VIDEO_FORMATS_NO_SLICE_ENC 0x0000 315 | 316 | /* max number of slices per picture MINUS 1 (0 not allowed) */ 317 | #define WFD_IE_SUB_VIDEO_FORMATS_SLICE_ENC_MAX_MASK 0x03ff 318 | #define WFD_IE_SUB_VIDEO_FORMATS_SLICE_ENC_MAX_SHIFT 0 319 | 320 | /* ratio of max-slice-size to be used and slice_min field (0 not allowed) */ 321 | #define WFD_IE_SUB_VIDEO_FORMATS_SLICE_ENC_RATIO_MASK 0x0c00 322 | #define WFD_IE_SUB_VIDEO_FORMATS_SLICE_ENC_RATIO_SHIFT 10 323 | 324 | /* frame skipping */ 325 | #define WFD_IE_SUB_VIDEO_FORMATS_NO_FRAME_SKIP 0x00 326 | #define WFD_IE_SUB_VIDEO_FORMATS_CAN_FRAME_SKIP 0x01 327 | 328 | #define WFD_IE_SUB_VIDEO_FORMATS_FRAME_SKIP_MAX_I_MASK 0x0e 329 | #define WFD_IE_SUB_VIDEO_FORMATS_FRAME_SKIP_MAX_I_SHIFT 1 330 | #define WFD_IE_SUB_VIDEO_FORMATS_FRAME_SKIP_MAX_I_ANY 0x00 331 | 332 | #define WFD_IE_SUB_VIDEO_FORMATS_FRAME_SKIP_NO_DYN 0x00 333 | #define WFD_IE_SUB_VIDEO_FORMATS_FRAME_SKIP_CAN_DYN 0x10 334 | 335 | struct wfd_ie_sub_video_formats { 336 | uint32_t cea_modes; 337 | uint32_t vesa_modes; 338 | uint32_t hh_modes; 339 | uint8_t native_mode; 340 | uint8_t h264_profile; 341 | uint8_t h264_max_level; 342 | uint8_t latency; 343 | uint16_t slice_min; 344 | uint16_t slice_enc; 345 | uint8_t frame_skip; 346 | } WFD__PACKED; 347 | 348 | /* 349 | * IE subelement 3d formats 350 | * Multiple 3d-subelements are allowed, one for each supported h264 profile. 351 | */ 352 | 353 | /* 3d cpabilities; required modes; 1920x540/540@p24 is always required; if you 354 | * support higher resolutions at p60 or p50, you also must support 355 | * 1280x360/360@p60 or 1280x360/360@p50 respectively */ 356 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_540_540_P24 0x0000000000000001 357 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_360_360_P60 0x0000000000000002 358 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_360_360_P50 0x0000000000000004 359 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_1080_P24_P24 0x0000000000000008 360 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_P60_P60 0x0000000000000010 361 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_P30_P30 0x0000000000000020 362 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_P50_P50 0x0000000000000040 363 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_P25_P25 0x0000000000000080 364 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_1080_45_1080_P24 0x0000000000000100 365 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_30_720_P60 0x0000000000000200 366 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_30_720_P30 0x0000000000000400 367 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_30_720_P50 0x0000000000000800 368 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_720_30_720_P25 0x0000000000001000 369 | #define WFD_IE_SUB_3D_FORMATS_CAP_960_960_X_1080_I60 0x0000000000002000 370 | #define WFD_IE_SUB_3D_FORMATS_CAP_960_960_X_1080_I50 0x0000000000004000 371 | #define WFD_IE_SUB_3D_FORMATS_CAP_640_X_240_240_P60 0x0000000000008000 372 | #define WFD_IE_SUB_3D_FORMATS_CAP_320_320_X_480_P60 0x0000000000010000 373 | #define WFD_IE_SUB_3D_FORMATS_CAP_720_X_240_240_P60 0x0000000000020000 374 | #define WFD_IE_SUB_3D_FORMATS_CAP_360_360_X_480_P60 0x0000000000040000 375 | #define WFD_IE_SUB_3D_FORMATS_CAP_720_X_288_288_P50 0x0000000000080000 376 | #define WFD_IE_SUB_3D_FORMATS_CAP_360_360_X_576_P50 0x0000000000100000 377 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_360_360_P24 0x0000000000200000 378 | #define WFD_IE_SUB_3D_FORMATS_CAP_640_640_X_720_P24 0x0000000000400000 379 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_360_360_P25 0x0000000000800000 380 | #define WFD_IE_SUB_3D_FORMATS_CAP_640_640_X_720_P25 0x0000000001000000 381 | #define WFD_IE_SUB_3D_FORMATS_CAP_1280_X_360_360_P30 0x0000000002000000 382 | #define WFD_IE_SUB_3D_FORMATS_CAP_640_640_X_720_P30 0x0000000004000000 383 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_540_540_P30 0x0000000008000000 384 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_540_540_P50 0x0000000010000000 385 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_540_540_P60 0x0000000020000000 386 | #define WFD_IE_SUB_3D_FORMATS_CAP_640_640_X_720_P50 0x0000000040000000 387 | #define WFD_IE_SUB_3D_FORMATS_CAP_640_640_X_720_P60 0x0000000080000000 388 | #define WFD_IE_SUB_3D_FORMATS_CAP_960_960_X_1080_P24 0x0000000100000000 389 | #define WFD_IE_SUB_3D_FORMATS_CAP_960_960_X_1080_P50 0x0000000200000000 390 | #define WFD_IE_SUB_3D_FORMATS_CAP_960_960_X_1080_P60 0x0000000400000000 391 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_1080_45_1080_P30 0x0000000800000000 392 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_1080_45_1080_I50 0x0000001000000000 393 | #define WFD_IE_SUB_3D_FORMATS_CAP_1920_X_1080_45_1080_I60 0x0000002000000000 394 | 395 | struct wfd_ie_sub_3d_formats { 396 | uint64_t capabilities; 397 | uint8_t native_mode; /* same as video_formats.native_mode */ 398 | uint8_t h264_profile; /* same as video_formats.h264_profile */ 399 | uint8_t h264_max_level; /* same as video_formats.h264_max_level */ 400 | uint8_t latency; /* same as video_formats.latency */ 401 | uint16_t slice_min; /* same as video_formats.slice_min */ 402 | uint16_t slice_enc; /* same as video_formats.slice_enc */ 403 | uint8_t frame_skip; /* same as video_formats.frame_skip */ 404 | } WFD__PACKED; 405 | 406 | /* 407 | * IE subelement content protection 408 | */ 409 | 410 | /* HDCP 2.0 */ 411 | #define WFD_IE_SUB_CONTENT_PROTECT_HDCP_2_0_MASK 0x01 412 | #define WFD_IE_SUB_CONTENT_PROTECT_NO_HDCP_2_0 0x00 413 | #define WFD_IE_SUB_CONTENT_PROTECT_CAN_HDCP_2_0 0x01 414 | 415 | /* HDCP 2.1; if set, you must also set HDCP 2.0 */ 416 | #define WFD_IE_SUB_CONTENT_PROTECT_HDCP_2_1_MASK 0x02 417 | #define WFD_IE_SUB_CONTENT_PROTECT_NO_HDCP_2_1 0x00 418 | #define WFD_IE_SUB_CONTENT_PROTECT_CAN_HDCP_2_1 0x02 419 | 420 | struct wfd_ie_sub_content_protect { 421 | uint8_t flags; 422 | } WFD__PACKED; 423 | 424 | /* 425 | * IE subelement coupled sink information 426 | */ 427 | 428 | /* status */ 429 | #define WFD_IE_SUB_COUPLED_SINK_STATUS_MASK 0x03 430 | #define WFD_IE_SUB_COUPLED_SINK_NOT_COUPLED 0x00 431 | #define WFD_IE_SUB_COUPLED_SINK_COUPLED 0x01 432 | #define WFD_IE_SUB_COUPLED_SINK_COUPLE_TEARDOWN 0x02 433 | 434 | struct wfd_ie_sub_coupled_sink { 435 | uint8_t status; 436 | uint8_t mac[6]; 437 | } WFD__PACKED; 438 | 439 | /* 440 | * IE subelement extended capabilities 441 | */ 442 | 443 | /* UIBC */ 444 | #define WFD_IE_SUB_EXT_CAP_UIBC_MASK 0x01 445 | #define WFD_IE_SUB_EXT_CAP_NO_UIBC 0x00 446 | #define WFD_IE_SUB_EXT_CAP_CAN_UIBC 0x01 447 | 448 | /* I2C */ 449 | #define WFD_IE_SUB_EXT_CAP_I2C_MASK 0x02 450 | #define WFD_IE_SUB_EXT_CAP_NO_I2C 0x00 451 | #define WFD_IE_SUB_EXT_CAP_CAN_I2C 0x02 452 | 453 | /* Preferred Mode */ 454 | #define WFD_IE_SUB_EXT_CAP_PREFER_MODE_MASK 0x04 455 | #define WFD_IE_SUB_EXT_CAP_NO_PREFER_MODE 0x00 456 | #define WFD_IE_SUB_EXT_CAP_CAN_PREFER_MODE 0x04 457 | 458 | /* Standby */ 459 | #define WFD_IE_SUB_EXT_CAP_STANDBY_MASK 0x08 460 | #define WFD_IE_SUB_EXT_CAP_NO_STANDBY 0x00 461 | #define WFD_IE_SUB_EXT_CAP_CAN_STANDBY 0x08 462 | 463 | /* Persistend TDLS */ 464 | #define WFD_IE_SUB_EXT_CAP_PERSIST_TDLS_MASK 0x10 465 | #define WFD_IE_SUB_EXT_CAP_NO_PERSIST_TDLS 0x00 466 | #define WFD_IE_SUB_EXT_CAP_CAN_PERSIST_TDLS 0x10 467 | 468 | /* Persistend TDLS BSSID */ 469 | #define WFD_IE_SUB_EXT_CAP_PERSIST_TDLS_BSSID_MASK 0x20 470 | #define WFD_IE_SUB_EXT_CAP_NO_PERSIST_TDLS_BSSID 0x00 471 | #define WFD_IE_SUB_EXT_CAP_CAN_PERSIST_TDLS_BSSID 0x20 472 | 473 | struct wfd_ie_sub_ext_cap { 474 | uint16_t flags; 475 | } WFD__PACKED; 476 | 477 | /* 478 | * IE subelement local ip 479 | */ 480 | 481 | #define WFD_IE_SUB_LOCAL_IP_IPV4 0x01 482 | 483 | struct wfd_ie_sub_local_ip { 484 | uint8_t version; 485 | uint8_t ip[4]; 486 | } WFD__PACKED; 487 | 488 | /* 489 | * IE subelement session information 490 | */ 491 | 492 | /* real payload is actually an array of this object for each device */ 493 | struct wfd_ie_sub_session_info { 494 | uint8_t length; /* fixed: 23 == (sizeof(this) - 1) */ 495 | uint8_t mac[6]; 496 | uint8_t bssid[6]; 497 | uint16_t dev_info; /* same as dev_info.dev_info */ 498 | uint16_t max_throughput; /* same as dev_info.max_throughput */ 499 | uint8_t coupled_status; /* same as coupled_sink.status */ 500 | uint8_t coupled_mac[6]; /* same as coupled_sink.mac */ 501 | } WFD__PACKED; 502 | 503 | /* 504 | * IE subelement alternative mac 505 | */ 506 | 507 | struct wfd_ie_sub_alt_mac { 508 | uint8_t mac[6]; 509 | } WFD__PACKED; 510 | 511 | /** @} */ 512 | 513 | /** 514 | * @defgroup wfd_wpa WPA-Supplicant API 515 | * Helper API to deal with wpa_supplicant for WFD devices 516 | * 517 | * On linux wpa_supplicant is the de-facto standard for wifi-handling. It 518 | * provides a standard-compiant supplicant implementation with a custom API for 519 | * applications. This API implements helpers to deal with this daemon and get 520 | * wifi-P2P connections for WFD working. 521 | * 522 | * @{ 523 | */ 524 | 525 | /* wpa ctrl */ 526 | 527 | struct wfd_wpa_ctrl; 528 | 529 | typedef void (*wfd_wpa_ctrl_event_t) (struct wfd_wpa_ctrl *wpa, void *data, 530 | void *buf, size_t len); 531 | 532 | int wfd_wpa_ctrl_new(wfd_wpa_ctrl_event_t event_fn, void *data, 533 | struct wfd_wpa_ctrl **out); 534 | void wfd_wpa_ctrl_ref(struct wfd_wpa_ctrl *wpa); 535 | void wfd_wpa_ctrl_unref(struct wfd_wpa_ctrl *wpa); 536 | 537 | void wfd_wpa_ctrl_set_data(struct wfd_wpa_ctrl *wpa, void *data); 538 | void *wfd_wpa_ctrl_get_data(struct wfd_wpa_ctrl *wpa); 539 | 540 | int wfd_wpa_ctrl_open(struct wfd_wpa_ctrl *wpa, const char *ctrl_path); 541 | void wfd_wpa_ctrl_close(struct wfd_wpa_ctrl *wpa); 542 | bool wfd_wpa_ctrl_is_open(struct wfd_wpa_ctrl *wpa); 543 | 544 | int wfd_wpa_ctrl_get_fd(struct wfd_wpa_ctrl *wpa); 545 | void wfd_wpa_ctrl_set_sigmask(struct wfd_wpa_ctrl *wpa, const sigset_t *mask); 546 | int wfd_wpa_ctrl_dispatch(struct wfd_wpa_ctrl *wpa, int timeout); 547 | 548 | int wfd_wpa_ctrl_request(struct wfd_wpa_ctrl *wpa, const void *cmd, 549 | size_t cmd_len, void *reply, size_t *reply_len, 550 | int timeout); 551 | int wfd_wpa_ctrl_request_ok(struct wfd_wpa_ctrl *wpa, const void *cmd, 552 | size_t cmd_len, int timeout); 553 | 554 | /* wpa parser */ 555 | 556 | enum wfd_wpa_event_type { 557 | WFD_WPA_EVENT_UNKNOWN, 558 | WFD_WPA_EVENT_CTRL_EVENT_SCAN_STARTED, 559 | WFD_WPA_EVENT_CTRL_EVENT_TERMINATING, 560 | WFD_WPA_EVENT_AP_STA_CONNECTED, 561 | WFD_WPA_EVENT_AP_STA_DISCONNECTED, 562 | WFD_WPA_EVENT_P2P_DEVICE_FOUND, 563 | WFD_WPA_EVENT_P2P_DEVICE_LOST, 564 | WFD_WPA_EVENT_P2P_FIND_STOPPED, 565 | WFD_WPA_EVENT_P2P_GO_NEG_REQUEST, 566 | WFD_WPA_EVENT_P2P_GO_NEG_SUCCESS, 567 | WFD_WPA_EVENT_P2P_GO_NEG_FAILURE, 568 | WFD_WPA_EVENT_P2P_GROUP_FORMATION_SUCCESS, 569 | WFD_WPA_EVENT_P2P_GROUP_FORMATION_FAILURE, 570 | WFD_WPA_EVENT_P2P_GROUP_STARTED, 571 | WFD_WPA_EVENT_P2P_GROUP_REMOVED, 572 | WFD_WPA_EVENT_P2P_PROV_DISC_SHOW_PIN, 573 | WFD_WPA_EVENT_P2P_PROV_DISC_ENTER_PIN, 574 | WFD_WPA_EVENT_P2P_PROV_DISC_PBC_REQ, 575 | WFD_WPA_EVENT_P2P_PROV_DISC_PBC_RESP, 576 | WFD_WPA_EVENT_P2P_SERV_DISC_REQ, 577 | WFD_WPA_EVENT_P2P_SERV_DISC_RESP, 578 | WFD_WPA_EVENT_P2P_INVITATION_RECEIVED, 579 | WFD_WPA_EVENT_P2P_INVITATION_RESULT, 580 | WFD_WPA_EVENT_CNT, 581 | }; 582 | 583 | enum wfd_wpa_event_priority { 584 | WFD_WPA_EVENT_P_MSGDUMP, 585 | WFD_WPA_EVENT_P_DEBUG, 586 | WFD_WPA_EVENT_P_INFO, 587 | WFD_WPA_EVENT_P_WARNING, 588 | WFD_WPA_EVENT_P_ERROR, 589 | WFD_WPA_EVENT_P_CNT 590 | }; 591 | 592 | enum wfd_wpa_event_role { 593 | WFD_WPA_EVENT_ROLE_GO, 594 | WFD_WPA_EVENT_ROLE_CLIENT, 595 | WFD_WPA_EVENT_ROLE_CNT, 596 | }; 597 | 598 | #define WFD_WPA_EVENT_MAC_STRLEN 18 599 | 600 | struct wfd_wpa_event { 601 | unsigned int type; 602 | unsigned int priority; 603 | char *raw; 604 | 605 | union wfd_wpa_event_payload { 606 | struct wfd_wpa_event_ap_sta_connected { 607 | char mac[WFD_WPA_EVENT_MAC_STRLEN]; 608 | char iface[WFD_WPA_EVENT_MAC_STRLEN]; 609 | } ap_sta_connected; 610 | 611 | struct wfd_wpa_event_ap_sta_disconnected { 612 | char mac[WFD_WPA_EVENT_MAC_STRLEN]; 613 | char iface[WFD_WPA_EVENT_MAC_STRLEN]; 614 | } ap_sta_disconnected; 615 | 616 | struct wfd_wpa_event_p2p_device_found { 617 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 618 | char *name; 619 | } p2p_device_found; 620 | 621 | struct wfd_wpa_event_p2p_device_lost { 622 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 623 | } p2p_device_lost; 624 | 625 | struct wfd_wpa_event_p2p_go_neg_success { 626 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 627 | char peer_iface[WFD_WPA_EVENT_MAC_STRLEN]; 628 | unsigned int role; 629 | } p2p_go_neg_success; 630 | 631 | struct wfd_wpa_event_p2p_group_started { 632 | char go_mac[WFD_WPA_EVENT_MAC_STRLEN]; 633 | unsigned int role; 634 | char *ifname; 635 | } p2p_group_started; 636 | 637 | struct wfd_wpa_event_p2p_group_removed { 638 | unsigned int role; 639 | char *ifname; 640 | } p2p_group_removed; 641 | 642 | struct wfd_wpa_event_p2p_prov_disc_show_pin { 643 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 644 | char *pin; 645 | } p2p_prov_disc_show_pin; 646 | 647 | struct wfd_wpa_event_p2p_prov_disc_enter_pin { 648 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 649 | } p2p_prov_disc_enter_pin; 650 | 651 | struct wfd_wpa_event_p2p_prov_disc_pbc_req { 652 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 653 | } p2p_prov_disc_pbc_req; 654 | 655 | struct wfd_wpa_event_p2p_prov_disc_pbc_resp { 656 | char peer_mac[WFD_WPA_EVENT_MAC_STRLEN]; 657 | } p2p_prov_disc_pbc_resp; 658 | } p; 659 | }; 660 | 661 | void wfd_wpa_event_init(struct wfd_wpa_event *ev); 662 | void wfd_wpa_event_reset(struct wfd_wpa_event *ev); 663 | int wfd_wpa_event_parse(struct wfd_wpa_event *ev, const char *event); 664 | const char *wfd_wpa_event_name(unsigned int type); 665 | 666 | /** @} */ 667 | 668 | /** 669 | * @defgroup wfd_rtsp Wifi-Display API for RTSP 670 | * API for the Wifi-Display specification regarding RTSP 671 | * 672 | * This section contains definitions and constants from the Wifi-Display 673 | * specification regarding RTSP and provides a basic API to parse and handle 674 | * WFD-RTSP messages. 675 | * 676 | * Note that this parser is neither fast nor optimized for memory-usage. RTSP is 677 | * not a high-throughput protocol, therefore, this API is made for easy use, not 678 | * low-latency and high-throughput. This shouldn't hurt at all. If you use RTSP 679 | * for huge payloads, you're doing it wrong. Note that this does *NOT* apply to 680 | * RTSP data messages, which allow combining RTP streams with RTSP. These 681 | * messages are handled properly and in a fast manner by this decoder. 682 | * 683 | * This decoder tries to provide a very convenient API that parses basic types 684 | * but still allows the caller to get access to any unknown lines. This way, 685 | * standard-conformant parsers can be easily written, while extensions are still 686 | * allowed. 687 | * 688 | * @{ 689 | */ 690 | 691 | /** 692 | * wfd_rtsp_tokenize - Tokenize an RTSP line 693 | * @line: input line 694 | * @len: length of the line or -1 if zero-terminated 695 | * @out: storage pointer for tokens 696 | * 697 | * This tokenizes a single RTSP line. It splits the given input-line by RTSP 698 | * tokens and does some very basic line-parsing. A pointer to the tokenized 699 | * string is stored in @out and must be freed by the caller. The tokenized 700 | * string is separated by binary-0 and you can use wfd_rtsp_next_token() to jump 701 | * to the next token. Binary-0 is not allowed in RTSP lines so this is safe. 702 | * 703 | * This function returns the number of tokens parsed. On error, a negative errno 704 | * code is returned and @out is left untouched. 705 | */ 706 | ssize_t wfd_rtsp_tokenize(const char *line, ssize_t len, char **out); 707 | 708 | /** 709 | * wfd_rtsp_next_token - Return next RTSP token 710 | * @tokens: toknized RTSP line 711 | * 712 | * This can only be used in combination with wfd_rtsp_tokenize. It returns the next 713 | * token from the tokenized line. 714 | */ 715 | static inline char *wfd_rtsp_next_token(char *tokens) 716 | { 717 | return tokens + strlen(tokens) + 1; 718 | } 719 | 720 | /* RTSP messages */ 721 | 722 | typedef void (*wfd_rtsp_log_t) (void *data, 723 | const char *file, 724 | int line, 725 | const char *func, 726 | const char *subs, 727 | unsigned int sev, 728 | const char *format, 729 | va_list args); 730 | 731 | enum wfd_rtsp_msg_type { 732 | WFD_RTSP_MSG_UNKNOWN, 733 | 734 | WFD_RTSP_MSG_REQUEST, 735 | WFD_RTSP_MSG_RESPONSE, 736 | 737 | WFD_RTSP_MSG_CNT 738 | }; 739 | 740 | enum wfd_rtsp_method_type { 741 | WFD_RTSP_METHOD_UNKNOWN, 742 | 743 | WFD_RTSP_METHOD_ANNOUNCE, 744 | WFD_RTSP_METHOD_DESCRIBE, 745 | WFD_RTSP_METHOD_GET_PARAMETER, 746 | WFD_RTSP_METHOD_OPTIONS, 747 | WFD_RTSP_METHOD_PAUSE, 748 | WFD_RTSP_METHOD_PLAY, 749 | WFD_RTSP_METHOD_RECORD, 750 | WFD_RTSP_METHOD_REDIRECT, 751 | WFD_RTSP_METHOD_SETUP, 752 | WFD_RTSP_METHOD_SET_PARAMETER, 753 | WFD_RTSP_METHOD_TEARDOWN, 754 | 755 | WFD_RTSP_METHOD_CNT 756 | }; 757 | 758 | const char *wfd_rtsp_method_get_name(unsigned int method); 759 | unsigned int wfd_rtsp_method_from_name(const char *method); 760 | 761 | enum wfd_rtsp_status_code { 762 | WFD_RTSP_STATUS_CONTINUE = 100, 763 | 764 | WFD_RTSP_STATUS_OK = 200, 765 | WFD_RTSP_STATUS_CREATED, 766 | 767 | WFD_RTSP_STATUS_LOW_ON_STORAGE_SPACE = 250, 768 | 769 | WFD_RTSP_STATUS_MULTIPLE_CHOICES = 300, 770 | WFD_RTSP_STATUS_MOVED_PERMANENTLY, 771 | WFD_RTSP_STATUS_MOVED_TEMPORARILY, 772 | WFD_RTSP_STATUS_SEE_OTHER, 773 | WFD_RTSP_STATUS_NOT_MODIFIED, 774 | WFD_RTSP_STATUS_USE_PROXY, 775 | 776 | WFD_RTSP_STATUS_BAD_REQUEST = 400, 777 | WFD_RTSP_STATUS_UNAUTHORIZED, 778 | WFD_RTSP_STATUS_PAYMENT_REQUIRED, 779 | WFD_RTSP_STATUS_FORBIDDEN, 780 | WFD_RTSP_STATUS_NOT_FOUND, 781 | WFD_RTSP_STATUS_METHOD_NOT_ALLOWED, 782 | WFD_RTSP_STATUS_NOT_ACCEPTABLE, 783 | WFD_RTSP_STATUS_PROXY_AUTHENTICATION_REQUIRED, 784 | WFD_RTSP_STATUS_REQUEST_TIMEOUT, 785 | WFD_RTSP_STATUS__PLACEHOLDER__1, 786 | WFD_RTSP_STATUS_GONE, 787 | WFD_RTSP_STATUS_LENGTH_REQUIRED, 788 | WFD_RTSP_STATUS_PRECONDITION_FAILED, 789 | WFD_RTSP_STATUS_REQUEST_ENTITY_TOO_LARGE, 790 | WFD_RTSP_STATUS_REQUEST_URI_TOO_LARGE, 791 | WFD_RTSP_STATUS_UNSUPPORTED_MEDIA_TYPE, 792 | 793 | WFD_RTSP_STATUS_PARAMETER_NOT_UNDERSTOOD = 451, 794 | WFD_RTSP_STATUS_CONFERENCE_NOT_FOUND, 795 | WFD_RTSP_STATUS_NOT_ENOUGH_BANDWIDTH, 796 | WFD_RTSP_STATUS_SESSION_NOT_FOUND, 797 | WFD_RTSP_STATUS_METHOD_NOT_VALID_IN_THIS_STATE, 798 | WFD_RTSP_STATUS_HEADER_FIELD_NOT_VALID_FOR_RESOURCE, 799 | WFD_RTSP_STATUS_INVALID_RANGE, 800 | WFD_RTSP_STATUS_PARAMETER_IS_READ_ONLY, 801 | WFD_RTSP_STATUS_AGGREGATE_OPERATION_NOT_ALLOWED, 802 | WFD_RTSP_STATUS_ONLY_AGGREGATE_OPERATION_ALLOWED, 803 | WFD_RTSP_STATUS_UNSUPPORTED_TRANSPORT, 804 | WFD_RTSP_STATUS_DESTINATION_UNREACHABLE, 805 | 806 | WFD_RTSP_STATUS_INTERNAL_SERVER_ERROR = 500, 807 | WFD_RTSP_STATUS_NOT_IMPLEMENTED, 808 | WFD_RTSP_STATUS_BAD_GATEWAY, 809 | WFD_RTSP_STATUS_SERVICE_UNAVAILABLE, 810 | WFD_RTSP_STATUS_GATEWAY_TIMEOUT, 811 | WFD_RTSP_STATUS_WFD_RTSP_VERSION_NOT_SUPPORTED, 812 | 813 | WFD_RTSP_STATUS_OPTION_NOT_SUPPORTED = 551, 814 | 815 | WFD_RTSP_STATUS_CNT 816 | }; 817 | 818 | bool wfd_rtsp_status_is_valid(unsigned int status); 819 | unsigned int wfd_rtsp_status_get_base(unsigned int status); 820 | const char *wfd_rtsp_status_get_description(unsigned int status); 821 | 822 | enum wfd_rtsp_header_type { 823 | WFD_RTSP_HEADER_UNKNOWN, 824 | 825 | WFD_RTSP_HEADER_ACCEPT, 826 | WFD_RTSP_HEADER_ACCEPT_ENCODING, 827 | WFD_RTSP_HEADER_ACCEPT_LANGUAGE, 828 | WFD_RTSP_HEADER_ALLOW, 829 | WFD_RTSP_HEADER_AUTHORIZATION, 830 | WFD_RTSP_HEADER_BANDWIDTH, 831 | WFD_RTSP_HEADER_BLOCKSIZE, 832 | WFD_RTSP_HEADER_CACHE_CONTROL, 833 | WFD_RTSP_HEADER_CONFERENCE, 834 | WFD_RTSP_HEADER_CONNECTION, 835 | WFD_RTSP_HEADER_CONTENT_BASE, 836 | WFD_RTSP_HEADER_CONTENT_ENCODING, 837 | WFD_RTSP_HEADER_CONTENT_LANGUAGE, 838 | WFD_RTSP_HEADER_CONTENT_LENGTH, 839 | WFD_RTSP_HEADER_CONTENT_LOCATION, 840 | WFD_RTSP_HEADER_CONTENT_TYPE, 841 | WFD_RTSP_HEADER_CSEQ, 842 | WFD_RTSP_HEADER_DATE, 843 | WFD_RTSP_HEADER_EXPIRES, 844 | WFD_RTSP_HEADER_FROM, 845 | WFD_RTSP_HEADER_HOST, 846 | WFD_RTSP_HEADER_IF_MATCH, 847 | WFD_RTSP_HEADER_IF_MODIFIED_SINCE, 848 | WFD_RTSP_HEADER_LAST_MODIFIED, 849 | WFD_RTSP_HEADER_LOCATION, 850 | WFD_RTSP_HEADER_PROXY_AUTHENTICATE, 851 | WFD_RTSP_HEADER_PROXY_REQUIRE, 852 | WFD_RTSP_HEADER_PUBLIC, 853 | WFD_RTSP_HEADER_RANGE, 854 | WFD_RTSP_HEADER_REFERER, 855 | WFD_RTSP_HEADER_RETRY_AFTER, 856 | WFD_RTSP_HEADER_REQUIRE, 857 | WFD_RTSP_HEADER_RTP_INFO, 858 | WFD_RTSP_HEADER_SCALE, 859 | WFD_RTSP_HEADER_SPEED, 860 | WFD_RTSP_HEADER_SERVER, 861 | WFD_RTSP_HEADER_SESSION, 862 | WFD_RTSP_HEADER_TIMESTAMP, 863 | WFD_RTSP_HEADER_TRANSPORT, 864 | WFD_RTSP_HEADER_UNSUPPORTED, 865 | WFD_RTSP_HEADER_USER_AGENT, 866 | WFD_RTSP_HEADER_VARY, 867 | WFD_RTSP_HEADER_VIA, 868 | WFD_RTSP_HEADER_WWW_AUTHENTICATE, 869 | 870 | WFD_RTSP_HEADER_CNT 871 | }; 872 | 873 | const char *wfd_rtsp_header_get_name(unsigned int header); 874 | unsigned int wfd_rtsp_header_from_name(const char *header); 875 | unsigned int wfd_rtsp_header_from_name_n(const char *header, size_t len); 876 | 877 | struct wfd_rtsp_msg { 878 | unsigned int type; 879 | 880 | struct wfd_rtsp_msg_id { 881 | char *line; 882 | size_t length; 883 | 884 | union { 885 | struct { 886 | char *method; 887 | unsigned int type; 888 | char *uri; 889 | unsigned int major; 890 | unsigned int minor; 891 | } request; 892 | 893 | struct { 894 | unsigned int major; 895 | unsigned int minor; 896 | unsigned int status; 897 | char *phrase; 898 | } response; 899 | }; 900 | } id; 901 | 902 | struct wfd_rtsp_msg_header { 903 | size_t count; 904 | char **lines; 905 | size_t *lengths; 906 | 907 | union { 908 | size_t content_length; 909 | unsigned long cseq; 910 | }; 911 | } headers[WFD_RTSP_HEADER_CNT]; 912 | 913 | struct wfd_rtsp_msg_entity { 914 | void *value; 915 | size_t size; 916 | } entity; 917 | }; 918 | 919 | /* rtsp decoder */ 920 | 921 | struct wfd_rtsp_decoder; 922 | 923 | enum wfd_rtsp_decoder_event_type { 924 | WFD_RTSP_DECODER_MSG, 925 | WFD_RTSP_DECODER_DATA, 926 | WFD_RTSP_DECODER_ERROR, 927 | }; 928 | 929 | struct wfd_rtsp_decoder_event { 930 | unsigned int type; 931 | 932 | union { 933 | struct wfd_rtsp_msg *msg; 934 | struct wfd_rtsp_decoder_data { 935 | uint8_t channel; 936 | uint16_t size; 937 | uint8_t *value; 938 | } data; 939 | struct wfd_rtsp_decoder_error { 940 | void *data; 941 | size_t length; 942 | } error; 943 | }; 944 | }; 945 | 946 | typedef int (*wfd_rtsp_decoder_event_t) (struct wfd_rtsp_decoder *dec, 947 | void *data, 948 | struct wfd_rtsp_decoder_event *event); 949 | 950 | int wfd_rtsp_decoder_new(wfd_rtsp_decoder_event_t event_fn, 951 | void *data, 952 | wfd_rtsp_log_t log_fn, 953 | void *log_data, 954 | struct wfd_rtsp_decoder **out); 955 | void wfd_rtsp_decoder_free(struct wfd_rtsp_decoder *dec); 956 | void wfd_rtsp_decoder_reset(struct wfd_rtsp_decoder *dec); 957 | 958 | void wfd_rtsp_decoder_set_data(struct wfd_rtsp_decoder *dec, void *data); 959 | void *wfd_rtsp_decoder_get_data(struct wfd_rtsp_decoder *dec); 960 | 961 | int wfd_rtsp_decoder_feed(struct wfd_rtsp_decoder *dec, 962 | const void *buf, 963 | size_t len); 964 | 965 | /** @} */ 966 | 967 | #ifdef __cplusplus 968 | } 969 | #endif 970 | 971 | #endif /* WFD_LIBWFD_H */ 972 | -------------------------------------------------------------------------------- /src/rtsp_decoder.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "libwfd.h" 32 | #include "shl_llog.h" 33 | #include "shl_macro.h" 34 | #include "shl_ring.h" 35 | #include "shl_util.h" 36 | 37 | enum state { 38 | STATE_NEW, 39 | STATE_HEADER, 40 | STATE_HEADER_QUOTE, 41 | STATE_HEADER_NL, 42 | STATE_BODY, 43 | STATE_DATA_HEAD, 44 | STATE_DATA_BODY, 45 | }; 46 | 47 | struct wfd_rtsp_decoder { 48 | wfd_rtsp_decoder_event_t event_fn; 49 | void *data; 50 | llog_submit_t llog; 51 | void *llog_data; 52 | 53 | struct wfd_rtsp_msg msg; 54 | 55 | struct shl_ring buf; 56 | size_t buflen; 57 | unsigned int state; 58 | char last_chr; 59 | size_t remaining_body; 60 | 61 | uint8_t data_channel; 62 | size_t data_size; 63 | 64 | bool quoted : 1; 65 | bool dead : 1; 66 | }; 67 | 68 | /* 69 | * Lookup Tables 70 | */ 71 | 72 | static const char *method_names[] = { 73 | [WFD_RTSP_METHOD_ANNOUNCE] = "ANNOUNCE", 74 | [WFD_RTSP_METHOD_DESCRIBE] = "DESCRIBE", 75 | [WFD_RTSP_METHOD_GET_PARAMETER] = "GET_PARAMETER", 76 | [WFD_RTSP_METHOD_OPTIONS] = "OPTIONS", 77 | [WFD_RTSP_METHOD_PAUSE] = "PAUSE", 78 | [WFD_RTSP_METHOD_PLAY] = "PLAY", 79 | [WFD_RTSP_METHOD_RECORD] = "RECORD", 80 | [WFD_RTSP_METHOD_REDIRECT] = "REDIRECT", 81 | [WFD_RTSP_METHOD_SETUP] = "SETUP", 82 | [WFD_RTSP_METHOD_SET_PARAMETER] = "SET_PARAMETER", 83 | [WFD_RTSP_METHOD_TEARDOWN] = "TEARDOWN", 84 | [WFD_RTSP_METHOD_CNT] = NULL, 85 | }; 86 | 87 | _shl_public_ 88 | const char *wfd_rtsp_method_get_name(unsigned int method) 89 | { 90 | if (method >= SHL_ARRAY_LENGTH(method_names)) 91 | return NULL; 92 | 93 | return method_names[method]; 94 | } 95 | 96 | _shl_public_ 97 | unsigned int wfd_rtsp_method_from_name(const char *method) 98 | { 99 | size_t i; 100 | 101 | for (i = 0; i < SHL_ARRAY_LENGTH(method_names); ++i) 102 | if (method_names[i] && !strcasecmp(method, method_names[i])) 103 | return i; 104 | 105 | return WFD_RTSP_METHOD_UNKNOWN; 106 | } 107 | 108 | _shl_public_ 109 | bool wfd_rtsp_status_is_valid(unsigned int status) 110 | { 111 | return status >= 100 && status < 600; 112 | } 113 | 114 | _shl_public_ 115 | unsigned int wfd_rtsp_status_get_base(unsigned int status) 116 | { 117 | switch (status) { 118 | case 100 ... 199: 119 | return 100; 120 | case 200 ... 299: 121 | return 200; 122 | case 300 ... 399: 123 | return 300; 124 | case 400 ... 499: 125 | return 400; 126 | case 500 ... 599: 127 | return 500; 128 | default: 129 | return 600; 130 | } 131 | } 132 | 133 | static const char *status_descriptions[] = { 134 | [WFD_RTSP_STATUS_CONTINUE] = "Continue", 135 | 136 | [WFD_RTSP_STATUS_OK] = "OK", 137 | [WFD_RTSP_STATUS_CREATED] = "Created", 138 | 139 | [WFD_RTSP_STATUS_LOW_ON_STORAGE_SPACE] = "Low on Storage Space", 140 | 141 | [WFD_RTSP_STATUS_MULTIPLE_CHOICES] = "Multiple Choices", 142 | [WFD_RTSP_STATUS_MOVED_PERMANENTLY] = "Moved Permanently", 143 | [WFD_RTSP_STATUS_MOVED_TEMPORARILY] = "Moved Temporarily", 144 | [WFD_RTSP_STATUS_SEE_OTHER] = "See Other", 145 | [WFD_RTSP_STATUS_NOT_MODIFIED] = "Not Modified", 146 | [WFD_RTSP_STATUS_USE_PROXY] = "Use Proxy", 147 | 148 | [WFD_RTSP_STATUS_BAD_REQUEST] = "Bad Request", 149 | [WFD_RTSP_STATUS_UNAUTHORIZED] = "Unauthorized", 150 | [WFD_RTSP_STATUS_PAYMENT_REQUIRED] = "Payment Required", 151 | [WFD_RTSP_STATUS_FORBIDDEN] = "Forbidden", 152 | [WFD_RTSP_STATUS_NOT_FOUND] = "Not Found", 153 | [WFD_RTSP_STATUS_METHOD_NOT_ALLOWED] = "Method not Allowed", 154 | [WFD_RTSP_STATUS_NOT_ACCEPTABLE] = "Not Acceptable", 155 | [WFD_RTSP_STATUS_PROXY_AUTHENTICATION_REQUIRED] = "Proxy Authentication Required", 156 | [WFD_RTSP_STATUS_REQUEST_TIMEOUT] = "Request Time-out", 157 | [WFD_RTSP_STATUS_GONE] = "Gone", 158 | [WFD_RTSP_STATUS_LENGTH_REQUIRED] = "Length Required", 159 | [WFD_RTSP_STATUS_PRECONDITION_FAILED] = "Precondition Failed", 160 | [WFD_RTSP_STATUS_REQUEST_ENTITY_TOO_LARGE] = "Request Entity Too Large", 161 | [WFD_RTSP_STATUS_REQUEST_URI_TOO_LARGE] = "Request-URI too Large", 162 | [WFD_RTSP_STATUS_UNSUPPORTED_MEDIA_TYPE] = "Unsupported Media Type", 163 | 164 | [WFD_RTSP_STATUS_PARAMETER_NOT_UNDERSTOOD] = "Parameter not Understood", 165 | [WFD_RTSP_STATUS_CONFERENCE_NOT_FOUND] = "Conference not Found", 166 | [WFD_RTSP_STATUS_NOT_ENOUGH_BANDWIDTH] = "Not Enough Bandwidth", 167 | [WFD_RTSP_STATUS_SESSION_NOT_FOUND] = "Session not Found", 168 | [WFD_RTSP_STATUS_METHOD_NOT_VALID_IN_THIS_STATE] = "Method not Valid in this State", 169 | [WFD_RTSP_STATUS_HEADER_FIELD_NOT_VALID_FOR_RESOURCE] = "Header Field not Valid for Resource", 170 | [WFD_RTSP_STATUS_INVALID_RANGE] = "Invalid Range", 171 | [WFD_RTSP_STATUS_PARAMETER_IS_READ_ONLY] = "Parameter is Read-only", 172 | [WFD_RTSP_STATUS_AGGREGATE_OPERATION_NOT_ALLOWED] = "Aggregate Operation not Allowed", 173 | [WFD_RTSP_STATUS_ONLY_AGGREGATE_OPERATION_ALLOWED] = "Only Aggregate Operation Allowed", 174 | [WFD_RTSP_STATUS_UNSUPPORTED_TRANSPORT] = "Unsupported Transport", 175 | [WFD_RTSP_STATUS_DESTINATION_UNREACHABLE] = "Destination Unreachable", 176 | 177 | [WFD_RTSP_STATUS_INTERNAL_SERVER_ERROR] = "Internal Server Error", 178 | [WFD_RTSP_STATUS_NOT_IMPLEMENTED] = "Not Implemented", 179 | [WFD_RTSP_STATUS_BAD_GATEWAY] = "Bad Gateway", 180 | [WFD_RTSP_STATUS_SERVICE_UNAVAILABLE] = "Service Unavailable", 181 | [WFD_RTSP_STATUS_GATEWAY_TIMEOUT] = "Gateway Time-out", 182 | [WFD_RTSP_STATUS_WFD_RTSP_VERSION_NOT_SUPPORTED] = "RTSP Version not Supported", 183 | 184 | [WFD_RTSP_STATUS_OPTION_NOT_SUPPORTED] = "Option not Supported", 185 | 186 | [WFD_RTSP_STATUS_CNT] = NULL, 187 | }; 188 | 189 | _shl_public_ 190 | const char *wfd_rtsp_status_get_description(unsigned int status) 191 | { 192 | if (status >= SHL_ARRAY_LENGTH(status_descriptions)) 193 | return NULL; 194 | 195 | return status_descriptions[status]; 196 | } 197 | 198 | static const char *header_names[] = { 199 | [WFD_RTSP_HEADER_ACCEPT] = "Accept", 200 | [WFD_RTSP_HEADER_ACCEPT_ENCODING] = "Accept-Encoding", 201 | [WFD_RTSP_HEADER_ACCEPT_LANGUAGE] = "Accept-Language", 202 | [WFD_RTSP_HEADER_ALLOW] = "Allow", 203 | [WFD_RTSP_HEADER_AUTHORIZATION] = "Authorization", 204 | [WFD_RTSP_HEADER_BANDWIDTH] = "Bandwidth", 205 | [WFD_RTSP_HEADER_BLOCKSIZE] = "Blocksize", 206 | [WFD_RTSP_HEADER_CACHE_CONTROL] = "Cache-Control", 207 | [WFD_RTSP_HEADER_CONFERENCE] = "Conference", 208 | [WFD_RTSP_HEADER_CONNECTION] = "Connection", 209 | [WFD_RTSP_HEADER_CONTENT_BASE] = "Content-Base", 210 | [WFD_RTSP_HEADER_CONTENT_ENCODING] = "Content-Encoding", 211 | [WFD_RTSP_HEADER_CONTENT_LANGUAGE] = "Content-Language", 212 | [WFD_RTSP_HEADER_CONTENT_LENGTH] = "Content-Length", 213 | [WFD_RTSP_HEADER_CONTENT_LOCATION] = "Content-Location", 214 | [WFD_RTSP_HEADER_CONTENT_TYPE] = "Content-Type", 215 | [WFD_RTSP_HEADER_CSEQ] = "CSeq", 216 | [WFD_RTSP_HEADER_DATE] = "Date", 217 | [WFD_RTSP_HEADER_EXPIRES] = "Expires", 218 | [WFD_RTSP_HEADER_FROM] = "From", 219 | [WFD_RTSP_HEADER_HOST] = "Host", 220 | [WFD_RTSP_HEADER_IF_MATCH] = "If-Match", 221 | [WFD_RTSP_HEADER_IF_MODIFIED_SINCE] = "If-Modified-Since", 222 | [WFD_RTSP_HEADER_LAST_MODIFIED] = "Last-Modified", 223 | [WFD_RTSP_HEADER_LOCATION] = "Location", 224 | [WFD_RTSP_HEADER_PROXY_AUTHENTICATE] = "Proxy-Authenticate", 225 | [WFD_RTSP_HEADER_PROXY_REQUIRE] = "Proxy-Require", 226 | [WFD_RTSP_HEADER_PUBLIC] = "Public", 227 | [WFD_RTSP_HEADER_RANGE] = "Range", 228 | [WFD_RTSP_HEADER_REFERER] = "Referer", 229 | [WFD_RTSP_HEADER_RETRY_AFTER] = "Retry-After", 230 | [WFD_RTSP_HEADER_REQUIRE] = "Require", 231 | [WFD_RTSP_HEADER_RTP_INFO] = "RTP-Info", 232 | [WFD_RTSP_HEADER_SCALE] = "Scale", 233 | [WFD_RTSP_HEADER_SPEED] = "Speed", 234 | [WFD_RTSP_HEADER_SERVER] = "Server", 235 | [WFD_RTSP_HEADER_SESSION] = "Session", 236 | [WFD_RTSP_HEADER_TIMESTAMP] = "Timestamp", 237 | [WFD_RTSP_HEADER_TRANSPORT] = "Transport", 238 | [WFD_RTSP_HEADER_UNSUPPORTED] = "Unsupported", 239 | [WFD_RTSP_HEADER_USER_AGENT] = "User-Agent", 240 | [WFD_RTSP_HEADER_VARY] = "Vary", 241 | [WFD_RTSP_HEADER_VIA] = "Via", 242 | [WFD_RTSP_HEADER_WWW_AUTHENTICATE] = "WWW-Authenticate", 243 | [WFD_RTSP_HEADER_CNT] = NULL, 244 | }; 245 | 246 | _shl_public_ 247 | const char *wfd_rtsp_header_get_name(unsigned int header) 248 | { 249 | if (header >= SHL_ARRAY_LENGTH(header_names)) 250 | return NULL; 251 | 252 | return header_names[header]; 253 | } 254 | 255 | _shl_public_ 256 | unsigned int wfd_rtsp_header_from_name(const char *header) 257 | { 258 | size_t i; 259 | 260 | for (i = 0; i < SHL_ARRAY_LENGTH(header_names); ++i) 261 | if (header_names[i] && !strcasecmp(header, header_names[i])) 262 | return i; 263 | 264 | return WFD_RTSP_HEADER_UNKNOWN; 265 | } 266 | 267 | _shl_public_ 268 | unsigned int wfd_rtsp_header_from_name_n(const char *header, size_t len) 269 | { 270 | size_t i; 271 | 272 | for (i = 0; i < SHL_ARRAY_LENGTH(header_names); ++i) 273 | if (header_names[i] && 274 | !strncasecmp(header, header_names[i], len) && 275 | !header_names[i][len]) 276 | return i; 277 | 278 | return WFD_RTSP_HEADER_UNKNOWN; 279 | } 280 | 281 | /* 282 | * Helpers 283 | */ 284 | 285 | static void msg_clear_id(struct wfd_rtsp_msg *msg) 286 | { 287 | switch (msg->type) { 288 | case WFD_RTSP_MSG_REQUEST: 289 | free(msg->id.request.method); 290 | free(msg->id.request.uri); 291 | break; 292 | case WFD_RTSP_MSG_RESPONSE: 293 | free(msg->id.response.phrase); 294 | break; 295 | } 296 | 297 | free(msg->id.line); 298 | shl_zero(msg->id); 299 | } 300 | 301 | static void msg_clear_headers(struct wfd_rtsp_msg *msg) 302 | { 303 | struct wfd_rtsp_msg_header *h; 304 | size_t i, j; 305 | 306 | for (i = 0; i < WFD_RTSP_HEADER_CNT; ++i) { 307 | h = &msg->headers[i]; 308 | 309 | for (j = 0; j < h->count; ++j) 310 | free(h->lines[j]); 311 | 312 | free(h->lines); 313 | free(h->lengths); 314 | } 315 | 316 | shl_zero(msg->headers); 317 | } 318 | 319 | static void msg_clear_entity(struct wfd_rtsp_msg *msg) 320 | { 321 | free(msg->entity.value); 322 | shl_zero(msg->entity); 323 | } 324 | 325 | static void msg_clear(struct wfd_rtsp_msg *msg) 326 | { 327 | msg_clear_id(msg); 328 | msg_clear_headers(msg); 329 | msg_clear_entity(msg); 330 | } 331 | 332 | static int decoder_call(struct wfd_rtsp_decoder *dec, 333 | struct wfd_rtsp_decoder_event *ev) 334 | { 335 | return dec->event_fn(dec, dec->data, ev); 336 | } 337 | 338 | static int decoder_submit(struct wfd_rtsp_decoder *dec) 339 | { 340 | struct wfd_rtsp_decoder_event ev = { }; 341 | int r; 342 | 343 | ev.type = WFD_RTSP_DECODER_MSG; 344 | ev.msg = &dec->msg; 345 | r = decoder_call(dec, &ev); 346 | msg_clear(&dec->msg); 347 | 348 | return r; 349 | } 350 | 351 | static int decoder_submit_data(struct wfd_rtsp_decoder *dec, uint8_t *p) 352 | { 353 | struct wfd_rtsp_decoder_event ev = { }; 354 | 355 | ev.type = WFD_RTSP_DECODER_DATA; 356 | ev.data.channel = dec->data_channel; 357 | ev.data.size = dec->data_size; 358 | ev.data.value = p; 359 | return decoder_call(dec, &ev); 360 | } 361 | 362 | /* 363 | * Header ID-line Handling 364 | * This parses both, the REQUEST and RESPONSE lines of an RTSP method. It is 365 | * always the first header line and defines the type of message. If it is 366 | * unrecognized, we set it to UNKNOWN. 367 | * Note that regardless of the ID-line, all following lines are parsed as 368 | * generic headers followed by an optional entity. 369 | */ 370 | 371 | static int decoder_parse_request(struct wfd_rtsp_decoder *dec, 372 | char *line, 373 | size_t len) 374 | { 375 | unsigned int major, minor; 376 | size_t cmdlen, urllen; 377 | char *next, *prev, *cmd, *url; 378 | 379 | /* Requests look like this: 380 | * RTSP/. 381 | * We try to match here, but accept invalid commands. is 382 | * never parsed (it can become pretty complex if done properly). */ 383 | 384 | next = line; 385 | 386 | /* parse */ 387 | cmd = line; 388 | next = strchr(next, ' '); 389 | if (!next || next == cmd) 390 | goto error; 391 | cmdlen = next - cmd; 392 | 393 | /* skip " " */ 394 | ++next; 395 | 396 | /* parse */ 397 | url = next; 398 | next = strchr(next, ' '); 399 | if (!next || next == url) 400 | goto error; 401 | urllen = next - url; 402 | 403 | /* skip " " */ 404 | ++next; 405 | 406 | /* parse "RTSP/" */ 407 | if (strncasecmp(next, "RTSP/", 5)) 408 | goto error; 409 | next += 5; 410 | 411 | /* parse "%u" */ 412 | prev = next; 413 | shl_atoi_u(prev, 10, (const char**)&next, &major); 414 | if (next == prev || *next != '.') 415 | goto error; 416 | 417 | /* skip "." */ 418 | ++next; 419 | 420 | /* parse "%u" */ 421 | prev = next; 422 | shl_atoi_u(prev, 10, (const char**)&next, &minor); 423 | if (next == prev || *next) 424 | goto error; 425 | 426 | cmd = strndup(cmd, cmdlen); 427 | url = strndup(url, urllen); 428 | if (!cmd || !url) { 429 | free(cmd); 430 | return llog_ENOMEM(dec); 431 | } 432 | 433 | dec->msg.type = WFD_RTSP_MSG_REQUEST; 434 | dec->msg.id.line = line; 435 | dec->msg.id.length = len; 436 | dec->msg.id.request.method = cmd; 437 | dec->msg.id.request.type = wfd_rtsp_method_from_name(cmd); 438 | dec->msg.id.request.uri = url; 439 | dec->msg.id.request.major = major; 440 | dec->msg.id.request.minor = minor; 441 | 442 | return 0; 443 | 444 | error: 445 | /* Invalid request line.. Set type to UNKNOWN and let the caller deal 446 | * with it. We will not try to send any error to avoid triggering 447 | * another error if the remote side doesn't understand proper RTSP (or 448 | * if our implementation is buggy). */ 449 | dec->msg.type = WFD_RTSP_MSG_UNKNOWN; 450 | dec->msg.id.line = line; 451 | dec->msg.id.length = len; 452 | return 0; 453 | } 454 | 455 | static int decoder_parse_response(struct wfd_rtsp_decoder *dec, 456 | char *line, 457 | size_t len) 458 | { 459 | unsigned int major, minor, code; 460 | char *prev, *next, *str; 461 | 462 | /* Responses look like this: 463 | * RTSP/. 464 | * RTSP/%u.%u %u %s 465 | * We first parse the RTSP version and code. Everything appended to 466 | * this is optional and represents the error string. */ 467 | 468 | /* skip "RTSP/", already parsed by parent */ 469 | next = &line[5]; 470 | 471 | /* parse "%u" */ 472 | prev = next; 473 | shl_atoi_u(prev, 10, (const char**)&next, &major); 474 | if (next == prev || *next != '.') 475 | goto error; 476 | 477 | /* skip "." */ 478 | ++next; 479 | 480 | /* parse "%u" */ 481 | prev = next; 482 | shl_atoi_u(prev, 10, (const char**)&next, &minor); 483 | if (next == prev || *next != ' ') 484 | goto error; 485 | 486 | /* skip " " */ 487 | ++next; 488 | 489 | /* parse: %u */ 490 | prev = next; 491 | shl_atoi_u(prev, 10, (const char**)&next, &code); 492 | if (next == prev) 493 | goto error; 494 | if (*next && *next != ' ') 495 | goto error; 496 | 497 | /* skip " " */ 498 | if (*next) 499 | ++next; 500 | 501 | /* parse: %s */ 502 | str = strdup(next); 503 | if (!str) 504 | return llog_ENOMEM(dec); 505 | 506 | dec->msg.type = WFD_RTSP_MSG_RESPONSE; 507 | dec->msg.id.line = line; 508 | dec->msg.id.length = len; 509 | dec->msg.id.response.major = major; 510 | dec->msg.id.response.minor = minor; 511 | dec->msg.id.response.status = code; 512 | dec->msg.id.response.phrase = str; 513 | 514 | return 0; 515 | 516 | error: 517 | /* Couldn't parse line. Avoid sending an error message as we could 518 | * trigger another error and end up in an endless error loop. Instead, 519 | * set message type to UNKNOWN and let the caller deal with it. */ 520 | dec->msg.type = WFD_RTSP_MSG_UNKNOWN; 521 | dec->msg.id.line = line; 522 | dec->msg.id.length = len; 523 | return 0; 524 | } 525 | 526 | static int decoder_parse_id(struct wfd_rtsp_decoder *dec, char *line, size_t len) 527 | { 528 | if (!strncasecmp(line, "RTSP/", 5)) 529 | return decoder_parse_response(dec, line, len); 530 | else 531 | return decoder_parse_request(dec, line, len); 532 | } 533 | 534 | /* 535 | * RTSP Header Parser 536 | * This parses RTSP header lines. These follow the ID-line and may contain 537 | * arbitrary additional information. Note that we parse any kind of message that 538 | * we cannot identify as UNKNOWN. Thus, the caller can implement arbitrary 539 | * additional parsers. 540 | * 541 | * Furthermore, if a header-line cannot be parsed correctly, even though the 542 | * header-type is known, we still add it as UNKNOWN. Therefore, if the caller 543 | * implements extensions to *known* header lines, it still needs to go through 544 | * all unknown lines. It's not enough to go through the lines of the given type. 545 | */ 546 | 547 | static int header_append(struct wfd_rtsp_msg_header *h, char *line, size_t len) 548 | { 549 | char **tlines; 550 | size_t *tlengths; 551 | size_t num; 552 | 553 | num = h->count + 2; 554 | 555 | tlines = realloc(h->lines, num * sizeof(*h->lines)); 556 | if (!tlines) 557 | return -ENOMEM; 558 | h->lines = tlines; 559 | 560 | tlengths = realloc(h->lengths, num * sizeof(*h->lengths)); 561 | if (!tlengths) 562 | return -ENOMEM; 563 | h->lengths = tlengths; 564 | 565 | h->lines[h->count] = line; 566 | h->lengths[h->count] = len; 567 | ++h->count; 568 | h->lines[h->count] = NULL; 569 | h->lengths[h->count] = 0; 570 | 571 | return 0; 572 | } 573 | 574 | static int decoder_add_unknown_line(struct wfd_rtsp_decoder *dec, 575 | char *line, 576 | size_t len) 577 | { 578 | struct wfd_rtsp_msg_header *h; 579 | int r; 580 | 581 | /* Cannot parse header line. Append it at the end of the line-array 582 | * of type UNKNOWN. Let the caller deal with it. */ 583 | 584 | h = &dec->msg.headers[WFD_RTSP_HEADER_UNKNOWN]; 585 | r = header_append(h, line, len); 586 | return r < 0 ? llog_ERR(dec, r) : 0; 587 | } 588 | 589 | static int decoder_parse_content_length(struct wfd_rtsp_decoder *dec, 590 | char *line, 591 | size_t len, 592 | char *tokens, 593 | size_t num) 594 | { 595 | struct wfd_rtsp_msg_header *h; 596 | int r; 597 | size_t clen; 598 | char *next; 599 | 600 | h = &dec->msg.headers[WFD_RTSP_HEADER_CONTENT_LENGTH]; 601 | 602 | r = shl_atoi_z(tokens, 10, (const char**)&next, &clen); 603 | if (r < 0 || *next) { 604 | /* Screwed content-length line? We cannot recover from that as 605 | * the attached entity is of unknown length. Abort.. */ 606 | return -EINVAL; 607 | } 608 | 609 | r = header_append(h, line, len); 610 | if (r < 0) 611 | return llog_ERR(dec, r); 612 | 613 | /* overwrite previous lengths */ 614 | h->content_length = clen; 615 | dec->remaining_body = clen; 616 | 617 | return 0; 618 | } 619 | 620 | static int decoder_parse_cseq(struct wfd_rtsp_decoder *dec, 621 | char *line, 622 | size_t len, 623 | char *tokens, 624 | size_t num) 625 | { 626 | struct wfd_rtsp_msg_header *h; 627 | int r; 628 | unsigned long val; 629 | char *next; 630 | 631 | h = &dec->msg.headers[WFD_RTSP_HEADER_CSEQ]; 632 | 633 | r = shl_atoi_ul(tokens, 10, (const char**)&next, &val); 634 | if (r < 0 || *next) { 635 | /* Screwed cseq line? Append it as unknown line. */ 636 | return decoder_add_unknown_line(dec, line, len); 637 | } 638 | 639 | r = header_append(h, line, len); 640 | if (r < 0) 641 | return llog_ERR(dec, r); 642 | 643 | /* overwrite previous cseqs */ 644 | h->cseq = val; 645 | 646 | return 0; 647 | } 648 | 649 | static int decoder_parse_header(struct wfd_rtsp_decoder *dec, 650 | char *line, 651 | size_t len) 652 | { 653 | unsigned int type; 654 | char *next, *tokens; 655 | ssize_t num; 656 | int r; 657 | 658 | num = wfd_rtsp_tokenize(line, len, &tokens); 659 | if (num < 0) 660 | return llog_ENOMEM(dec); 661 | if (num < 2) 662 | goto error; 663 | 664 | /* Header lines look like this: 665 | * : */ 666 | next = tokens; 667 | 668 | /* parse */ 669 | type = wfd_rtsp_header_from_name(next); 670 | next = wfd_rtsp_next_token(next); 671 | --num; 672 | 673 | /* parse ":" */ 674 | if (strcmp(next, ":")) 675 | goto error; 676 | next = wfd_rtsp_next_token(next); 677 | --num; 678 | 679 | /* dispatch to header specific parser */ 680 | switch (type) { 681 | case WFD_RTSP_HEADER_CONTENT_LENGTH: 682 | r = decoder_parse_content_length(dec, line, len, next, num); 683 | break; 684 | case WFD_RTSP_HEADER_CSEQ: 685 | r = decoder_parse_cseq(dec, line, len, next, num); 686 | break; 687 | default: 688 | /* no parser for given type available; append to list */ 689 | r = header_append(&dec->msg.headers[type], line, len); 690 | if (r < 0) 691 | llog_vERR(dec, r); 692 | break; 693 | } 694 | 695 | free(tokens); 696 | return r; 697 | 698 | error: 699 | free(tokens); 700 | return decoder_add_unknown_line(dec, line, len); 701 | } 702 | 703 | /* 704 | * Generic Header-line and ID-line parser 705 | * This parser is invoked on each successfully read header/id-line. It sanitizes 706 | * the input and then calls the correct header or ID parser, depending on 707 | * current state. 708 | */ 709 | 710 | static size_t sanitize_header_line(struct wfd_rtsp_decoder *dec, 711 | char *line, size_t len) 712 | { 713 | char *src, *dst, c, prev, last_c; 714 | size_t i; 715 | bool quoted, escaped; 716 | 717 | src = line; 718 | dst = line; 719 | last_c = 0; 720 | quoted = 0; 721 | escaped = 0; 722 | 723 | for (i = 0; i < len; ++i) { 724 | c = *src++; 725 | prev = last_c; 726 | last_c = c; 727 | 728 | if (quoted) { 729 | if (prev == '\\' && !escaped) { 730 | escaped = 1; 731 | /* turn escaped binary zero into "\0" */ 732 | if (c == '\0') 733 | c = '0'; 734 | } else { 735 | escaped = 0; 736 | if (c == '"') { 737 | quoted = 0; 738 | } else if (c == '\0') { 739 | /* skip binary 0 */ 740 | continue; 741 | } 742 | } 743 | } else { 744 | /* ignore any binary 0 */ 745 | if (c == '\0') 746 | continue; 747 | 748 | /* turn new-lines/tabs into white-space */ 749 | if (c == '\r' || c == '\n' || c == '\t') { 750 | c = ' '; 751 | last_c = c; 752 | } 753 | 754 | /* trim whitespace */ 755 | if (c == ' ' && prev == ' ') 756 | continue; 757 | 758 | if (c == '"') { 759 | quoted = 1; 760 | escaped = 0; 761 | } 762 | } 763 | 764 | *dst++ = c; 765 | } 766 | 767 | /* terminate string with binary zero */ 768 | *dst = 0; 769 | 770 | /* remove trailing whitespace */ 771 | while (dst > line && *(dst - 1) == ' ') 772 | *--dst = 0; 773 | 774 | /* the decoder already trims leading-whitespace */ 775 | 776 | return dst - line; 777 | } 778 | 779 | static int decoder_finish_header_line(struct wfd_rtsp_decoder *dec) 780 | { 781 | char *line; 782 | size_t l; 783 | int r; 784 | 785 | line = malloc(dec->buflen + 1); 786 | if (!line) 787 | return llog_ENOMEM(dec); 788 | 789 | shl_ring_copy(&dec->buf, line, dec->buflen); 790 | line[dec->buflen] = 0; 791 | l = sanitize_header_line(dec, line, dec->buflen); 792 | 793 | if (!dec->msg.id.line) 794 | r = decoder_parse_id(dec, line, l); 795 | else 796 | r = decoder_parse_header(dec, line, l); 797 | 798 | if (r < 0) 799 | free(line); 800 | 801 | return r; 802 | } 803 | 804 | /* 805 | * State Machine 806 | * The decoder state-machine is quite simple. We take an input buffer of 807 | * arbitrary length from the user and feed it byte by byte into the state 808 | * machine. 809 | * 810 | * Parsing RTSP messages is rather troublesome due to the ASCII-nature. It's 811 | * easy to parse as is, but has lots of corner-cases which we want to be 812 | * compatible to maybe broken implementations. Thus, we need this 813 | * state-machine. 814 | * 815 | * All we do here is split the endless input stream into header-lines. The 816 | * header-lines are not handled by the state-machine itself but passed on. If a 817 | * message contains an entity payload, we parse the body. Otherwise, we submit 818 | * the message and continue parsing the next one. 819 | */ 820 | 821 | _shl_public_ 822 | int wfd_rtsp_decoder_new(wfd_rtsp_decoder_event_t event_fn, 823 | void *data, 824 | wfd_rtsp_log_t log_fn, 825 | void *log_data, 826 | struct wfd_rtsp_decoder **out) 827 | { 828 | struct wfd_rtsp_decoder *dec; 829 | 830 | if (!event_fn || !out) 831 | return llog_dEINVAL(log_fn, data); 832 | 833 | dec = calloc(1, sizeof(*dec)); 834 | if (!dec) 835 | return llog_dENOMEM(log_fn, data); 836 | 837 | dec->event_fn = event_fn; 838 | dec->data = data; 839 | dec->llog = log_fn; 840 | dec->llog_data = log_data; 841 | 842 | *out = dec; 843 | return 0; 844 | } 845 | 846 | _shl_public_ 847 | void wfd_rtsp_decoder_free(struct wfd_rtsp_decoder *dec) 848 | { 849 | if (!dec) 850 | return; 851 | 852 | msg_clear(&dec->msg); 853 | shl_ring_clear(&dec->buf); 854 | free(dec); 855 | } 856 | 857 | _shl_public_ 858 | void wfd_rtsp_decoder_reset(struct wfd_rtsp_decoder *dec) 859 | { 860 | if (!dec) 861 | return; 862 | 863 | msg_clear(&dec->msg); 864 | shl_ring_flush(&dec->buf); 865 | 866 | dec->buflen = 0; 867 | dec->last_chr = 0; 868 | dec->state = 0; 869 | dec->remaining_body = 0; 870 | 871 | dec->data_channel = 0; 872 | dec->data_size = 0; 873 | 874 | dec->quoted = false; 875 | dec->dead = false; 876 | } 877 | 878 | _shl_public_ 879 | void wfd_rtsp_decoder_set_data(struct wfd_rtsp_decoder *dec, void *data) 880 | { 881 | if (!dec) 882 | return; 883 | 884 | dec->data = data; 885 | } 886 | 887 | _shl_public_ 888 | void *wfd_rtsp_decoder_get_data(struct wfd_rtsp_decoder *dec) 889 | { 890 | if (!dec) 891 | return NULL; 892 | 893 | return dec->data; 894 | } 895 | 896 | static int decoder_feed_char_new(struct wfd_rtsp_decoder *dec, char ch) 897 | { 898 | switch (ch) { 899 | case '\r': 900 | case '\n': 901 | case '\t': 902 | case ' ': 903 | /* If no msg has been started, yet, we ignore LWS for 904 | * compatibility reasons. Note that they're actually not 905 | * allowed, but should be ignored by implementations. */ 906 | ++dec->buflen; 907 | break; 908 | case '$': 909 | /* Interleaved data. Followed by 1 byte channel-id and 2-byte 910 | * data-length. */ 911 | dec->state = STATE_DATA_HEAD; 912 | dec->data_channel = 0; 913 | dec->data_size = 0; 914 | 915 | /* clear any previous whitespace and leading '$' */ 916 | shl_ring_pull(&dec->buf, dec->buflen + 1); 917 | dec->buflen = 0; 918 | break; 919 | default: 920 | /* Clear any pending data in the ring-buffer and then just 921 | * push the char into the buffer. Any char except LWS is fine 922 | * here. */ 923 | dec->state = STATE_HEADER; 924 | dec->remaining_body = 0; 925 | 926 | shl_ring_pull(&dec->buf, dec->buflen); 927 | dec->buflen = 1; 928 | break; 929 | } 930 | 931 | return 0; 932 | } 933 | 934 | static int decoder_feed_char_header(struct wfd_rtsp_decoder *dec, char ch) 935 | { 936 | int r; 937 | 938 | switch (ch) { 939 | case '\r': 940 | if (dec->last_chr == '\r' || dec->last_chr == '\n') { 941 | /* \r\r means empty new-line. We actually allow \r\r\n, 942 | * too. \n\r means empty new-line, too, but might also 943 | * be finished off as \n\r\n so go to STATE_HEADER_NL 944 | * to optionally complete the new-line. 945 | * However, if the body is empty, we need to finish the 946 | * msg early as there might be no \n coming.. */ 947 | dec->state = STATE_HEADER_NL; 948 | 949 | /* First finish the last header line if any. Don't 950 | * include the current \r as it is already part of the 951 | * empty following line. */ 952 | r = decoder_finish_header_line(dec); 953 | if (r < 0) 954 | return r; 955 | 956 | /* discard buffer *and* whitespace */ 957 | shl_ring_pull(&dec->buf, dec->buflen + 1); 958 | dec->buflen = 0; 959 | 960 | /* No remaining body. Finish message! */ 961 | if (!dec->remaining_body) { 962 | r = decoder_submit(dec); 963 | if (r < 0) 964 | return r; 965 | } 966 | } else { 967 | /* '\r' following any character just means newline 968 | * (optionally followed by \n). We don't do anything as 969 | * it might be a continuation line. */ 970 | ++dec->buflen; 971 | } 972 | break; 973 | case '\n': 974 | if (dec->last_chr == '\n') { 975 | /* We got \n\n, which means we need to finish the 976 | * current header-line. If there's no remaining body, 977 | * we immediately finish the message and go to 978 | * STATE_NEW. Otherwise, we go to STATE_BODY 979 | * straight. */ 980 | 981 | /* don't include second \n in header-line */ 982 | r = decoder_finish_header_line(dec); 983 | if (r < 0) 984 | return r; 985 | 986 | /* discard buffer *and* whitespace */ 987 | shl_ring_pull(&dec->buf, dec->buflen + 1); 988 | dec->buflen = 0; 989 | 990 | if (dec->remaining_body) { 991 | dec->state = STATE_BODY; 992 | } else { 993 | dec->state = STATE_NEW; 994 | r = decoder_submit(dec); 995 | if (r < 0) 996 | return r; 997 | } 998 | } else if (dec->last_chr == '\r') { 999 | /* We got an \r\n. We cannot finish the header line as 1000 | * it might be a continuation line. Next character 1001 | * decides what to do. Don't do anything here. 1002 | * \r\n\r cannot happen here as it is handled by 1003 | * STATE_HEADER_NL. */ 1004 | ++dec->buflen; 1005 | } else { 1006 | /* Same as above, we cannot finish the line as it 1007 | * might be a continuation line. Do nothing. */ 1008 | ++dec->buflen; 1009 | } 1010 | break; 1011 | case '\t': 1012 | case ' ': 1013 | /* Whitespace. Simply push into buffer and don't do anything. 1014 | * In case of a continuation line, nothing has to be done, 1015 | * either. */ 1016 | ++dec->buflen; 1017 | break; 1018 | default: 1019 | if (dec->last_chr == '\r' || dec->last_chr == '\n') { 1020 | /* Last line is complete and this is no whitespace, 1021 | * thus it's not a continuation line. 1022 | * Finish the line. */ 1023 | 1024 | /* don't include new char in line */ 1025 | r = decoder_finish_header_line(dec); 1026 | if (r < 0) 1027 | return r; 1028 | shl_ring_pull(&dec->buf, dec->buflen); 1029 | dec->buflen = 0; 1030 | } 1031 | 1032 | /* consume character and handle special chars */ 1033 | ++dec->buflen; 1034 | if (ch == '"') { 1035 | /* go to STATE_HEADER_QUOTE */ 1036 | dec->state = STATE_HEADER_QUOTE; 1037 | dec->quoted = false; 1038 | } 1039 | 1040 | break; 1041 | } 1042 | 1043 | return 0; 1044 | } 1045 | 1046 | static int decoder_feed_char_header_quote(struct wfd_rtsp_decoder *dec, char ch) 1047 | { 1048 | if (dec->last_chr == '\\' && !dec->quoted) { 1049 | /* This character is quoted, so copy it unparsed. To handle 1050 | * double-backslash, we set the "quoted" bit. */ 1051 | ++dec->buflen; 1052 | dec->quoted = true; 1053 | } else { 1054 | dec->quoted = false; 1055 | 1056 | /* consume character and handle special chars */ 1057 | ++dec->buflen; 1058 | if (ch == '"') 1059 | dec->state = STATE_HEADER; 1060 | } 1061 | 1062 | return 0; 1063 | } 1064 | 1065 | static int decoder_feed_char_body(struct wfd_rtsp_decoder *dec, char ch) 1066 | { 1067 | char *line; 1068 | int r; 1069 | 1070 | /* If remaining_body was already 0, the message had no body. Note that 1071 | * messages without body are finished early, so no need to call 1072 | * decoder_submit() here. Simply forward @ch to STATE_NEW. 1073 | * @rlen is usually 0. We don't care and forward it, too. */ 1074 | if (!dec->remaining_body) { 1075 | dec->state = STATE_NEW; 1076 | return decoder_feed_char_new(dec, ch); 1077 | } 1078 | 1079 | /* *any* character is allowed as body */ 1080 | ++dec->buflen; 1081 | 1082 | if (!--dec->remaining_body) { 1083 | /* full body received, copy it and go to STATE_NEW */ 1084 | 1085 | line = malloc(dec->buflen + 1); 1086 | if (!line) 1087 | return llog_ENOMEM(dec); 1088 | 1089 | shl_ring_copy(&dec->buf, line, dec->buflen); 1090 | line[dec->buflen] = 0; 1091 | 1092 | dec->msg.entity.value = line; 1093 | dec->msg.entity.size = dec->buflen; 1094 | r = decoder_submit(dec); 1095 | 1096 | dec->state = STATE_NEW; 1097 | shl_ring_pull(&dec->buf, dec->buflen); 1098 | dec->buflen = 0; 1099 | 1100 | if (r < 0) 1101 | return r; 1102 | } 1103 | 1104 | return 0; 1105 | } 1106 | 1107 | static int decoder_feed_char_header_nl(struct wfd_rtsp_decoder *dec, char ch) 1108 | { 1109 | /* STATE_HEADER_NL means we received an empty line ending with \r. The 1110 | * standard requires a following \n but advises implementations to 1111 | * accept \r on itself, too. 1112 | * What we do is to parse a \n as end-of-header and any character as 1113 | * end-of-header plus start-of-body. Note that we discard anything in 1114 | * the ring-buffer that has already been parsed (which normally can 1115 | * nothing, but lets be safe). */ 1116 | 1117 | if (ch == '\n') { 1118 | /* discard transition chars plus new \n */ 1119 | shl_ring_pull(&dec->buf, dec->buflen + 1); 1120 | dec->buflen = 0; 1121 | 1122 | dec->state = STATE_BODY; 1123 | if (!dec->remaining_body) 1124 | dec->state = STATE_NEW; 1125 | 1126 | return 0; 1127 | } else { 1128 | /* discard any transition chars and push @ch into body */ 1129 | shl_ring_pull(&dec->buf, dec->buflen); 1130 | dec->buflen = 0; 1131 | 1132 | dec->state = STATE_BODY; 1133 | return decoder_feed_char_body(dec, ch); 1134 | } 1135 | } 1136 | 1137 | static int decoder_feed_char_data_head(struct wfd_rtsp_decoder *dec, char ch) 1138 | { 1139 | uint8_t buf[3]; 1140 | 1141 | /* Read 1 byte channel-id and 2 byte body length. */ 1142 | 1143 | if (++dec->buflen >= 3) { 1144 | shl_ring_copy(&dec->buf, buf, 3); 1145 | shl_ring_pull(&dec->buf, dec->buflen); 1146 | dec->buflen = 0; 1147 | 1148 | dec->data_channel = buf[0]; 1149 | dec->data_size = (((uint16_t)buf[1]) << 8) | (uint16_t)buf[2]; 1150 | dec->state = STATE_DATA_BODY; 1151 | } 1152 | 1153 | return 0; 1154 | } 1155 | 1156 | static int decoder_feed_char_data_body(struct wfd_rtsp_decoder *dec, char ch) 1157 | { 1158 | uint8_t *buf; 1159 | int r; 1160 | 1161 | /* Read @dec->data_size bytes of raw data. */ 1162 | 1163 | if (++dec->buflen >= dec->data_size) { 1164 | buf = malloc(dec->data_size + 1); 1165 | if (!buf) 1166 | return llog_ENOMEM(dec); 1167 | 1168 | /* Not really needed, but in case it's actually a text-payload 1169 | * make sure it's 0-terminated to work around client bugs. */ 1170 | buf[dec->data_size] = 0; 1171 | 1172 | shl_ring_copy(&dec->buf, buf, dec->data_size); 1173 | 1174 | r = decoder_submit_data(dec, buf); 1175 | free(buf); 1176 | 1177 | dec->state = STATE_NEW; 1178 | shl_ring_pull(&dec->buf, dec->buflen); 1179 | dec->buflen = 0; 1180 | 1181 | if (r < 0) 1182 | return r; 1183 | } 1184 | 1185 | return 0; 1186 | } 1187 | 1188 | static int decoder_feed_char(struct wfd_rtsp_decoder *dec, char ch) 1189 | { 1190 | int r = 0; 1191 | 1192 | switch (dec->state) { 1193 | case STATE_NEW: 1194 | r = decoder_feed_char_new(dec, ch); 1195 | break; 1196 | case STATE_HEADER: 1197 | r = decoder_feed_char_header(dec, ch); 1198 | break; 1199 | case STATE_HEADER_QUOTE: 1200 | r = decoder_feed_char_header_quote(dec, ch); 1201 | break; 1202 | case STATE_HEADER_NL: 1203 | r = decoder_feed_char_header_nl(dec, ch); 1204 | break; 1205 | case STATE_BODY: 1206 | r = decoder_feed_char_body(dec, ch); 1207 | break; 1208 | case STATE_DATA_HEAD: 1209 | r = decoder_feed_char_data_head(dec, ch); 1210 | break; 1211 | case STATE_DATA_BODY: 1212 | r = decoder_feed_char_data_body(dec, ch); 1213 | break; 1214 | } 1215 | 1216 | return r; 1217 | } 1218 | 1219 | _shl_public_ 1220 | int wfd_rtsp_decoder_feed(struct wfd_rtsp_decoder *dec, 1221 | const void *buf, 1222 | size_t len) 1223 | { 1224 | char ch; 1225 | size_t i; 1226 | int r; 1227 | 1228 | if (!dec) 1229 | return -EINVAL; 1230 | if (dec->dead) 1231 | return llog_EINVAL(dec); 1232 | if (!len) 1233 | return 0; 1234 | if (!buf) 1235 | return llog_EINVAL(dec); 1236 | 1237 | /* We keep dec->buflen as cache for the current parsed-buffer size. We 1238 | * need to push the whole input-buffer into our parser-buffer and go 1239 | * through it one-by-one. The parser increments dec->buflen for each of 1240 | * these and once we're done, we verify our state is consistent. */ 1241 | 1242 | dec->buflen = shl_ring_get_size(&dec->buf); 1243 | r = shl_ring_push(&dec->buf, buf, len); 1244 | if (r < 0) { 1245 | llog_vERR(dec, r); 1246 | goto error; 1247 | } 1248 | 1249 | for (i = 0; i < len; ++i) { 1250 | ch = ((const char*)buf)[i]; 1251 | r = decoder_feed_char(dec, ch); 1252 | if (r < 0) 1253 | goto error; 1254 | 1255 | dec->last_chr = ch; 1256 | } 1257 | 1258 | /* check for internal parser inconsistencies; should not happen! */ 1259 | if (dec->buflen != shl_ring_get_size(&dec->buf)) { 1260 | llog_error(dec, "internal RTSP parser error"); 1261 | r = -EFAULT; 1262 | goto error; 1263 | } 1264 | 1265 | return 0; 1266 | 1267 | error: 1268 | dec->dead = true; 1269 | return r; 1270 | } 1271 | -------------------------------------------------------------------------------- /src/rtsp_tokenizer.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include "libwfd.h" 32 | #include "shl_macro.h" 33 | 34 | /* 35 | * RTSP Tokenizer 36 | * The RTSP standard is word-based and allows linear-whitespace between any two 37 | * tokens or special-chars. This tokenizer splits a given line into a list of 38 | * tokens and returns how many tokens were generated. It also sanitizes the line 39 | * by removing whitespace, trimming leading/trailing whitespace, removing binary 40 | * zero characters and decoding escape-sequences. 41 | * 42 | * This tokenizer can be used before or after the basic RTSP sanitizer. But note 43 | * that some RTSP requests or responses contain URIs or other embedded 44 | * information which should not be tokenized as they don't follow basic RTSP 45 | * rules (yeah, who came up with that shit..). 46 | */ 47 | _shl_public_ 48 | ssize_t wfd_rtsp_tokenize(const char *line, ssize_t len, char **out) 49 | { 50 | char *t, *dst, c, prev, last_c; 51 | const char *src; 52 | size_t num; 53 | bool quoted, escaped; 54 | 55 | if (!line || !out) 56 | return -EINVAL; 57 | 58 | /* we need at most twice as much space for all the terminating 0s */ 59 | if (len < 0) 60 | len = strlen(line); 61 | t = calloc(2, len + 1); 62 | if (!t) 63 | return -ENOMEM; 64 | 65 | num = 0; 66 | src = line; 67 | dst = t; 68 | quoted = false; 69 | escaped = false; 70 | prev = 0; 71 | last_c = 0; 72 | 73 | for ( ; len > 0; --len) { 74 | c = *src++; 75 | prev = last_c; 76 | last_c = 0; 77 | 78 | if (quoted) { 79 | if (escaped) { 80 | last_c = c; 81 | if (c == '\\') { 82 | *dst++ = '\\'; 83 | } else if (c == '"') { 84 | *dst++ = '"'; 85 | } else if (c == 'n') { 86 | *dst++ = '\n'; 87 | } else if (c == 'r') { 88 | *dst++ = '\r'; 89 | } else if (c == 't') { 90 | *dst++ = '\t'; 91 | } else if (c == 'a') { 92 | *dst++ = '\a'; 93 | } else if (c == 'f') { 94 | *dst++ = '\f'; 95 | } else if (c == 'v') { 96 | *dst++ = '\v'; 97 | } else if (c == 'b') { 98 | *dst++ = '\b'; 99 | } else if (c == 'e') { 100 | *dst++ = 0x1b; /* ESC */ 101 | } else if (c == 0) { 102 | /* turn escaped binary 0 into \0 */ 103 | *dst++ = '\\'; 104 | *dst++ = '0'; 105 | last_c = '0'; 106 | } else { 107 | /* keep unknown escape sequences */ 108 | *dst++ = '\\'; 109 | *dst++ = c; 110 | } 111 | escaped = false; 112 | } else { 113 | if (c == '"') { 114 | *dst++ = 0; 115 | ++num; 116 | quoted = false; 117 | } else if (c == '\\') { 118 | escaped = true; 119 | last_c = prev; 120 | } else if (c == 0) { 121 | /* discard */ 122 | last_c = prev; 123 | } else { 124 | *dst++ = c; 125 | last_c = c; 126 | } 127 | } 128 | } else { 129 | if (c == '"') { 130 | if (prev) { 131 | *dst++ = 0; 132 | ++num; 133 | } 134 | quoted = true; 135 | } else if (c == 0) { 136 | /* discard */ 137 | last_c = prev; 138 | } else if (c == ' ' || 139 | c == '\t' || 140 | c == '\n' || 141 | c == '\r') { 142 | if (prev) { 143 | *dst++ = 0; 144 | ++num; 145 | } 146 | } else if (c == '(' || 147 | c == ')' || 148 | c == '[' || 149 | c == ']' || 150 | c == '{' || 151 | c == '}' || 152 | c == '<' || 153 | c == '>' || 154 | c == '@' || 155 | c == ',' || 156 | c == ';' || 157 | c == ':' || 158 | c == '\\' || 159 | c == '/' || 160 | c == '?' || 161 | c == '=') { 162 | if (prev) { 163 | *dst++ = 0; 164 | ++num; 165 | } 166 | *dst++ = c; 167 | *dst++ = 0; 168 | ++num; 169 | } else if (c <= 31 || c == 127) { 170 | /* ignore CTLs */ 171 | if (prev) { 172 | *dst++ = 0; 173 | ++num; 174 | } 175 | } else { 176 | *dst++ = c; 177 | last_c = c; 178 | } 179 | } 180 | } 181 | 182 | if (last_c || quoted) { 183 | if (escaped) 184 | *dst++ = '\\'; 185 | *dst++ = 0; 186 | ++num; 187 | } 188 | 189 | *out = t; 190 | return num; 191 | } 192 | -------------------------------------------------------------------------------- /src/shl_llog.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Library Log/Debug Interface 3 | * 4 | * Copyright (c) 2010-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Library Log/Debug Interface 10 | * Libraries should always avoid producing side-effects. This includes writing 11 | * log-messages of any kind. However, you often don't want to disable debugging 12 | * entirely, therefore, the core objects often contain a pointer to a function 13 | * which performs logging. If that pointer is NULL (default), logging is 14 | * disabled. 15 | * 16 | * This header should never be installed into the system! This is _no_ public 17 | * header. Instead, copy it into your application if you want and use it there. 18 | * Your public library API should include something like this: 19 | * 20 | * typedef void (*MYPREFIX_log_t) (void *data, 21 | * const char *file, 22 | * int line, 23 | * const char *func, 24 | * const char *subs, 25 | * unsigned int sev, 26 | * const char *format, 27 | * va_list args); 28 | * 29 | * And then the user can supply such a function when creating a new context 30 | * object of your library or simply supply NULL. Internally, you have a field of 31 | * type "MYPREFIX_log_t llog" in your main structure. If you pass this to the 32 | * convenience helpers like llog_dbg(), llog_warn() etc. it will automatically 33 | * use the "llog" field to print the message. If it is NULL, nothing is done. 34 | * 35 | * The arguments of the log-function are defined as: 36 | * data: User-supplied data field that is passed straight through. 37 | * file: Zero terminated string of the file-name where the log-message 38 | * occurred. Can be NULL. 39 | * line: Line number of @file where the message occurred. Set to 0 or smaller 40 | * if not available. 41 | * func: Function name where the log-message occurred. Can be NULL. 42 | * subs: Subsystem where the message occurred (zero terminated). Can be NULL. 43 | * sev: Severity of log-message. An integer between 0 and 7 as defined below. 44 | * These are identical to the linux-kernel severities so there is no need 45 | * to include these in your public API. Every app can define them 46 | * themselves, if they need it. 47 | * format: Format string. Must not be NULL. 48 | * args: Argument array 49 | * 50 | * The user should also be able to optionally provide a data field which is 51 | * always passed unmodified as first parameter to the log-function. This allows 52 | * to add context to the logger. 53 | */ 54 | 55 | #ifndef SHL_LLOG_H 56 | #define SHL_LLOG_H 57 | 58 | #include 59 | #include 60 | #include 61 | #include 62 | 63 | enum llog_severity { 64 | LLOG_FATAL = 0, 65 | LLOG_ALERT = 1, 66 | LLOG_CRITICAL = 2, 67 | LLOG_ERROR = 3, 68 | LLOG_WARNING = 4, 69 | LLOG_NOTICE = 5, 70 | LLOG_INFO = 6, 71 | LLOG_DEBUG = 7, 72 | LLOG_SEV_NUM, 73 | }; 74 | 75 | typedef void (*llog_submit_t) (void *data, 76 | const char *file, 77 | int line, 78 | const char *func, 79 | const char *subs, 80 | unsigned int sev, 81 | const char *format, 82 | va_list args); 83 | 84 | static inline __attribute__((format(printf, 8, 9))) 85 | void llog_format(llog_submit_t llog, 86 | void *data, 87 | const char *file, 88 | int line, 89 | const char *func, 90 | const char *subs, 91 | unsigned int sev, 92 | const char *format, 93 | ...) 94 | { 95 | int saved_errno = errno; 96 | va_list list; 97 | 98 | if (llog) { 99 | va_start(list, format); 100 | errno = saved_errno; 101 | llog(data, file, line, func, subs, sev, format, list); 102 | va_end(list); 103 | } 104 | } 105 | 106 | #ifndef LLOG_SUBSYSTEM 107 | static const char *LLOG_SUBSYSTEM __attribute__((__unused__)); 108 | #endif 109 | 110 | #define LLOG_DEFAULT __FILE__, __LINE__, __func__, LLOG_SUBSYSTEM 111 | 112 | #define llog_printf(obj, sev, format, ...) \ 113 | llog_format((obj)->llog, \ 114 | (obj)->llog_data, \ 115 | LLOG_DEFAULT, \ 116 | (sev), \ 117 | (format), \ 118 | ##__VA_ARGS__) 119 | #define llog_dprintf(obj, data, sev, format, ...) \ 120 | llog_format((obj), \ 121 | (data), \ 122 | LLOG_DEFAULT, \ 123 | (sev), \ 124 | (format), \ 125 | ##__VA_ARGS__) 126 | 127 | static inline __attribute__((format(printf, 4, 5))) 128 | void llog_dummyf(llog_submit_t llog, void *data, unsigned int sev, 129 | const char *format, ...) 130 | { 131 | } 132 | 133 | /* 134 | * Helpers 135 | * They pick up all the default values and submit the message to the 136 | * llog-subsystem. The llog_debug() function will discard the message unless 137 | * BUILD_ENABLE_DEBUG is defined. 138 | */ 139 | 140 | #ifdef BUILD_ENABLE_DEBUG 141 | #define llog_ddebug(obj, data, format, ...) \ 142 | llog_dprintf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__) 143 | #define llog_debug(obj, format, ...) \ 144 | llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__) 145 | #else 146 | #define llog_ddebug(obj, data, format, ...) \ 147 | llog_dummyf((obj), (data), LLOG_DEBUG, (format), ##__VA_ARGS__) 148 | #define llog_debug(obj, format, ...) \ 149 | llog_ddebug((obj)->llog, (obj)->llog_data, (format), ##__VA_ARGS__) 150 | #endif 151 | 152 | #define llog_info(obj, format, ...) \ 153 | llog_printf((obj), LLOG_INFO, (format), ##__VA_ARGS__) 154 | #define llog_dinfo(obj, data, format, ...) \ 155 | llog_dprintf((obj), (data), LLOG_INFO, (format), ##__VA_ARGS__) 156 | #define llog_notice(obj, format, ...) \ 157 | llog_printf((obj), LLOG_NOTICE, (format), ##__VA_ARGS__) 158 | #define llog_dnotice(obj, data, format, ...) \ 159 | llog_dprintf((obj), (data), LLOG_NOTICE, (format), ##__VA_ARGS__) 160 | #define llog_warning(obj, format, ...) \ 161 | llog_printf((obj), LLOG_WARNING, (format), ##__VA_ARGS__) 162 | #define llog_dwarning(obj, data, format, ...) \ 163 | llog_dprintf((obj), (data), LLOG_WARNING, (format), ##__VA_ARGS__) 164 | #define llog_error(obj, format, ...) \ 165 | llog_printf((obj), LLOG_ERROR, (format), ##__VA_ARGS__) 166 | #define llog_derror(obj, data, format, ...) \ 167 | llog_dprintf((obj), (data), LLOG_ERROR, (format), ##__VA_ARGS__) 168 | #define llog_critical(obj, format, ...) \ 169 | llog_printf((obj), LLOG_CRITICAL, (format), ##__VA_ARGS__) 170 | #define llog_dcritical(obj, data, format, ...) \ 171 | llog_dprintf((obj), (data), LLOG_CRITICAL, (format), ##__VA_ARGS__) 172 | #define llog_alert(obj, format, ...) \ 173 | llog_printf((obj), LLOG_ALERT, (format), ##__VA_ARGS__) 174 | #define llog_dalert(obj, data, format, ...) \ 175 | llog_dprintf((obj), (data), LLOG_ALERT, (format), ##__VA_ARGS__) 176 | #define llog_fatal(obj, format, ...) \ 177 | llog_printf((obj), LLOG_FATAL, (format), ##__VA_ARGS__) 178 | #define llog_dfatal(obj, data, format, ...) \ 179 | llog_dprintf((obj), (data), LLOG_FATAL, (format), ##__VA_ARGS__) 180 | 181 | /* 182 | * Default log messages 183 | * These macros can be used to produce default log messages. You can use them 184 | * directly in an "return" statement. The "v" variants automatically cast the 185 | * result to void so it can be used in return statements inside of void 186 | * functions. The "d" variants use the logging object directly as the parent 187 | * might not exist, yet. 188 | * 189 | * Most of the messages work only if debugging is enabled. This is, because they 190 | * are used in debug paths and would slow down normal applications. 191 | */ 192 | 193 | #define llog_dEINVAL(obj, data) \ 194 | (llog_derror((obj), (data), "invalid arguments"), -EINVAL) 195 | #define llog_EINVAL(obj) \ 196 | (llog_dEINVAL((obj)->llog, (obj)->llog_data)) 197 | #define llog_vEINVAL(obj) \ 198 | ((void)llog_EINVAL(obj)) 199 | #define llog_vdEINVAL(obj, data) \ 200 | ((void)llog_dEINVAL((obj), (data))) 201 | 202 | #define llog_dEFAULT(obj, data) \ 203 | (llog_derror((obj), (data), "internal operation failed"), -EFAULT) 204 | #define llog_EFAULT(obj) \ 205 | (llog_dEFAULT((obj)->llog, (obj)->llog_data)) 206 | #define llog_vEFAULT(obj) \ 207 | ((void)llog_EFAULT(obj)) 208 | #define llog_vdEFAULT(obj, data) \ 209 | ((void)llog_dEFAULT((obj), (data))) 210 | 211 | #define llog_dENOMEM(obj, data) \ 212 | (llog_derror((obj), (data), "out of memory"), -ENOMEM) 213 | #define llog_ENOMEM(obj) \ 214 | (llog_dENOMEM((obj)->llog, (obj)->llog_data)) 215 | #define llog_vENOMEM(obj) \ 216 | ((void)llog_ENOMEM(obj)) 217 | #define llog_vdENOMEM(obj, data) \ 218 | ((void)llog_dENOMEM((obj), (data))) 219 | 220 | #define llog_dEPIPE(obj, data) \ 221 | (llog_derror((obj), (data), "fd closed unexpectedly"), -EPIPE) 222 | #define llog_EPIPE(obj) \ 223 | (llog_dEPIPE((obj)->llog, (obj)->llog_data)) 224 | #define llog_vEPIPE(obj) \ 225 | ((void)llog_EPIPE(obj)) 226 | #define llog_vdEPIPE(obj, data) \ 227 | ((void)llog_dEPIPE((obj), (data))) 228 | 229 | #define llog_dERRNO(obj, data) \ 230 | (llog_derror((obj), (data), "syscall failed (%d): %m", errno), -errno) 231 | #define llog_ERRNO(obj) \ 232 | (llog_dERRNO((obj)->llog, (obj)->llog_data)) 233 | #define llog_vERRNO(obj) \ 234 | ((void)llog_ERRNO(obj)) 235 | #define llog_vdERRNO(obj, data) \ 236 | ((void)llog_dERRNO((obj), (data))) 237 | 238 | #define llog_dERR(obj, data, _r) \ 239 | (errno = -(_r), llog_derror((obj), (data), "syscall failed (%d): %m", (_r)), (_r)) 240 | #define llog_ERR(obj, _r) \ 241 | (llog_dERR((obj)->llog, (obj)->llog_data, (_r))) 242 | #define llog_vERR(obj, _r) \ 243 | ((void)llog_ERR((obj), (_r))) 244 | #define llog_vdERR(obj, data, _r) \ 245 | ((void)llog_dERR((obj), (data), (_r))) 246 | 247 | #endif /* SHL_LLOG_H */ 248 | -------------------------------------------------------------------------------- /src/shl_macro.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Macros 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Macros 10 | */ 11 | 12 | #ifndef SHL_MACRO_H 13 | #define SHL_MACRO_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | /* sanity checks required for some macros */ 23 | #if __SIZEOF_POINTER__ != 4 && __SIZEOF_POINTER__ != 8 24 | #error "Pointer size is neither 4 nor 8 bytes" 25 | #endif 26 | 27 | /* gcc attributes; look them up for more information */ 28 | #define _shl_printf_(_a, _b) __attribute__((__format__(printf, _a, _b))) 29 | #define _shl_alloc_(...) __attribute__((__alloc_size__(__VA_ARGS__))) 30 | #define _shl_sentinel_ __attribute__((__sentinel__)) 31 | #define _shl_noreturn_ __attribute__((__noreturn__)) 32 | #define _shl_unused_ __attribute__((__unused__)) 33 | #define _shl_pure_ __attribute__((__pure__)) 34 | #define _shl_const_ __attribute__((__const__)) 35 | #define _shl_deprecated_ __attribute__((__deprecated__)) 36 | #define _shl_packed_ __attribute__((__packed__)) 37 | #define _shl_malloc_ __attribute__((__malloc__)) 38 | #define _shl_weak_ __attribute__((__weak__)) 39 | #define _shl_likely_(_val) (__builtin_expect(!!(_val), 1)) 40 | #define _shl_unlikely_(_val) (__builtin_expect(!!(_val), 0)) 41 | #define _shl_public_ __attribute__((__visibility__("default"))) 42 | #define _shl_hidden_ __attribute__((__visibility__("hidden"))) 43 | #define _shl_weakref_(_val) __attribute__((__weakref__(#_val))) 44 | #define _shl_cleanup_(_val) __attribute__((__cleanup__(_val))) 45 | 46 | /* 2-level stringify helper */ 47 | #define SHL__STRINGIFY(_val) #_val 48 | #define SHL_STRINGIFY(_val) SHL__STRINGIFY(_val) 49 | 50 | /* 2-level concatenate helper */ 51 | #define SHL__CONCATENATE(_a, _b) _a ## _b 52 | #define SHL_CONCATENATE(_a, _b) SHL__CONCATENATE(_a, _b) 53 | 54 | /* unique identifier with prefix */ 55 | #define SHL_UNIQUE(_prefix) SHL_CONCATENATE(_prefix, __COUNTER__) 56 | 57 | /* array element count */ 58 | #define SHL_ARRAY_LENGTH(_array) (sizeof(_array)/sizeof(*(_array))) 59 | 60 | /* get parent pointer by container-type, member and member-pointer */ 61 | #define shl_container_of(_ptr, _type, _member) \ 62 | ({ \ 63 | const typeof( ((_type *)0)->_member ) *__mptr = (_ptr); \ 64 | (_type *)( (char *)__mptr - offsetof(_type, _member) ); \ 65 | }) 66 | 67 | /* return maximum of two values and do strict type checking */ 68 | #define shl_max(_a, _b) \ 69 | ({ \ 70 | typeof(_a) __a = (_a); \ 71 | typeof(_b) __b = (_b); \ 72 | (void) (&__a == &__b); \ 73 | __a > __b ? __a : __b; \ 74 | }) 75 | 76 | /* same as shl_max() but perform explicit cast beforehand */ 77 | #define shl_max_t(_type, _a, _b) \ 78 | ({ \ 79 | _type __a = (_type)(_a); \ 80 | _type __b = (_type)(_b); \ 81 | __a > __b ? __a : __b; \ 82 | }) 83 | 84 | /* return minimum of two values and do strict type checking */ 85 | #define shl_min(_a, _b) \ 86 | ({ \ 87 | typeof(_a) __a = (_a); \ 88 | typeof(_b) __b = (_b); \ 89 | (void) (&__a == &__b); \ 90 | __a < __b ? __a : __b; \ 91 | }) 92 | 93 | /* same as shl_min() but perform explicit cast beforehand */ 94 | #define shl_min_t(_type, _a, _b) \ 95 | ({ \ 96 | _type __a = (_type)(_a); \ 97 | _type __b = (_type)(_b); \ 98 | __a < __b ? __a : __b; \ 99 | }) 100 | 101 | /* clamp value between low and high barriers */ 102 | #define shl_clamp(_val, _low, _high) \ 103 | ({ \ 104 | typeof(_val) __v = (_val); \ 105 | typeof(_low) __l = (_low); \ 106 | typeof(_high) __h = (_high); \ 107 | (void) (&__v == &__l); \ 108 | (void) (&__v == &__h); \ 109 | ((__v > __h) ? __h : ((__v < __l) ? __l : __v)); \ 110 | }) 111 | 112 | /* align to next higher power-of-2 (except for: 0 => 0, overflow => 0) */ 113 | static inline size_t SHL_ALIGN_POWER2(size_t u) 114 | { 115 | return 1ULL << ((sizeof(u) * 8ULL) - __builtin_clzll(u - 1ULL)); 116 | } 117 | 118 | /* zero memory or type */ 119 | #define shl_memzero(_ptr, _size) (memset((_ptr), 0, (_size))) 120 | #define shl_zero(_ptr) (shl_memzero(&(_ptr), sizeof(_ptr))) 121 | 122 | /* ptr <=> uint casts */ 123 | #define SHL_PTR_TO_TYPE(_type, _ptr) ((_type)((uintptr_t)(_ptr))) 124 | #define SHL_TYPE_TO_PTR(_type, _int) ((void*)((uintptr_t)(_int))) 125 | #define SHL_PTR_TO_INT(_ptr) SHL_PTR_TO_TYPE(int, (_ptr)) 126 | #define SHL_INT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int, (_ptr)) 127 | #define SHL_PTR_TO_UINT(_ptr) SHL_PTR_TO_TYPE(unsigned int, (_ptr)) 128 | #define SHL_UINT_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned int, (_ptr)) 129 | #define SHL_PTR_TO_LONG(_ptr) SHL_PTR_TO_TYPE(long, (_ptr)) 130 | #define SHL_LONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(long, (_ptr)) 131 | #define SHL_PTR_TO_ULONG(_ptr) SHL_PTR_TO_TYPE(unsigned long, (_ptr)) 132 | #define SHL_ULONG_TO_PTR(_ptr) SHL_TYPE_TO_PTR(unsigned long, (_ptr)) 133 | #define SHL_PTR_TO_S32(_ptr) SHL_PTR_TO_TYPE(int32_t, (_ptr)) 134 | #define SHL_S32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int32_t, (_ptr)) 135 | #define SHL_PTR_TO_U32(_ptr) SHL_PTR_TO_TYPE(uint32_t, (_ptr)) 136 | #define SHL_U32_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint32_t, (_ptr)) 137 | #define SHL_PTR_TO_S64(_ptr) SHL_PTR_TO_TYPE(int64_t, (_ptr)) 138 | #define SHL_S64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(int64_t, (_ptr)) 139 | #define SHL_PTR_TO_U64(_ptr) SHL_PTR_TO_TYPE(uint64_t, (_ptr)) 140 | #define SHL_U64_TO_PTR(_ptr) SHL_TYPE_TO_PTR(uint64_t, (_ptr)) 141 | 142 | /* compile-time assertions */ 143 | #define shl_assert_cc(_expr) static_assert(_expr, #_expr) 144 | 145 | /* 146 | * Safe Multiplications 147 | * Multiplications are subject to overflows. These helpers guarantee that the 148 | * multiplication can be done safely and return -ERANGE if not. 149 | * 150 | * Note: This is horribly slow for ull/uint64_t as we need a division to test 151 | * for overflows. Take that into account when using these. For smaller integers, 152 | * we can simply use an upcast-multiplication which gcc should be smart enough 153 | * to optimize. 154 | */ 155 | 156 | #define SHL__REAL_MULT(_max, _val, _factor) \ 157 | ({ \ 158 | (_factor == 0 || *(_val) <= (_max) / (_factor)) ? \ 159 | ((*(_val) *= (_factor)), 0) : \ 160 | -ERANGE; \ 161 | }) 162 | 163 | #define SHL__UPCAST_MULT(_type, _max, _val, _factor) \ 164 | ({ \ 165 | _type v = *(_val) * (_type)(_factor); \ 166 | (v <= (_max)) ? \ 167 | ((*(_val) = v), 0) : \ 168 | -ERANGE; \ 169 | }) 170 | 171 | static inline int shl_mult_ull(unsigned long long *val, 172 | unsigned long long factor) 173 | { 174 | return SHL__REAL_MULT(ULLONG_MAX, val, factor); 175 | } 176 | 177 | static inline int shl_mult_ul(unsigned long *val, unsigned long factor) 178 | { 179 | #if ULONG_MAX < ULLONG_MAX 180 | return SHL__UPCAST_MULT(unsigned long long, ULONG_MAX, val, factor); 181 | #else 182 | shl_assert_cc(sizeof(unsigned long) == sizeof(unsigned long long)); 183 | return shl_mult_ull((unsigned long long*)val, factor); 184 | #endif 185 | } 186 | 187 | static inline int shl_mult_u(unsigned int *val, unsigned int factor) 188 | { 189 | #if UINT_MAX < ULONG_MAX 190 | return SHL__UPCAST_MULT(unsigned long, UINT_MAX, val, factor); 191 | #elif UINT_MAX < ULLONG_MAX 192 | return SHL__UPCAST_MULT(unsigned long long, UINT_MAX, val, factor); 193 | #else 194 | shl_assert_cc(sizeof(unsigned int) == sizeof(unsigned long long)); 195 | return shl_mult_ull(val, factor); 196 | #endif 197 | } 198 | 199 | static inline int shl_mult_u64(uint64_t *val, uint64_t factor) 200 | { 201 | return SHL__REAL_MULT(UINT64_MAX, val, factor); 202 | } 203 | 204 | static inline int shl_mult_u32(uint32_t *val, uint32_t factor) 205 | { 206 | return SHL__UPCAST_MULT(uint_fast64_t, UINT32_MAX, val, factor); 207 | } 208 | 209 | static inline int shl_mult_u16(uint16_t *val, uint16_t factor) 210 | { 211 | return SHL__UPCAST_MULT(uint_fast32_t, UINT16_MAX, val, factor); 212 | } 213 | 214 | static inline int shl_mult_u8(uint8_t *val, uint8_t factor) 215 | { 216 | return SHL__UPCAST_MULT(uint_fast16_t, UINT8_MAX, val, factor); 217 | } 218 | 219 | #endif /* SHL_MACRO_H */ 220 | -------------------------------------------------------------------------------- /src/shl_ring.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Ring buffer 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Ring buffer 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include "shl_macro.h" 17 | #include "shl_ring.h" 18 | 19 | #define RING_MASK(_r, _v) ((_v) & ((_r)->size - 1)) 20 | 21 | void shl_ring_flush(struct shl_ring *r) 22 | { 23 | r->start = 0; 24 | r->used = 0; 25 | } 26 | 27 | void shl_ring_clear(struct shl_ring *r) 28 | { 29 | free(r->buf); 30 | memset(r, 0, sizeof(*r)); 31 | } 32 | 33 | /* 34 | * Get data pointers for current ring-buffer data. @vec must be an array of 2 35 | * iovec objects. They are filled according to the data available in the 36 | * ring-buffer. 0, 1 or 2 is returned according to the number of iovec objects 37 | * that were filled (0 meaning buffer is empty). 38 | * 39 | * Hint: "struct iovec" is defined in and looks like this: 40 | * struct iovec { 41 | * void *iov_base; 42 | * size_t iov_len; 43 | * }; 44 | */ 45 | size_t shl_ring_peek(struct shl_ring *r, struct iovec *vec) 46 | { 47 | if (r->used == 0) { 48 | return 0; 49 | } else if (r->start + r->used <= r->size) { 50 | if (vec) { 51 | vec[0].iov_base = &r->buf[r->start]; 52 | vec[0].iov_len = r->used; 53 | } 54 | return 1; 55 | } else { 56 | if (vec) { 57 | vec[0].iov_base = &r->buf[r->start]; 58 | vec[0].iov_len = r->size - r->start; 59 | vec[1].iov_base = r->buf; 60 | vec[1].iov_len = r->used - (r->size - r->start); 61 | } 62 | return 2; 63 | } 64 | } 65 | 66 | /* 67 | * Copy data from the ring buffer into the linear external buffer @buf. Copy 68 | * at most @size bytes. If the ring buffer size is smaller, copy less bytes and 69 | * return the number of bytes copied. 70 | */ 71 | size_t shl_ring_copy(struct shl_ring *r, void *buf, size_t size) 72 | { 73 | size_t l; 74 | 75 | if (size > r->used) 76 | size = r->used; 77 | 78 | if (size > 0) { 79 | l = r->size - r->start; 80 | if (size <= l) { 81 | memcpy(buf, &r->buf[r->start], size); 82 | } else { 83 | memcpy(buf, &r->buf[r->start], l); 84 | memcpy((uint8_t*)buf + l, r->buf, size - l); 85 | } 86 | } 87 | 88 | return size; 89 | } 90 | 91 | /* 92 | * Resize ring-buffer to size @nsize. @nsize must be a power-of-2, otherwise 93 | * ring operations will behave incorrectly. 94 | */ 95 | static int ring_resize(struct shl_ring *r, size_t nsize) 96 | { 97 | uint8_t *buf; 98 | size_t l; 99 | 100 | buf = malloc(nsize); 101 | if (!buf) 102 | return -ENOMEM; 103 | 104 | if (r->used > 0) { 105 | l = r->size - r->start; 106 | if (r->used <= l) { 107 | memcpy(buf, &r->buf[r->start], r->used); 108 | } else { 109 | memcpy(buf, &r->buf[r->start], l); 110 | memcpy(&buf[l], r->buf, r->used - l); 111 | } 112 | } 113 | 114 | free(r->buf); 115 | r->buf = buf; 116 | r->size = nsize; 117 | r->start = 0; 118 | 119 | return 0; 120 | } 121 | 122 | /* 123 | * Resize ring-buffer to provide enough room for @add bytes of new data. This 124 | * resizes the buffer if it is too small. It returns -ENOMEM on OOM and 0 on 125 | * success. 126 | */ 127 | static int ring_grow(struct shl_ring *r, size_t add) 128 | { 129 | size_t need; 130 | 131 | if (r->size - r->used >= add) 132 | return 0; 133 | 134 | need = r->used + add; 135 | if (need <= r->used) 136 | return -ENOMEM; 137 | else if (need < 4096) 138 | need = 4096; 139 | 140 | need = SHL_ALIGN_POWER2(need); 141 | if (need == 0) 142 | return -ENOMEM; 143 | 144 | return ring_resize(r, need); 145 | } 146 | 147 | /* 148 | * Push @len bytes from @u8 into the ring buffer. The buffer is resized if it 149 | * is too small. -ENOMEM is returned on OOM, 0 on success. 150 | */ 151 | int shl_ring_push(struct shl_ring *r, const void *u8, size_t size) 152 | { 153 | int err; 154 | size_t pos, l; 155 | 156 | if (size == 0) 157 | return 0; 158 | 159 | err = ring_grow(r, size); 160 | if (err < 0) 161 | return err; 162 | 163 | pos = RING_MASK(r, r->start + r->used); 164 | l = r->size - pos; 165 | if (l >= size) { 166 | memcpy(&r->buf[pos], u8, size); 167 | } else { 168 | memcpy(&r->buf[pos], u8, l); 169 | memcpy(r->buf, (const uint8_t*)u8 + l, size - l); 170 | } 171 | 172 | r->used += size; 173 | 174 | return 0; 175 | } 176 | 177 | /* 178 | * Remove @len bytes from the start of the ring-buffer. Note that we protect 179 | * against overflows so removing more bytes than available is safe. 180 | */ 181 | void shl_ring_pull(struct shl_ring *r, size_t size) 182 | { 183 | if (size > r->used) 184 | size = r->used; 185 | 186 | r->start = RING_MASK(r, r->start + size); 187 | r->used -= size; 188 | } 189 | -------------------------------------------------------------------------------- /src/shl_ring.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Ring buffer 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Ring buffer 10 | */ 11 | 12 | #ifndef SHL_RING_H 13 | #define SHL_RING_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | struct shl_ring { 22 | uint8_t *buf; /* buffer or NULL */ 23 | size_t size; /* actual size of @buf */ 24 | size_t start; /* start position of ring */ 25 | size_t used; /* number of actually used bytes */ 26 | }; 27 | 28 | /* flush buffer so it is empty again */ 29 | void shl_ring_flush(struct shl_ring *r); 30 | 31 | /* flush buffer, free allocated data and reset to initial state */ 32 | void shl_ring_clear(struct shl_ring *r); 33 | 34 | /* get pointers to buffer data and their length */ 35 | size_t shl_ring_peek(struct shl_ring *r, struct iovec *vec); 36 | 37 | /* copy data into external linear buffer */ 38 | size_t shl_ring_copy(struct shl_ring *r, void *buf, size_t size); 39 | 40 | /* push data to the end of the buffer */ 41 | int shl_ring_push(struct shl_ring *r, const void *u8, size_t size); 42 | 43 | /* pull data from the front of the buffer */ 44 | void shl_ring_pull(struct shl_ring *r, size_t size); 45 | 46 | /* return size of occupied buffer in bytes */ 47 | static inline size_t shl_ring_get_size(struct shl_ring *r) 48 | { 49 | return r->used; 50 | } 51 | 52 | #endif /* SHL_RING_H */ 53 | -------------------------------------------------------------------------------- /src/shl_util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Utility Helpers 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Utility Helpers 10 | */ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "shl_macro.h" 19 | #include "shl_util.h" 20 | 21 | /* 22 | * Strict atoi() 23 | * These helpers implement a strict version of atoi() (or strtol()). They only 24 | * parse digit/alpha characters. No whitespace or other characters are parsed. 25 | * The unsigned-variants explicitly forbid leading +/- signs. Use the signed 26 | * variants to allow these. 27 | * Base-prefix parsing is only done if base=0 is requested. Otherwise, 28 | * base-prefixes are forbidden. 29 | * The input string must be ASCII compatbile (which includes UTF8). 30 | * 31 | * We also always check for overflows and return errors (but continue parsing!) 32 | * so callers can catch it correctly. 33 | * 34 | * Additionally, we allow "length" parameters so strings do not necessarily have 35 | * to be zero-terminated. We have wrappers which skip this by passing strlen(). 36 | */ 37 | 38 | int shl_ctoi(char ch, unsigned int base) 39 | { 40 | unsigned int v; 41 | 42 | switch (ch) { 43 | case '0'...'9': 44 | v = ch - '0'; 45 | break; 46 | case 'a'...'z': 47 | v = ch - 'a' + 10; 48 | break; 49 | case 'A'...'Z': 50 | v = ch - 'A' + 10; 51 | break; 52 | default: 53 | return -EINVAL; 54 | } 55 | 56 | if (v >= base) 57 | return -EINVAL; 58 | 59 | return v; 60 | } 61 | 62 | /* figure out base and skip prefix */ 63 | static unsigned int shl__skip_base(const char **str, size_t *len) 64 | { 65 | if (*len > 1) { 66 | if ((*str)[0] == '0') { 67 | if (shl_ctoi((*str)[1], 8) >= 0) { 68 | *str += 1; 69 | *len -= 1; 70 | return 8; 71 | } 72 | } 73 | } 74 | 75 | if (*len > 2) { 76 | if ((*str)[0] == '0' && (*str)[1] == 'x') { 77 | if (shl_ctoi((*str)[2], 16) >= 0) { 78 | *str += 2; 79 | *len -= 2; 80 | return 16; 81 | } 82 | } 83 | } 84 | 85 | return 10; 86 | } 87 | 88 | int shl_atoi_ulln(const char *str, 89 | size_t len, 90 | unsigned int base, 91 | const char **next, 92 | unsigned long long *out) 93 | { 94 | bool huge; 95 | uint32_t val1; 96 | unsigned long long val2; 97 | size_t pos; 98 | int r, c; 99 | 100 | /* We use u32 as storage first so we have fast mult-overflow checks. We 101 | * cast up to "unsigned long long" once we exceed UINT32_MAX. Overflow 102 | * checks will get pretty slow for non-power2 bases, though. */ 103 | 104 | huge = false; 105 | val1 = 0; 106 | val2 = 0; 107 | r = 0; 108 | 109 | if (base > 36) { 110 | if (next) 111 | *next = str; 112 | if (out) 113 | *out = 0; 114 | return -EINVAL; 115 | } 116 | 117 | if (base == 0) 118 | base = shl__skip_base(&str, &len); 119 | 120 | for (pos = 0; pos < len; ++pos) { 121 | c = shl_ctoi(str[pos], base); 122 | if (c < 0) 123 | break; 124 | 125 | /* skip calculations on error */ 126 | if (r < 0) 127 | continue; 128 | 129 | if (!huge) { 130 | val2 = val1; 131 | r = shl_mult_u32(&val1, base); 132 | if (r >= 0 && val1 + c >= val1) 133 | val1 += c; 134 | else 135 | huge = true; 136 | } 137 | 138 | if (huge) { 139 | r = shl_mult_ull(&val2, base); 140 | if (r >= 0 && val2 + c >= val2) 141 | val2 += c; 142 | } 143 | } 144 | 145 | if (next) 146 | *next = (char*)&str[pos]; 147 | if (out) { 148 | if (r < 0) 149 | *out = ULLONG_MAX; 150 | else if (huge) 151 | *out = val2; 152 | else 153 | *out = val1; 154 | } 155 | 156 | return r; 157 | } 158 | 159 | int shl_atoi_uln(const char *str, 160 | size_t len, 161 | unsigned int base, 162 | const char **next, 163 | unsigned long *out) 164 | { 165 | unsigned long long val; 166 | int r; 167 | 168 | r = shl_atoi_ulln(str, len, base, next, &val); 169 | if (r >= 0 && val > ULONG_MAX) 170 | r = -ERANGE; 171 | 172 | if (out) 173 | *out = shl_min(val, (unsigned long long)ULONG_MAX); 174 | 175 | return r; 176 | } 177 | 178 | int shl_atoi_un(const char *str, 179 | size_t len, 180 | unsigned int base, 181 | const char **next, 182 | unsigned int *out) 183 | { 184 | unsigned long long val; 185 | int r; 186 | 187 | r = shl_atoi_ulln(str, len, base, next, &val); 188 | if (r >= 0 && val > UINT_MAX) 189 | r = -ERANGE; 190 | 191 | if (out) 192 | *out = shl_min(val, (unsigned long long)UINT_MAX); 193 | 194 | return r; 195 | } 196 | 197 | int shl_atoi_zn(const char *str, 198 | size_t len, 199 | unsigned int base, 200 | const char **next, 201 | size_t *out) 202 | { 203 | unsigned long long val; 204 | int r; 205 | 206 | r = shl_atoi_ulln(str, len, base, next, &val); 207 | if (r >= 0 && val > SIZE_MAX) 208 | r = -ERANGE; 209 | 210 | if (out) 211 | *out = shl_min(val, (unsigned long long)SIZE_MAX); 212 | 213 | return r; 214 | } 215 | 216 | /* 217 | * Greedy Realloc 218 | * The greedy-realloc helpers simplify power-of-2 buffer allocations. If you 219 | * have a dynamic array, simply use shl_greedy_realloc() for re-allocations 220 | * and it makes sure your buffer-size is always a multiple of 2 and is big 221 | * enough for your new entries. 222 | * Default size is 64, but you can initialize your buffer to a bigger default 223 | * if you need. 224 | */ 225 | 226 | void *shl_greedy_realloc(void **mem, size_t *size, size_t need) 227 | { 228 | size_t nsize; 229 | void *p; 230 | 231 | if (*size >= need) 232 | return *mem; 233 | 234 | nsize = SHL_ALIGN_POWER2(shl_max_t(size_t, 64U, need)); 235 | if (nsize == 0) 236 | return NULL; 237 | 238 | p = realloc(*mem, nsize); 239 | if (!p) 240 | return NULL; 241 | 242 | *mem = p; 243 | *size = nsize; 244 | return p; 245 | } 246 | 247 | void *shl_greedy_realloc0(void **mem, size_t *size, size_t need) 248 | { 249 | size_t prev = *size; 250 | uint8_t *p; 251 | 252 | p = shl_greedy_realloc(mem, size, need); 253 | if (!p) 254 | return NULL; 255 | 256 | if (*size > prev) 257 | shl_memzero(&p[prev], *size - prev); 258 | 259 | return p; 260 | } 261 | -------------------------------------------------------------------------------- /src/shl_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SHL - Utility Helpers 3 | * 4 | * Copyright (c) 2011-2013 David Herrmann 5 | * Dedicated to the Public Domain 6 | */ 7 | 8 | /* 9 | * Utility Helpers 10 | */ 11 | 12 | #ifndef SHL_UTIL_H 13 | #define SHL_UTIL_H 14 | 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | /* strict atoi */ 22 | 23 | int shl_ctoi(char ch, unsigned int base); 24 | 25 | int shl_atoi_ulln(const char *str, 26 | size_t len, 27 | unsigned int base, 28 | const char **next, 29 | unsigned long long *out); 30 | int shl_atoi_uln(const char *str, 31 | size_t len, 32 | unsigned int base, 33 | const char **next, 34 | unsigned long *out); 35 | int shl_atoi_un(const char *str, 36 | size_t len, 37 | unsigned int base, 38 | const char **next, 39 | unsigned int *out); 40 | int shl_atoi_zn(const char *str, 41 | size_t len, 42 | unsigned int base, 43 | const char **next, 44 | size_t *out); 45 | 46 | static inline int shl_atoi_ull(const char *str, 47 | unsigned int base, 48 | const char **next, 49 | unsigned long long *out) 50 | { 51 | return shl_atoi_ulln(str, strlen(str), base, next, out); 52 | } 53 | 54 | static inline int shl_atoi_ul(const char *str, 55 | unsigned int base, 56 | const char **next, 57 | unsigned long *out) 58 | { 59 | return shl_atoi_uln(str, strlen(str), base, next, out); 60 | } 61 | 62 | static inline int shl_atoi_u(const char *str, 63 | unsigned int base, 64 | const char **next, 65 | unsigned int *out) 66 | { 67 | return shl_atoi_un(str, strlen(str), base, next, out); 68 | } 69 | 70 | static inline int shl_atoi_z(const char *str, 71 | unsigned int base, 72 | const char **next, 73 | size_t *out) 74 | { 75 | return shl_atoi_zn(str, strlen(str), base, next, out); 76 | } 77 | 78 | /* greedy alloc */ 79 | 80 | void *shl_greedy_realloc(void **mem, size_t *size, size_t need); 81 | void *shl_greedy_realloc0(void **mem, size_t *size, size_t need); 82 | 83 | #endif /* SHL_UTIL_H */ 84 | -------------------------------------------------------------------------------- /src/wpa_ctrl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include "libwfd.h" 42 | #include "shl_macro.h" 43 | 44 | #define CTRL_PATH_TEMPLATE "/tmp/libwfd-wpa-ctrl-%d-%lu" 45 | #define REQ_REPLY_MAX 512 46 | 47 | #ifndef UNIX_PATH_MAX 48 | # define UNIX_PATH_MAX (sizeof(((struct sockaddr_un*)0)->sun_path)) 49 | #endif 50 | 51 | struct wfd_wpa_ctrl { 52 | unsigned long ref; 53 | wfd_wpa_ctrl_event_t event_fn; 54 | void *data; 55 | sigset_t mask; 56 | int efd; 57 | int tfd; 58 | 59 | int req_fd; 60 | char req_name[UNIX_PATH_MAX]; 61 | int ev_fd; 62 | char ev_name[UNIX_PATH_MAX]; 63 | }; 64 | 65 | static int wpa_request(int fd, const void *cmd, size_t cmd_len, 66 | void *reply, size_t *reply_len, int64_t *t2, 67 | const sigset_t *mask); 68 | static int wpa_request_ok(int fd, const void *cmd, size_t cmd_len, int64_t *t, 69 | const sigset_t *mask); 70 | 71 | static int64_t get_time_us(void) 72 | { 73 | int64_t t; 74 | struct timespec ts; 75 | 76 | clock_gettime(CLOCK_MONOTONIC, &ts); 77 | t = ts.tv_sec * 1000LL * 1000LL; 78 | t += ts.tv_nsec / 1000LL; 79 | 80 | return t; 81 | } 82 | 83 | static void us_to_timespec(struct timespec *ts, int64_t us) 84 | { 85 | ts->tv_sec = us / (1000LL * 1000LL); 86 | ts->tv_nsec = (us % (1000LL * 1000LL)) * 1000LL; 87 | } 88 | 89 | _shl_public_ 90 | int wfd_wpa_ctrl_new(wfd_wpa_ctrl_event_t event_fn, void *data, 91 | struct wfd_wpa_ctrl **out) 92 | { 93 | struct wfd_wpa_ctrl *wpa; 94 | struct epoll_event ev; 95 | int r; 96 | 97 | if (!out || !event_fn) 98 | return -EINVAL; 99 | 100 | wpa = calloc(1, sizeof(*wpa)); 101 | if (!wpa) 102 | return -ENOMEM; 103 | wpa->ref = 1; 104 | wpa->event_fn = event_fn; 105 | wpa->data = data; 106 | wpa->efd = -1; 107 | wpa->tfd = -1; 108 | wpa->req_fd = -1; 109 | wpa->ev_fd = -1; 110 | sigemptyset(&wpa->mask); 111 | 112 | wpa->efd = epoll_create1(EPOLL_CLOEXEC); 113 | if (wpa->efd < 0) { 114 | r = -errno; 115 | goto err_wpa; 116 | } 117 | 118 | wpa->tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK); 119 | if (wpa->tfd < 0) { 120 | r = -errno; 121 | goto err_efd; 122 | } 123 | 124 | memset(&ev, 0, sizeof(ev)); 125 | ev.events = EPOLLHUP | EPOLLERR | EPOLLIN; 126 | ev.data.ptr = &wpa->tfd; 127 | 128 | r = epoll_ctl(wpa->efd, EPOLL_CTL_ADD, wpa->tfd, &ev); 129 | if (r < 0) { 130 | r = -errno; 131 | goto err_tfd; 132 | } 133 | 134 | *out = wpa; 135 | return 0; 136 | 137 | err_tfd: 138 | close(wpa->tfd); 139 | err_efd: 140 | close(wpa->efd); 141 | err_wpa: 142 | free(wpa); 143 | return r; 144 | } 145 | 146 | _shl_public_ 147 | void wfd_wpa_ctrl_ref(struct wfd_wpa_ctrl *wpa) 148 | { 149 | if (!wpa || !wpa->ref) 150 | return; 151 | 152 | ++wpa->ref; 153 | } 154 | 155 | _shl_public_ 156 | void wfd_wpa_ctrl_unref(struct wfd_wpa_ctrl *wpa) 157 | { 158 | if (!wpa || !wpa->ref || --wpa->ref) 159 | return; 160 | 161 | wfd_wpa_ctrl_close(wpa); 162 | close(wpa->tfd); 163 | close(wpa->efd); 164 | free(wpa); 165 | } 166 | 167 | _shl_public_ 168 | void wfd_wpa_ctrl_set_data(struct wfd_wpa_ctrl *wpa, void *data) 169 | { 170 | if (!wpa) 171 | return; 172 | 173 | wpa->data = data; 174 | } 175 | 176 | _shl_public_ 177 | void *wfd_wpa_ctrl_get_data(struct wfd_wpa_ctrl *wpa) 178 | { 179 | if (!wpa) 180 | return NULL; 181 | 182 | return wpa->data; 183 | } 184 | 185 | static int bind_socket(int fd, char *name) 186 | { 187 | static unsigned long real_counter; 188 | unsigned long counter; 189 | struct sockaddr_un src; 190 | int r; 191 | bool tried = false; 192 | 193 | /* TODO: make wpa_supplicant allow unbound clients */ 194 | 195 | /* Yes, this counter is racy, but wpa_supplicant doesn't provide support 196 | * for unbound clients (it crashes..). We could add a current-time based 197 | * random part, but that might leave stupid pipes around in /tmp. So 198 | * lets just use this internal counter and blame 199 | * wpa_supplicant.. Yey! */ 200 | counter = real_counter++; 201 | 202 | memset(&src, 0, sizeof(src)); 203 | src.sun_family = AF_UNIX; 204 | 205 | /* Ugh! mktemp() is racy but stupid wpa_supplicant requires us to bind 206 | * to a real file. Older versions will segfault if we don't... */ 207 | snprintf(name, UNIX_PATH_MAX - 1, CTRL_PATH_TEMPLATE, 208 | (int)getpid(), counter); 209 | name[UNIX_PATH_MAX - 1] = 0; 210 | 211 | try_again: 212 | strcpy(src.sun_path, name); 213 | 214 | r = bind(fd, (struct sockaddr*)&src, sizeof(src)); 215 | if (r < 0) { 216 | if (errno == EADDRINUSE && !tried) { 217 | tried = true; 218 | unlink(name); 219 | goto try_again; 220 | } 221 | 222 | return -errno; 223 | } 224 | 225 | return 0; 226 | } 227 | 228 | static int connect_socket(int fd, const char *ctrl_path) 229 | { 230 | int r; 231 | struct sockaddr_un dst; 232 | size_t len; 233 | 234 | memset(&dst, 0, sizeof(dst)); 235 | dst.sun_family = AF_UNIX; 236 | 237 | len = strlen(ctrl_path); 238 | if (!strncmp(ctrl_path, "@abstract:", 10)) { 239 | if (len > sizeof(dst.sun_path) - 2) 240 | return -EINVAL; 241 | 242 | dst.sun_path[0] = 0; 243 | dst.sun_path[sizeof(dst.sun_path) - 1] = 0; 244 | strncpy(&dst.sun_path[1], &ctrl_path[10], 245 | sizeof(dst.sun_path) - 2); 246 | } else { 247 | if (len > sizeof(dst.sun_path) - 1) 248 | return -EINVAL; 249 | 250 | dst.sun_path[sizeof(dst.sun_path) - 1] = 0; 251 | strncpy(dst.sun_path, ctrl_path, sizeof(dst.sun_path) - 1); 252 | } 253 | 254 | r = connect(fd, (struct sockaddr*)&dst, sizeof(dst)); 255 | if (r < 0) 256 | return -errno; 257 | 258 | return 0; 259 | } 260 | 261 | static int open_socket(struct wfd_wpa_ctrl *wpa, const char *ctrl_path, 262 | void *data, char *name) 263 | { 264 | int fd, r; 265 | struct epoll_event ev; 266 | 267 | fd = socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0); 268 | if (fd < 0) 269 | return -errno; 270 | 271 | r = bind_socket(fd, name); 272 | if (r < 0) 273 | goto err_fd; 274 | 275 | r = connect_socket(fd, ctrl_path); 276 | if (r < 0) 277 | goto err_name; 278 | 279 | memset(&ev, 0, sizeof(ev)); 280 | ev.events = EPOLLHUP | EPOLLERR | EPOLLIN; 281 | ev.data.ptr = data; 282 | r = epoll_ctl(wpa->efd, EPOLL_CTL_ADD, fd, &ev); 283 | if (r < 0) { 284 | r = -errno; 285 | goto err_name; 286 | } 287 | 288 | return fd; 289 | 290 | err_name: 291 | unlink(name); 292 | err_fd: 293 | close(fd); 294 | return r; 295 | } 296 | 297 | static void close_socket(struct wfd_wpa_ctrl *wpa, int fd, char *name) 298 | { 299 | epoll_ctl(wpa->efd, EPOLL_CTL_DEL, fd, NULL); 300 | unlink(name); 301 | close(fd); 302 | } 303 | 304 | static int arm_timer(struct wfd_wpa_ctrl *wpa, int64_t usecs) 305 | { 306 | struct itimerspec spec; 307 | int r; 308 | 309 | us_to_timespec(&spec.it_value, usecs); 310 | spec.it_interval = spec.it_value; 311 | 312 | r = timerfd_settime(wpa->tfd, 0, &spec, NULL); 313 | if (r < 0) 314 | return -errno; 315 | 316 | return 0; 317 | } 318 | 319 | static void disarm_timer(struct wfd_wpa_ctrl *wpa) 320 | { 321 | arm_timer(wpa, 0); 322 | } 323 | 324 | _shl_public_ 325 | int wfd_wpa_ctrl_open(struct wfd_wpa_ctrl *wpa, const char *ctrl_path) 326 | { 327 | int r; 328 | int64_t t = 1000LL * 10; /* 10ms */ 329 | 330 | if (!wpa || !ctrl_path) 331 | return -EINVAL; 332 | if (wfd_wpa_ctrl_is_open(wpa)) 333 | return -EALREADY; 334 | 335 | /* 10s PING timer for timeouts */ 336 | r = arm_timer(wpa, 10000000LL); 337 | if (r < 0) 338 | return r; 339 | 340 | wpa->req_fd = open_socket(wpa, ctrl_path, &wpa->req_fd, wpa->req_name); 341 | if (wpa->req_fd < 0) { 342 | r = wpa->req_fd; 343 | goto err_timer; 344 | } 345 | 346 | wpa->ev_fd = open_socket(wpa, ctrl_path, &wpa->ev_fd, wpa->ev_name); 347 | if (wpa->ev_fd < 0) { 348 | r = wpa->ev_fd; 349 | goto err_req; 350 | } 351 | 352 | r = wpa_request_ok(wpa->ev_fd, "ATTACH", 6, NULL, &wpa->mask); 353 | if (r < 0) 354 | goto err_ev; 355 | 356 | return 0; 357 | 358 | err_ev: 359 | wpa_request_ok(wpa->ev_fd, "DETACH", 6, &t, &wpa->mask); 360 | close_socket(wpa, wpa->ev_fd, wpa->ev_name); 361 | wpa->ev_fd = -1; 362 | err_req: 363 | close_socket(wpa, wpa->req_fd, wpa->req_name); 364 | wpa->req_fd = -1; 365 | err_timer: 366 | disarm_timer(wpa); 367 | return r; 368 | } 369 | 370 | _shl_public_ 371 | void wfd_wpa_ctrl_close(struct wfd_wpa_ctrl *wpa) 372 | { 373 | int64_t t = 1000LL * 10; /* 10ms */ 374 | 375 | if (!wpa || !wfd_wpa_ctrl_is_open(wpa)) 376 | return; 377 | 378 | wpa_request_ok(wpa->ev_fd, "DETACH", 6, &t, &wpa->mask); 379 | 380 | close_socket(wpa, wpa->ev_fd, wpa->ev_name); 381 | wpa->ev_fd = -1; 382 | 383 | close_socket(wpa, wpa->req_fd, wpa->req_name); 384 | wpa->req_fd = -1; 385 | 386 | disarm_timer(wpa); 387 | } 388 | 389 | _shl_public_ 390 | bool wfd_wpa_ctrl_is_open(struct wfd_wpa_ctrl *wpa) 391 | { 392 | return wpa && wpa->ev_fd >= 0; 393 | } 394 | 395 | _shl_public_ 396 | int wfd_wpa_ctrl_get_fd(struct wfd_wpa_ctrl *wpa) 397 | { 398 | return wpa ? wpa->efd : -1; 399 | } 400 | 401 | _shl_public_ 402 | void wfd_wpa_ctrl_set_sigmask(struct wfd_wpa_ctrl *wpa, const sigset_t *mask) 403 | { 404 | if (!wpa || !mask) 405 | return; 406 | 407 | memcpy(&wpa->mask, mask, sizeof(sigset_t)); 408 | } 409 | 410 | static int read_ev(struct wfd_wpa_ctrl *wpa) 411 | { 412 | char buf[REQ_REPLY_MAX + 1]; 413 | ssize_t l; 414 | 415 | do { 416 | l = recv(wpa->ev_fd, buf, sizeof(buf) - 1, MSG_DONTWAIT); 417 | if (l < 0) { 418 | if (errno == EAGAIN || errno == EINTR) 419 | return 0; 420 | else 421 | return -errno; 422 | } else if (l > 0) { 423 | if (l > sizeof(buf) - 1) 424 | l = sizeof(buf) - 1; 425 | buf[l] = 0; 426 | 427 | /* only handle event-msgs ('<') on ev-socket */ 428 | if (*buf == '<') 429 | wpa->event_fn(wpa, wpa->data, buf, l); 430 | 431 | /* exit if the callback closed the connection */ 432 | if (!wfd_wpa_ctrl_is_open(wpa)) 433 | return -ENODEV; 434 | } 435 | } while (l > 0); 436 | 437 | return 0; 438 | } 439 | 440 | static int dispatch_ev(struct wfd_wpa_ctrl *wpa, const struct epoll_event *e) 441 | { 442 | int r; 443 | 444 | if (e->events & EPOLLIN) { 445 | r = read_ev(wpa); 446 | if (r < 0) 447 | return r; 448 | } 449 | 450 | /* handle HUP/ERR last so we drain input first */ 451 | if (e->events & (EPOLLHUP | EPOLLERR)) 452 | return -EPIPE; 453 | 454 | return 0; 455 | } 456 | 457 | static int read_req(struct wfd_wpa_ctrl *wpa) 458 | { 459 | char buf[REQ_REPLY_MAX]; 460 | ssize_t l; 461 | 462 | /* 463 | * Drain input queue on req-socket; we're not interested in spurious 464 | * events on this fd so ignore any data. 465 | */ 466 | 467 | do { 468 | l = recv(wpa->req_fd, buf, sizeof(buf), MSG_DONTWAIT); 469 | if (l < 0) { 470 | if (errno == EAGAIN || errno == EINTR) 471 | return 0; 472 | else 473 | return -errno; 474 | } 475 | } while (l > 0); 476 | 477 | return 0; 478 | } 479 | 480 | static int dispatch_req(struct wfd_wpa_ctrl *wpa, const struct epoll_event *e) 481 | { 482 | int r; 483 | 484 | if (e->events & EPOLLIN) { 485 | r = read_req(wpa); 486 | if (r < 0) 487 | return r; 488 | } 489 | 490 | /* handle HUP/ERR last so we drain input first */ 491 | if (e->events & (EPOLLHUP | EPOLLERR)) 492 | return -EPIPE; 493 | 494 | return 0; 495 | } 496 | 497 | static int read_tfd(struct wfd_wpa_ctrl *wpa) 498 | { 499 | ssize_t l; 500 | uint64_t exp; 501 | int r; 502 | char buf[10]; 503 | size_t len = sizeof(buf); 504 | 505 | /* Send PING request if the timer expires. If the wpa_supplicant 506 | * doesn't respond in a timely manner, return an error. */ 507 | 508 | l = read(wpa->tfd, &exp, sizeof(exp)); 509 | if (l < 0 && errno != EAGAIN && errno != EINTR) { 510 | return -errno; 511 | } else if (l == sizeof(exp)) { 512 | r = wpa_request(wpa->req_fd, "PING", 4, buf, &len, NULL, 513 | &wpa->mask); 514 | if (r < 0) 515 | return r; 516 | if (len != 5 || strncmp(buf, "PONG\n", 5)) 517 | return -ETIMEDOUT; 518 | } 519 | 520 | return 0; 521 | } 522 | 523 | static int dispatch_tfd(struct wfd_wpa_ctrl *wpa, const struct epoll_event *e) 524 | { 525 | int r = 0; 526 | 527 | /* Remove tfd from epoll-set on HUP/ERR. This shouldn't happen, but if 528 | * it does, just stop receiving events from it. */ 529 | if (e->events & (EPOLLHUP | EPOLLERR)) { 530 | r = -EFAULT; 531 | goto error; 532 | } 533 | 534 | if (e->events & (EPOLLIN)) { 535 | r = read_tfd(wpa); 536 | if (r < 0) 537 | goto error; 538 | } 539 | 540 | return 0; 541 | 542 | error: 543 | epoll_ctl(wpa->efd, EPOLL_CTL_DEL, wpa->tfd, NULL); 544 | return r; 545 | } 546 | 547 | _shl_public_ 548 | int wfd_wpa_ctrl_dispatch(struct wfd_wpa_ctrl *wpa, int timeout) 549 | { 550 | struct epoll_event ev[2], *e; 551 | int r, n, i; 552 | const size_t max = sizeof(ev) / sizeof(*ev); 553 | 554 | if (!wfd_wpa_ctrl_is_open(wpa)) 555 | return -ENODEV; 556 | 557 | n = epoll_wait(wpa->efd, ev, max, timeout); 558 | if (n < 0) { 559 | if (errno == EAGAIN || errno == EINTR) 560 | return 0; 561 | else 562 | return -errno; 563 | } else if (n > max) { 564 | n = max; 565 | } 566 | 567 | wfd_wpa_ctrl_ref(wpa); 568 | 569 | r = 0; 570 | for (i = 0; i < n; ++i) { 571 | e = &ev[i]; 572 | if (e->data.ptr == &wpa->ev_fd) 573 | r = dispatch_ev(wpa, e); 574 | else if (e->data.ptr == &wpa->req_fd) 575 | r = dispatch_req(wpa, e); 576 | else if (e->data.ptr == &wpa->tfd) 577 | r = dispatch_tfd(wpa, e); 578 | 579 | if (r < 0) 580 | break; 581 | } 582 | 583 | wfd_wpa_ctrl_unref(wpa); 584 | 585 | return r; 586 | } 587 | 588 | static int timed_send(int fd, const void *cmd, size_t cmd_len, 589 | int64_t *timeout, const sigset_t *mask) 590 | { 591 | bool done = false; 592 | int64_t start, t; 593 | ssize_t l; 594 | int n; 595 | struct pollfd fds[1]; 596 | const size_t max = sizeof(fds) / sizeof(*fds); 597 | struct timespec ts; 598 | 599 | start = get_time_us(); 600 | 601 | do { 602 | memset(fds, 0, sizeof(fds)); 603 | fds[0].fd = fd; 604 | fds[0].events = POLLHUP | POLLERR | POLLOUT; 605 | 606 | us_to_timespec(&ts, *timeout); 607 | n = ppoll(fds, max, &ts, mask); 608 | if (n < 0 && errno != EAGAIN) { 609 | return -errno; 610 | } else if (!n) { 611 | return -ETIMEDOUT; 612 | } else if (n > 0) { 613 | if (fds[0].revents & (POLLHUP | POLLERR)) 614 | return -EPIPE; 615 | 616 | l = send(fd, cmd, cmd_len, MSG_NOSIGNAL); 617 | if (l < 0 && errno != EAGAIN && errno != EINTR) { 618 | return -errno; 619 | } else if (l > 0) { 620 | /* We don't care how much was sent. If we 621 | * couldn't send the whole datagram, we still 622 | * try to recv the error reply from 623 | * wpa_supplicant. There's no way to split 624 | * datagrams so we're screwed in that case. */ 625 | done = true; 626 | } 627 | } 628 | 629 | /* recalculate remaining timeout */ 630 | t = *timeout - (get_time_us() - start); 631 | if (t <= 0) { 632 | *timeout = 0; 633 | if (!done) 634 | return -ETIMEDOUT; 635 | } else { 636 | *timeout = t; 637 | } 638 | } while (!done); 639 | 640 | return 0; 641 | } 642 | 643 | static int timed_recv(int fd, void *reply, size_t *reply_len, int64_t *timeout, 644 | const sigset_t *mask) 645 | { 646 | bool done = false; 647 | int64_t start, t; 648 | ssize_t l; 649 | int n; 650 | struct pollfd fds[1]; 651 | const size_t max = sizeof(fds) / sizeof(*fds); 652 | struct timespec ts; 653 | 654 | start = get_time_us(); 655 | 656 | do { 657 | memset(fds, 0, sizeof(fds)); 658 | fds[0].fd = fd; 659 | fds[0].events = POLLHUP | POLLERR | POLLIN; 660 | 661 | us_to_timespec(&ts, *timeout); 662 | n = ppoll(fds, max, &ts, mask); 663 | if (n < 0 && errno != EAGAIN) { 664 | return -errno; 665 | } else if (!n) { 666 | return -ETIMEDOUT; 667 | } else if (n > 0) { 668 | if (fds[0].revents & (POLLHUP | POLLERR)) 669 | return -EPIPE; 670 | 671 | l = recv(fd, reply, *reply_len, MSG_DONTWAIT); 672 | if (l < 0 && errno != EAGAIN && errno != EINTR) { 673 | return -errno; 674 | } else if (l > 0 && *(char*)reply != '<') { 675 | /* We ignore any event messages ('<') on this 676 | * fd as they're handled via a separate pipe. 677 | * Return the received reply unchanged. */ 678 | *reply_len = (l > *reply_len) ? *reply_len : l; 679 | done = true; 680 | } 681 | } 682 | 683 | /* recalculate remaining timeout */ 684 | t = *timeout - (get_time_us() - start); 685 | if (t <= 0) { 686 | *timeout = 0; 687 | if (!done) 688 | return -ETIMEDOUT; 689 | } else { 690 | *timeout = t; 691 | } 692 | } while (!done); 693 | 694 | return 0; 695 | } 696 | 697 | static int wpa_request(int fd, const void *cmd, size_t cmd_len, 698 | void *reply, size_t *reply_len, int64_t *t2, 699 | const sigset_t *mask) 700 | { 701 | char buf[REQ_REPLY_MAX]; 702 | size_t l = REQ_REPLY_MAX; 703 | int64_t *t, t1 = -1, max; 704 | int r; 705 | 706 | if (!cmd || !cmd_len) 707 | return -EINVAL; 708 | if (fd < 0) 709 | return -ENODEV; 710 | 711 | if (t2) 712 | t = t2; 713 | else 714 | t = &t1; 715 | if (!reply) 716 | reply = buf; 717 | if (!reply_len) 718 | reply_len = &l; 719 | if (*reply_len < 2) 720 | return -EINVAL; 721 | *reply_len -= 1; 722 | 723 | /* use a maximum of 1s */ 724 | max = 1LL * 1000LL * 1000LL; 725 | if (*t < 0 || *t > max) 726 | *t = max; 727 | 728 | /* send() with timeout */ 729 | r = timed_send(fd, cmd, cmd_len, t, mask); 730 | if (r < 0) 731 | return r; 732 | 733 | /* recv() with timeout */ 734 | r = timed_recv(fd, reply, reply_len, t, mask); 735 | if (r < 0) 736 | return r; 737 | ((char*)reply)[*reply_len] = 0; 738 | 739 | return 0; 740 | } 741 | 742 | static int wpa_request_ok(int fd, const void *cmd, size_t cmd_len, int64_t *t, 743 | const sigset_t *mask) 744 | { 745 | char buf[REQ_REPLY_MAX]; 746 | size_t l = REQ_REPLY_MAX; 747 | int r; 748 | 749 | r = wpa_request(fd, cmd, cmd_len, buf, &l, t, mask); 750 | if (r < 0) 751 | return r; 752 | 753 | if (l != 3 || strncmp(buf, "OK\n", 3)) 754 | return -EINVAL; 755 | 756 | return 0; 757 | } 758 | 759 | _shl_public_ 760 | int wfd_wpa_ctrl_request(struct wfd_wpa_ctrl *wpa, const void *cmd, 761 | size_t cmd_len, void *reply, size_t *reply_len, 762 | int timeout) 763 | { 764 | int64_t t; 765 | 766 | if (!wpa) 767 | return -EINVAL; 768 | if (!wfd_wpa_ctrl_is_open(wpa)) 769 | return -ENODEV; 770 | 771 | /* prevent mult-overflow */ 772 | if (timeout < 0) 773 | timeout = -1; 774 | else if (timeout > 1000000) 775 | timeout = 1000000; 776 | t = timeout * 1000LL; 777 | 778 | return wpa_request(wpa->req_fd, cmd, cmd_len, reply, reply_len, &t, 779 | &wpa->mask); 780 | } 781 | 782 | _shl_public_ 783 | int wfd_wpa_ctrl_request_ok(struct wfd_wpa_ctrl *wpa, const void *cmd, 784 | size_t cmd_len, int timeout) 785 | { 786 | char buf[REQ_REPLY_MAX]; 787 | size_t l = REQ_REPLY_MAX; 788 | int r; 789 | 790 | if (!wpa) 791 | return -EINVAL; 792 | 793 | r = wfd_wpa_ctrl_request(wpa, cmd, cmd_len, buf, &l, timeout); 794 | if (r < 0) 795 | return r; 796 | 797 | if (l != 3 || strncmp(buf, "OK\n", 3)) 798 | return -EINVAL; 799 | 800 | return 0; 801 | } 802 | -------------------------------------------------------------------------------- /src/wpa_parser.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "libwfd.h" 31 | #include "shl_macro.h" 32 | 33 | _shl_public_ 34 | void wfd_wpa_event_init(struct wfd_wpa_event *ev) 35 | { 36 | if (!ev) 37 | return; 38 | 39 | memset(ev, 0, sizeof(*ev)); 40 | } 41 | 42 | _shl_public_ 43 | void wfd_wpa_event_reset(struct wfd_wpa_event *ev) 44 | { 45 | if (!ev) 46 | return; 47 | 48 | free(ev->raw); 49 | 50 | switch (ev->type) { 51 | case WFD_WPA_EVENT_P2P_DEVICE_FOUND: 52 | free(ev->p.p2p_device_found.name); 53 | break; 54 | case WFD_WPA_EVENT_P2P_GROUP_STARTED: 55 | free(ev->p.p2p_group_started.ifname); 56 | break; 57 | case WFD_WPA_EVENT_P2P_PROV_DISC_SHOW_PIN: 58 | free(ev->p.p2p_prov_disc_show_pin.pin); 59 | break; 60 | default: 61 | break; 62 | } 63 | 64 | memset(ev, 0, sizeof(*ev)); 65 | } 66 | 67 | static const struct event_type { 68 | const char *name; 69 | size_t len; 70 | unsigned int code; 71 | } event_list[] = { 72 | 73 | #define EVENT(_name, _suffix) { \ 74 | .name = _name, \ 75 | .len = sizeof(_name) - 1, \ 76 | .code = WFD_WPA_EVENT_ ## _suffix \ 77 | } 78 | 79 | /* MUST BE ORDERED ALPHABETICALLY FOR BINARY SEARCH! */ 80 | 81 | EVENT("AP-STA-CONNECTED", AP_STA_CONNECTED), 82 | EVENT("AP-STA-DISCONNECTED", AP_STA_DISCONNECTED), 83 | EVENT("CTRL-EVENT-SCAN-STARTED", CTRL_EVENT_SCAN_STARTED), 84 | EVENT("CTRL-EVENT-TERMINATING", CTRL_EVENT_TERMINATING), 85 | EVENT("P2P-DEVICE-FOUND", P2P_DEVICE_FOUND), 86 | EVENT("P2P-DEVICE-LOST", P2P_DEVICE_LOST), 87 | EVENT("P2P-FIND-STOPPED", P2P_FIND_STOPPED), 88 | EVENT("P2P-GO-NEG-FAILURE", P2P_GO_NEG_FAILURE), 89 | EVENT("P2P-GO-NEG-REQUEST", P2P_GO_NEG_REQUEST), 90 | EVENT("P2P-GO-NEG-SUCCESS", P2P_GO_NEG_SUCCESS), 91 | EVENT("P2P-GROUP-FORMATION-FAILURE", P2P_GROUP_FORMATION_FAILURE), 92 | EVENT("P2P-GROUP-FORMATION-SUCCESS", P2P_GROUP_FORMATION_SUCCESS), 93 | EVENT("P2P-GROUP-REMOVED", P2P_GROUP_REMOVED), 94 | EVENT("P2P-GROUP-STARTED", P2P_GROUP_STARTED), 95 | EVENT("P2P-INVITATION-RECEIVED", P2P_INVITATION_RECEIVED), 96 | EVENT("P2P-INVITATION-RESULT", P2P_INVITATION_RESULT), 97 | EVENT("P2P-PROV-DISC-ENTER-PIN", P2P_PROV_DISC_ENTER_PIN), 98 | EVENT("P2P-PROV-DISC-PBC-REQ", P2P_PROV_DISC_PBC_REQ), 99 | EVENT("P2P-PROV-DISC-PBC-RESP", P2P_PROV_DISC_PBC_RESP), 100 | EVENT("P2P-PROV-DISC-SHOW-PIN", P2P_PROV_DISC_SHOW_PIN), 101 | EVENT("P2P-SERV-DISC-REQ", P2P_SERV_DISC_REQ), 102 | EVENT("P2P-SERV-DISC-RESP", P2P_SERV_DISC_RESP), 103 | 104 | #undef EVENT 105 | }; 106 | 107 | _shl_public_ 108 | const char *wfd_wpa_event_name(unsigned int type) 109 | { 110 | size_t i, max; 111 | 112 | max = sizeof(event_list) / sizeof(*event_list); 113 | 114 | for (i = 0; i < max; ++i) { 115 | if (event_list[i].code == type) 116 | return event_list[i].name; 117 | } 118 | 119 | return "UNKNOWN"; 120 | } 121 | 122 | static int event_comp(const void *key, const void *type) 123 | { 124 | const struct event_type *t; 125 | const char *k; 126 | int r; 127 | 128 | k = key; 129 | t = type; 130 | 131 | r = strncmp(k, t->name, t->len); 132 | if (r) 133 | return r; 134 | 135 | if (k[t->len] != 0 && k[t->len] != ' ') 136 | return 1; 137 | 138 | return 0; 139 | } 140 | 141 | static char *tokenize(const char *src, size_t *num) 142 | { 143 | char *buf, *dst; 144 | char last_c; 145 | size_t n; 146 | bool quoted, escaped; 147 | 148 | buf = malloc(strlen(src) + 1); 149 | if (!buf) 150 | return NULL; 151 | 152 | dst = buf; 153 | last_c = 0; 154 | n = 0; 155 | *dst = 0; 156 | quoted = 0; 157 | escaped = 0; 158 | 159 | for ( ; *src; ++src) { 160 | if (quoted) { 161 | if (escaped) { 162 | escaped = 0; 163 | last_c = *src; 164 | *dst++ = last_c; 165 | } else if (*src == '\'') { 166 | quoted = 0; 167 | } else if (*src == '\\') { 168 | escaped = 1; 169 | } else { 170 | last_c = *src; 171 | *dst++ = last_c; 172 | } 173 | } else { 174 | if (*src == ' ' || 175 | *src == '\n' || 176 | *src == '\t' || 177 | *src == '\r') { 178 | if (last_c) { 179 | *dst++ = 0; 180 | ++n; 181 | } 182 | last_c = 0; 183 | } else if (*src == '\'') { 184 | quoted = 1; 185 | escaped = 0; 186 | last_c = *src; 187 | } else { 188 | last_c = *src; 189 | *dst++ = last_c; 190 | } 191 | } 192 | } 193 | 194 | if (last_c) { 195 | *dst = 0; 196 | ++n; 197 | } 198 | 199 | *num = n; 200 | return buf; 201 | } 202 | 203 | static int parse_mac(char *buf, const char *src) 204 | { 205 | int r, a1, a2, a3, a4, a5, a6; 206 | 207 | if (strlen(src) > 17) 208 | return -EINVAL; 209 | 210 | r = sscanf(src, "%2x:%2x:%2x:%2x:%2x:%2x", 211 | &a1, &a2, &a3, &a4, &a5, &a6); 212 | if (r != 6) 213 | return -EINVAL; 214 | 215 | strcpy(buf, src); 216 | return 0; 217 | } 218 | 219 | static int parse_ap_sta_connected(struct wfd_wpa_event *ev, 220 | char *tokens, size_t num) 221 | { 222 | int r; 223 | size_t i; 224 | 225 | if (num < 1) 226 | return -EINVAL; 227 | 228 | r = parse_mac(ev->p.ap_sta_connected.iface, tokens); 229 | if (r < 0) 230 | return r; 231 | 232 | for (i = 1; i < num; ++i, tokens += strlen(tokens) + 1) { 233 | if (strncmp(tokens, "p2p_dev_addr=", 13)) 234 | continue; 235 | 236 | r = parse_mac(ev->p.ap_sta_connected.mac, &tokens[13]); 237 | if (r < 0) 238 | return r; 239 | 240 | return 0; 241 | } 242 | 243 | return 0; 244 | } 245 | 246 | static int parse_ap_sta_disconnected(struct wfd_wpa_event *ev, 247 | char *tokens, size_t num) 248 | { 249 | int r; 250 | size_t i; 251 | 252 | if (num < 1) 253 | return -EINVAL; 254 | 255 | r = parse_mac(ev->p.ap_sta_disconnected.iface, tokens); 256 | if (r < 0) 257 | return r; 258 | 259 | for (i = 1; i < num; ++i, tokens += strlen(tokens) + 1) { 260 | if (strncmp(tokens, "p2p_dev_addr=", 13)) 261 | continue; 262 | 263 | r = parse_mac(ev->p.ap_sta_disconnected.mac, &tokens[13]); 264 | if (r < 0) 265 | return r; 266 | 267 | return 0; 268 | } 269 | 270 | return 0; 271 | } 272 | 273 | static int parse_p2p_device_found(struct wfd_wpa_event *ev, 274 | char *tokens, size_t num) 275 | { 276 | int r; 277 | size_t i; 278 | 279 | if (num < 2) 280 | return -EINVAL; 281 | 282 | r = parse_mac(ev->p.p2p_device_found.peer_mac, tokens); 283 | if (r < 0) 284 | return r; 285 | 286 | tokens += strlen(tokens) + 1; 287 | for (i = 1; i < num; ++i, tokens += strlen(tokens) + 1) { 288 | if (strncmp(tokens, "name=", 5)) 289 | continue; 290 | 291 | ev->p.p2p_device_found.name = strdup(&tokens[5]); 292 | if (!ev->p.p2p_device_found.name) 293 | return -ENOMEM; 294 | 295 | return 0; 296 | } 297 | 298 | return -EINVAL; 299 | } 300 | 301 | static int parse_p2p_device_lost(struct wfd_wpa_event *ev, 302 | char *tokens, size_t num) 303 | { 304 | int r; 305 | size_t i; 306 | 307 | if (num < 1) 308 | return -EINVAL; 309 | 310 | for (i = 0; i < num; ++i, tokens += strlen(tokens) + 1) { 311 | if (strncmp(tokens, "p2p_dev_addr=", 13)) 312 | continue; 313 | 314 | r = parse_mac(ev->p.p2p_device_lost.peer_mac, &tokens[13]); 315 | if (r < 0) 316 | return r; 317 | 318 | return 0; 319 | } 320 | 321 | return 0; 322 | } 323 | 324 | static int parse_p2p_go_neg_success(struct wfd_wpa_event *ev, 325 | char *tokens, size_t num) 326 | { 327 | int r; 328 | size_t i; 329 | bool has_role = false, has_peer = false, has_iface = false; 330 | 331 | if (num < 3) 332 | return -EINVAL; 333 | 334 | for (i = 0; i < num; ++i, tokens += strlen(tokens) + 1) { 335 | if (!strncmp(tokens, "role=", 5)) { 336 | if (!strcmp(&tokens[5], "GO")) 337 | ev->p.p2p_go_neg_success.role = WFD_WPA_EVENT_ROLE_GO; 338 | else if (!strcmp(&tokens[5], "client")) 339 | ev->p.p2p_go_neg_success.role = WFD_WPA_EVENT_ROLE_CLIENT; 340 | else 341 | return -EINVAL; 342 | 343 | has_role = true; 344 | } else if (!strncmp(tokens, "peer_dev=", 9)) { 345 | r = parse_mac(ev->p.p2p_go_neg_success.peer_mac, 346 | &tokens[9]); 347 | if (r < 0) 348 | return r; 349 | 350 | has_peer = true; 351 | } else if (!strncmp(tokens, "peer_iface=", 11)) { 352 | r = parse_mac(ev->p.p2p_go_neg_success.peer_iface, 353 | &tokens[11]); 354 | if (r < 0) 355 | return r; 356 | 357 | has_iface = true; 358 | } 359 | } 360 | 361 | return (has_role && has_peer && has_iface) ? 0 : -EINVAL; 362 | } 363 | 364 | static int parse_p2p_group_started(struct wfd_wpa_event *ev, 365 | char *tokens, size_t num) 366 | { 367 | int r; 368 | size_t i; 369 | 370 | if (num < 3) 371 | return -EINVAL; 372 | 373 | ev->p.p2p_group_started.ifname = strdup(tokens); 374 | if (!ev->p.p2p_group_started.ifname) 375 | return -ENOMEM; 376 | 377 | tokens += strlen(tokens) + 1; 378 | 379 | if (!strcmp(tokens, "GO")) 380 | ev->p.p2p_group_started.role = WFD_WPA_EVENT_ROLE_GO; 381 | else if (!strcmp(tokens, "client")) 382 | ev->p.p2p_group_started.role = WFD_WPA_EVENT_ROLE_CLIENT; 383 | else 384 | return -EINVAL; 385 | 386 | tokens += strlen(tokens) + 1; 387 | 388 | for (i = 2; i < num; ++i, tokens += strlen(tokens) + 1) { 389 | if (strncmp(tokens, "go_dev_addr=", 12)) 390 | continue; 391 | 392 | r = parse_mac(ev->p.p2p_group_started.go_mac, &tokens[12]); 393 | if (r < 0) 394 | return r; 395 | 396 | return 0; 397 | } 398 | 399 | return -EINVAL; 400 | } 401 | 402 | static int parse_p2p_group_removed(struct wfd_wpa_event *ev, 403 | char *tokens, size_t num) 404 | { 405 | if (num < 2) 406 | return -EINVAL; 407 | 408 | ev->p.p2p_group_removed.ifname = strdup(tokens); 409 | if (!ev->p.p2p_group_removed.ifname) 410 | return -ENOMEM; 411 | 412 | tokens += strlen(tokens) + 1; 413 | 414 | if (!strcmp(tokens, "GO")) 415 | ev->p.p2p_group_removed.role = WFD_WPA_EVENT_ROLE_GO; 416 | else if (!strcmp(tokens, "client")) 417 | ev->p.p2p_group_removed.role = WFD_WPA_EVENT_ROLE_CLIENT; 418 | else 419 | return -EINVAL; 420 | 421 | return 0; 422 | } 423 | 424 | static int parse_p2p_prov_disc_show_pin(struct wfd_wpa_event *ev, 425 | char *tokens, size_t num) 426 | { 427 | int r; 428 | 429 | if (num < 2) 430 | return -EINVAL; 431 | 432 | r = parse_mac(ev->p.p2p_prov_disc_show_pin.peer_mac, tokens); 433 | if (r < 0) 434 | return r; 435 | 436 | tokens += strlen(tokens) + 1; 437 | ev->p.p2p_prov_disc_show_pin.pin = strdup(tokens); 438 | if (!ev->p.p2p_prov_disc_show_pin.pin) 439 | return -ENOMEM; 440 | 441 | return 0; 442 | } 443 | 444 | static int parse_p2p_prov_disc_enter_pin(struct wfd_wpa_event *ev, 445 | char *tokens, size_t num) 446 | { 447 | int r; 448 | 449 | if (num < 1) 450 | return -EINVAL; 451 | 452 | r = parse_mac(ev->p.p2p_prov_disc_enter_pin.peer_mac, tokens); 453 | if (r < 0) 454 | return r; 455 | 456 | return 0; 457 | } 458 | 459 | static int parse_p2p_prov_disc_pbc_req(struct wfd_wpa_event *ev, 460 | char *tokens, size_t num) 461 | { 462 | int r; 463 | 464 | if (num < 1) 465 | return -EINVAL; 466 | 467 | r = parse_mac(ev->p.p2p_prov_disc_pbc_req.peer_mac, tokens); 468 | if (r < 0) 469 | return r; 470 | 471 | return 0; 472 | } 473 | 474 | static int parse_p2p_prov_disc_pbc_resp(struct wfd_wpa_event *ev, 475 | char *tokens, size_t num) 476 | { 477 | int r; 478 | 479 | if (num < 1) 480 | return -EINVAL; 481 | 482 | r = parse_mac(ev->p.p2p_prov_disc_pbc_resp.peer_mac, tokens); 483 | if (r < 0) 484 | return r; 485 | 486 | return 0; 487 | } 488 | 489 | _shl_public_ 490 | int wfd_wpa_event_parse(struct wfd_wpa_event *ev, const char *event) 491 | { 492 | const char *t; 493 | char *end, *tokens = NULL; 494 | size_t num; 495 | struct event_type *code; 496 | int r; 497 | 498 | if (!ev || !event) 499 | return -EINVAL; 500 | 501 | wfd_wpa_event_reset(ev); 502 | 503 | if (*event == '<') { 504 | t = strchr(event, '>'); 505 | if (!t) 506 | goto unknown; 507 | 508 | ++t; 509 | ev->priority = strtoul(event + 1, &end, 10); 510 | if (ev->priority >= WFD_WPA_EVENT_P_CNT || 511 | end + 1 != t || 512 | event[1] == '+' || 513 | event[1] == '-') 514 | ev->priority = WFD_WPA_EVENT_P_MSGDUMP; 515 | } else { 516 | t = event; 517 | ev->priority = WFD_WPA_EVENT_P_MSGDUMP; 518 | } 519 | 520 | code = bsearch(t, event_list, 521 | sizeof(event_list) / sizeof(*event_list), 522 | sizeof(*event_list), 523 | event_comp); 524 | if (!code) 525 | goto unknown; 526 | 527 | ev->type = code->code; 528 | t += code->len; 529 | while (*t == ' ') 530 | ++t; 531 | 532 | ev->raw = strdup(t); 533 | if (!ev->raw) { 534 | r = -ENOMEM; 535 | goto error; 536 | } 537 | 538 | tokens = tokenize(ev->raw, &num); 539 | if (!tokens) { 540 | r = -ENOMEM; 541 | goto error; 542 | } 543 | 544 | switch (ev->type) { 545 | case WFD_WPA_EVENT_AP_STA_CONNECTED: 546 | r = parse_ap_sta_connected(ev, tokens, num); 547 | break; 548 | case WFD_WPA_EVENT_AP_STA_DISCONNECTED: 549 | r = parse_ap_sta_disconnected(ev, tokens, num); 550 | break; 551 | case WFD_WPA_EVENT_P2P_DEVICE_FOUND: 552 | r = parse_p2p_device_found(ev, tokens, num); 553 | break; 554 | case WFD_WPA_EVENT_P2P_DEVICE_LOST: 555 | r = parse_p2p_device_lost(ev, tokens, num); 556 | break; 557 | case WFD_WPA_EVENT_P2P_GO_NEG_SUCCESS: 558 | r = parse_p2p_go_neg_success(ev, tokens, num); 559 | break; 560 | case WFD_WPA_EVENT_P2P_GROUP_STARTED: 561 | r = parse_p2p_group_started(ev, tokens, num); 562 | break; 563 | case WFD_WPA_EVENT_P2P_GROUP_REMOVED: 564 | r = parse_p2p_group_removed(ev, tokens, num); 565 | break; 566 | case WFD_WPA_EVENT_P2P_PROV_DISC_SHOW_PIN: 567 | r = parse_p2p_prov_disc_show_pin(ev, tokens, num); 568 | break; 569 | case WFD_WPA_EVENT_P2P_PROV_DISC_ENTER_PIN: 570 | r = parse_p2p_prov_disc_enter_pin(ev, tokens, num); 571 | break; 572 | case WFD_WPA_EVENT_P2P_PROV_DISC_PBC_REQ: 573 | r = parse_p2p_prov_disc_pbc_req(ev, tokens, num); 574 | break; 575 | case WFD_WPA_EVENT_P2P_PROV_DISC_PBC_RESP: 576 | r = parse_p2p_prov_disc_pbc_resp(ev, tokens, num); 577 | break; 578 | default: 579 | r = 0; 580 | break; 581 | } 582 | 583 | free(tokens); 584 | 585 | if (r < 0) 586 | goto error; 587 | 588 | return 0; 589 | 590 | unknown: 591 | ev->type = WFD_WPA_EVENT_UNKNOWN; 592 | return 0; 593 | 594 | error: 595 | wfd_wpa_event_reset(ev); 596 | return r; 597 | } 598 | -------------------------------------------------------------------------------- /test/test_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | /* 27 | * Test Helper 28 | * This header includes all kinds of helpers for testing. It tries to include 29 | * everything required and provides simple macros to avoid duplicating code in 30 | * each test. We try to keep tests as small as possible and move everything that 31 | * might be common here. 32 | * 33 | * We avoid sticking to our usual coding conventions (including headers in 34 | * source files, etc. ..) and instead make this the most convenient we can. 35 | */ 36 | 37 | #ifndef TEST_COMMON_H 38 | #define TEST_COMMON_H 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include "libwfd.h" 49 | #include "shl_macro.h" 50 | #include "shl_util.h" 51 | 52 | /* lower address-space is protected from user-allocation, so this is invalid */ 53 | #define TEST_INVALID_PTR ((void*)0x10) 54 | 55 | #define TEST_DEFINE_CASE(_name) \ 56 | static TCase *test_create_case_##_name(void) \ 57 | { \ 58 | TCase *tc; \ 59 | \ 60 | tc = tcase_create(#_name); \ 61 | 62 | #define TEST(_name) tcase_add_test(tc, _name); 63 | 64 | #define TEST_END_CASE \ 65 | return tc; \ 66 | } \ 67 | 68 | #define TEST_END NULL 69 | 70 | #define TEST_CASE(_name) test_create_case_##_name 71 | 72 | static inline Suite *test_create_suite(const char *name, ...) 73 | { 74 | Suite *s; 75 | va_list list; 76 | TCase *(*fn)(void); 77 | 78 | s = suite_create(name); 79 | 80 | va_start(list, name); 81 | while ((fn = va_arg(list, TCase *(*)(void)))) 82 | suite_add_tcase(s, fn()); 83 | va_end(list); 84 | 85 | return s; 86 | } 87 | 88 | #define TEST_SUITE(_name, ...) test_create_suite((#_name), ##__VA_ARGS__) 89 | 90 | static inline int test_run_suite(Suite *s) 91 | { 92 | int ret; 93 | SRunner *sr; 94 | 95 | sr = srunner_create(s); 96 | srunner_run_all(sr, CK_NORMAL); 97 | ret = srunner_ntests_failed(sr); 98 | srunner_free(sr); 99 | 100 | return ret; 101 | } 102 | 103 | #define TEST_DEFINE(_suite) \ 104 | int main(int argc, char **argv) \ 105 | { \ 106 | return test_run_suite(_suite); \ 107 | } 108 | 109 | #endif /* TEST_COMMON_H */ 110 | -------------------------------------------------------------------------------- /test/test_rtsp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "test_common.h" 27 | 28 | static int received; 29 | 30 | struct orig { 31 | ssize_t len; 32 | const char *str; 33 | }; 34 | 35 | struct expect { 36 | size_t times; 37 | struct wfd_rtsp_msg msg; 38 | }; 39 | 40 | static const struct orig orig[] = { 41 | { -1, "SOMETHING\r\n\r\n" }, 42 | { -1, "SOMETHING" }, 43 | { -1, "\r" }, 44 | { -1, "\n" }, 45 | { -1, "\n" }, 46 | { -1, "SOME" }, 47 | { -1, "THING" }, 48 | { -1, "\r" }, 49 | { -1, "\r" }, 50 | { -1, "\n" }, 51 | { -1, "SOME" }, 52 | { -1, "THING\n" }, 53 | { -1, "\r" }, 54 | { -1, "SOMETHING\n\n" }, 55 | { -1, "SOMETHING\r\r" }, 56 | { -1, "SOMETHING\r\r\n" }, 57 | { -1, "SOMETHING\n\r\n" }, 58 | 59 | { -1, "OPTIONS * RTSP/1.0\n\r\n" }, 60 | { -1, "OPTIONS * RTSP/1.0\n\r\n" }, 61 | { -1, "OPTIONS *\r RTSP/1.0\n\r\n" }, 62 | { -1, "OPTIONS *\r\n RTSP/1.0\n\r\n" }, 63 | { -1, "OPTIONS\r *\n RTSP/1.0\n\r\n" }, 64 | { -1, " \r\n OPTIONS * RTSP/1.0\n\r\n" }, 65 | { -1, "\rOPTIONS * RTSP/1.0\n\r\n" }, 66 | { -1, "\nOPTIONS * RTSP/1.0\n\r\n" }, 67 | { -1, " OPTIONS *\n\t \r\tRTSP/1.0\n\r\n" }, 68 | { -1, "OPTIONS * RTSP/1.0 \n\r\n" }, 69 | 70 | { -1, "RTSP/1.0 200 OK Something\n\n" }, 71 | 72 | { 10, "$\001\000\006RAWSTH" }, 73 | { 10, "$\001\000\006RAWSTH" }, 74 | 75 | { -1, "SOMETHING\r\nsome-header:value\r\n\r\n" }, 76 | 77 | { -1, "OPTIONS * RTSP/2.1\n" }, 78 | { -1, "some-header:value\n" }, 79 | { -1, "some-other-header:buhu\n" }, 80 | { -1, "\n" }, 81 | { -1, "OPTIONS * RTSP/2.1\n" }, 82 | { -1, "some-header:value \n" }, 83 | { -1, "some-other-header:buhu \r \n \n" }, 84 | { -1, "\n" }, 85 | 86 | { 16, " \n $\001\000\006RAWSTH" }, 87 | { 11, " \n \r\n$\001\000" }, 88 | { 7, "\006RAWSTH" }, 89 | 90 | { -1, "OPTIONS * RTSP/2.1\n" }, 91 | { -1, "some-header :value \n" }, 92 | { -1, "some-other-header: buhu \r \n \n" }, 93 | { -1, "some-header : value \n" }, 94 | { -1, "\n" }, 95 | { -1, "OPTIONS * RTSP/2.1\n" }, 96 | { -1, "some-header \r \n :value \n" }, 97 | { -1, "some-other-header: \r\n buhu \r \n \n" }, 98 | { -1, "some-header \t\t\t:\r\n value \n" }, 99 | { -1, "\n" }, 100 | 101 | { -1, "STH\r\ncontent-length:5\r\n\r\n12345" }, 102 | 103 | { -1, "STH\r\ncontent-length:5/suffix\r\n\r\n12345" }, 104 | 105 | { -1, "OPTIONS * RTSP/1.0\n" }, 106 | { -1, "cseq: 100\n" }, 107 | { -1, "\n" }, 108 | 109 | /* leave this at the end to test missing trailing \n */ 110 | { -1, "SOMETHING\n\r" }, 111 | }; 112 | 113 | static const struct expect expect[] = { 114 | { 115 | .times = 8, 116 | .msg = { 117 | .type = WFD_RTSP_MSG_UNKNOWN, 118 | .id = { 119 | /* test .length comparison in test-module */ 120 | .line = "SOMETHING" "-STUPID", 121 | .length = 9, 122 | }, 123 | }, 124 | }, 125 | { 126 | .times = 10, 127 | .msg = { 128 | .type = WFD_RTSP_MSG_REQUEST, 129 | .id = { 130 | .line = "OPTIONS * RTSP/1.0", 131 | .request = { 132 | .method = "OPTIONS", 133 | .type = WFD_RTSP_METHOD_OPTIONS, 134 | .uri = "*", 135 | .major = 1, 136 | .minor = 0, 137 | }, 138 | }, 139 | }, 140 | }, 141 | { 142 | .times = 1, 143 | .msg = { 144 | .type = WFD_RTSP_MSG_RESPONSE, 145 | .id = { 146 | .line = "RTSP/1.0 200 OK Something", 147 | .response = { 148 | .major = 1, 149 | .minor = 0, 150 | .status = 200, 151 | .phrase = "OK Something", 152 | }, 153 | }, 154 | }, 155 | }, 156 | { 157 | .times = 1, 158 | .msg = { 159 | .type = WFD_RTSP_MSG_UNKNOWN, 160 | .id = { 161 | .line = "SOMETHING", 162 | }, 163 | .headers = { 164 | [WFD_RTSP_HEADER_UNKNOWN] = { 165 | .count = 1, 166 | .lines = (char*[]){ 167 | (char*)"some-header:value", 168 | }, 169 | .lengths = (size_t[]){ 170 | 0, 171 | }, 172 | }, 173 | }, 174 | }, 175 | }, 176 | { 177 | .times = 2, 178 | .msg = { 179 | .type = WFD_RTSP_MSG_REQUEST, 180 | .id = { 181 | .line = "OPTIONS * RTSP/2.1", 182 | .request = { 183 | .method = "OPTIONS", 184 | .type = WFD_RTSP_METHOD_OPTIONS, 185 | .uri = "*", 186 | .major = 2, 187 | .minor = 1, 188 | }, 189 | }, 190 | .headers = { 191 | [WFD_RTSP_HEADER_UNKNOWN] = { 192 | .count = 2, 193 | .lines = (char*[]){ 194 | (char*)"some-header:value", 195 | (char*)"some-other-header:buhu", 196 | }, 197 | .lengths = (size_t[]){ 198 | 0, 199 | 0, 200 | }, 201 | }, 202 | }, 203 | }, 204 | }, 205 | { 206 | .times = 2, 207 | .msg = { 208 | .type = WFD_RTSP_MSG_REQUEST, 209 | .id = { 210 | .line = "OPTIONS * RTSP/2.1", 211 | .request = { 212 | .method = "OPTIONS", 213 | .type = WFD_RTSP_METHOD_OPTIONS, 214 | .uri = "*", 215 | .major = 2, 216 | .minor = 1, 217 | }, 218 | }, 219 | .headers = { 220 | [WFD_RTSP_HEADER_UNKNOWN] = { 221 | .count = 3, 222 | .lines = (char*[]){ 223 | (char*)"some-header :value", 224 | (char*)"some-other-header: buhu", 225 | (char*)"some-header : value", 226 | }, 227 | .lengths = (size_t[]){ 228 | 0, 229 | 0, 230 | 0, 231 | }, 232 | }, 233 | }, 234 | }, 235 | }, 236 | { 237 | .times = 1, 238 | .msg = { 239 | .type = WFD_RTSP_MSG_UNKNOWN, 240 | .id = { 241 | .line = "STH", 242 | }, 243 | .headers = { 244 | [WFD_RTSP_HEADER_CONTENT_LENGTH] = { 245 | .count = 1, 246 | .lines = (char*[]){ 247 | (char*)"content-length:5", 248 | }, 249 | .lengths = (size_t[]){ 250 | 0, 251 | }, 252 | }, 253 | }, 254 | .entity = { 255 | .value = "12345", 256 | }, 257 | }, 258 | }, 259 | { 260 | .times = 1, 261 | .msg = { 262 | .type = WFD_RTSP_MSG_UNKNOWN, 263 | .id = { 264 | .line = "STH", 265 | }, 266 | .headers = { 267 | [WFD_RTSP_HEADER_CONTENT_LENGTH] = { 268 | .count = 1, 269 | .lines = (char*[]){ 270 | (char*)"content-length:5/suffix", 271 | }, 272 | .lengths = (size_t[]){ 273 | 0, 274 | }, 275 | }, 276 | }, 277 | .entity = { 278 | .value = "12345", 279 | }, 280 | }, 281 | }, 282 | { 283 | .times = 1, 284 | .msg = { 285 | .type = WFD_RTSP_MSG_REQUEST, 286 | .id = { 287 | .line = "OPTIONS * RTSP/1.0", 288 | .request = { 289 | .method = "OPTIONS", 290 | .type = WFD_RTSP_METHOD_OPTIONS, 291 | .uri = "*", 292 | .major = 1, 293 | .minor = 0, 294 | }, 295 | }, 296 | .headers = { 297 | [WFD_RTSP_HEADER_CSEQ] = { 298 | .count = 1, 299 | .lines = (char*[]){ 300 | (char*)"cseq: 100", 301 | }, 302 | .lengths = (size_t[]){ 303 | 0, 304 | }, 305 | }, 306 | }, 307 | }, 308 | }, 309 | { 310 | .times = 1, 311 | .msg = { 312 | .type = WFD_RTSP_MSG_UNKNOWN, 313 | .id = { 314 | .line = "SOMETHING", 315 | }, 316 | }, 317 | }, 318 | }; 319 | 320 | #define LEN(_len, _str) ((_len) <= 0 ? ((_str) ? strlen(_str) : 0) : (_len)) 321 | 322 | static int test_wfd_rtsp_decoder_event(struct wfd_rtsp_decoder *dec, 323 | void *data, 324 | struct wfd_rtsp_decoder_event *ev) 325 | { 326 | bool debug = true; 327 | static int pos, num; 328 | size_t i, j; 329 | const struct expect *e; 330 | const struct wfd_rtsp_msg *m, *msg; 331 | const struct wfd_rtsp_msg_header *h, *hm; 332 | 333 | if (ev->type == WFD_RTSP_DECODER_DATA) { 334 | if (debug) 335 | fprintf(stderr, "Raw Data (%u:%u): %s\n", 336 | (unsigned int)ev->data.channel, 337 | (unsigned int)ev->data.size, 338 | (char*)ev->data.value); 339 | return 0; 340 | } 341 | 342 | if (ev->type != WFD_RTSP_DECODER_MSG) 343 | return 0; 344 | 345 | msg = ev->msg; 346 | ck_assert(data == TEST_INVALID_PTR); 347 | ++received; 348 | 349 | ck_assert(pos < SHL_ARRAY_LENGTH(expect)); 350 | e = &expect[pos]; 351 | m = &e->msg; 352 | if (++num >= e->times) { 353 | ++pos; 354 | num = 0; 355 | } 356 | 357 | /* print msg */ 358 | 359 | if (debug) { 360 | fprintf(stderr, "Received Message:\n"); 361 | fprintf(stderr, " type: %u\n", msg->type); 362 | fprintf(stderr, " id: len: %zu line: %s\n", 363 | msg->id.length, msg->id.line); 364 | 365 | if (msg->type == WFD_RTSP_MSG_REQUEST) { 366 | fprintf(stderr, " method (%d): %s %s RTSP/%u.%u\n", 367 | msg->id.request.type, 368 | msg->id.request.method, 369 | msg->id.request.uri, 370 | msg->id.request.major, 371 | msg->id.request.minor); 372 | } else if (msg->type == WFD_RTSP_MSG_RESPONSE) { 373 | fprintf(stderr, " RTSP/%u.%u %u %s\n", 374 | msg->id.response.major, 375 | msg->id.response.minor, 376 | msg->id.response.status, 377 | msg->id.response.phrase); 378 | } 379 | 380 | fprintf(stderr, " headers:\n"); 381 | for (i = 0; i < SHL_ARRAY_LENGTH(msg->headers); ++i) { 382 | h = &msg->headers[i]; 383 | if (!h->count) 384 | continue; 385 | 386 | fprintf(stderr, " id: %zu %s (count: %zu)\n", 387 | i, wfd_rtsp_header_get_name(i) ? : "", 388 | h->count); 389 | 390 | for (j = 0; j < h->count; ++j) { 391 | fprintf(stderr, " line (%zu): %s\n", 392 | h->lengths[j], h->lines[j]); 393 | } 394 | } 395 | 396 | if (msg->entity.size) 397 | fprintf(stderr, " body (%zu): %s\n", 398 | msg->entity.size, (char*)msg->entity.value); 399 | } 400 | 401 | /* compare msgs */ 402 | 403 | ck_assert_int_eq(m->type, msg->type); 404 | ck_assert_int_eq(LEN(m->id.length, m->id.line), msg->id.length); 405 | ck_assert(!memcmp(m->id.line, msg->id.line, msg->id.length)); 406 | 407 | if (m->type == WFD_RTSP_MSG_REQUEST) { 408 | ck_assert(!strcmp(m->id.request.method, 409 | msg->id.request.method)); 410 | ck_assert_int_eq(m->id.request.type, 411 | msg->id.request.type); 412 | ck_assert(!strcmp(m->id.request.uri, 413 | msg->id.request.uri)); 414 | ck_assert_int_eq(m->id.request.major, 415 | msg->id.request.major); 416 | ck_assert_int_eq(m->id.request.minor, 417 | msg->id.request.minor); 418 | } else if (m->type == WFD_RTSP_MSG_RESPONSE) { 419 | ck_assert_int_eq(m->id.response.major, 420 | msg->id.response.major); 421 | ck_assert_int_eq(m->id.response.minor, 422 | msg->id.response.minor); 423 | ck_assert_int_eq(m->id.response.status, 424 | msg->id.response.status); 425 | ck_assert(!strcmp(m->id.response.phrase, 426 | msg->id.response.phrase)); 427 | } 428 | 429 | for (i = 0; i < SHL_ARRAY_LENGTH(msg->headers); ++i) { 430 | h = &m->headers[i]; 431 | hm = &msg->headers[i]; 432 | ck_assert_int_eq(h->count, hm->count); 433 | 434 | for (j = 0; j < h->count; ++j) { 435 | ck_assert_int_eq(LEN(h->lengths[j], h->lines[j]), 436 | hm->lengths[j]); 437 | ck_assert(!memcmp(h->lines[j], 438 | hm->lines[j], 439 | hm->lengths[j])); 440 | } 441 | } 442 | 443 | ck_assert_int_eq(LEN(m->entity.size, m->entity.value), 444 | msg->entity.size); 445 | ck_assert(!memcmp(m->entity.value, msg->entity.value, 446 | msg->entity.size)); 447 | 448 | return 0; 449 | } 450 | 451 | START_TEST(test_wfd_rtsp_decoder) 452 | { 453 | struct wfd_rtsp_decoder *d; 454 | int r, sent = 0; 455 | size_t len, num, i; 456 | 457 | r = wfd_rtsp_decoder_new(NULL, NULL, NULL, NULL, &d); 458 | ck_assert(r == -EINVAL); 459 | 460 | r = wfd_rtsp_decoder_new(test_wfd_rtsp_decoder_event, NULL, NULL, NULL, &d); 461 | ck_assert(r >= 0); 462 | 463 | wfd_rtsp_decoder_set_data(d, TEST_INVALID_PTR); 464 | ck_assert(wfd_rtsp_decoder_get_data(d) == TEST_INVALID_PTR); 465 | ck_assert(received == sent); 466 | 467 | for (i = 0; i < SHL_ARRAY_LENGTH(orig); ++i) { 468 | if (orig[i].len >= 0) 469 | len = orig[i].len; 470 | else 471 | len = strlen(orig[i].str); 472 | 473 | r = wfd_rtsp_decoder_feed(d, orig[i].str, len); 474 | ck_assert(r >= 0); 475 | } 476 | 477 | num = 0; 478 | for (i = 0; i < SHL_ARRAY_LENGTH(expect); ++i) 479 | num += expect[i].times; 480 | 481 | ck_assert_int_eq(received, num); 482 | 483 | wfd_rtsp_decoder_free(d); 484 | } 485 | END_TEST 486 | 487 | static void tokenize(const char *line, 488 | size_t linelen, 489 | const char *expect, 490 | size_t len, 491 | size_t num) 492 | { 493 | const char *s; 494 | char *t; 495 | ssize_t l, i; 496 | 497 | ck_assert(len > 0); 498 | 499 | l = wfd_rtsp_tokenize(line, linelen, &t); 500 | ck_assert(l >= 0); 501 | 502 | if (0) { 503 | fprintf(stderr, "TOKENIZER (%lu):\n", (unsigned long)l); 504 | s = t; 505 | for (i = 0; i < l; ++i) { 506 | fprintf(stderr, " TOKEN: %s\n", s); 507 | s += strlen(s) + 1; 508 | } 509 | 510 | fprintf(stderr, "EXPECT (%lu):\n", (unsigned long)num); 511 | s = expect; 512 | for (i = 0; i < l; ++i) { 513 | fprintf(stderr, " TOKEN: %s\n", s); 514 | s += strlen(s) + 1; 515 | } 516 | } 517 | 518 | ck_assert(l == (ssize_t)num); 519 | ck_assert(!memcmp(t, expect, len)); 520 | free(t); 521 | } 522 | 523 | #define TOKENIZE(_line, _exp, _num) \ 524 | tokenize((_line), strlen(_line), (_exp), sizeof(_exp), _num) 525 | #define TOKENIZE_N(_line, _len, _exp, _num) \ 526 | tokenize((_line), _len, (_exp), sizeof(_exp), _num) 527 | 528 | START_TEST(test_wfd_rtsp_tokenizer) 529 | { 530 | TOKENIZE("", "", 0); 531 | TOKENIZE("asdf", "asdf", 1); 532 | TOKENIZE("asdf\"\"asdf", "asdf\0\0asdf", 3); 533 | TOKENIZE("asdf\"asdf\"asdf", "asdf\0asdf\0asdf", 3); 534 | TOKENIZE("\"asdf\"", "asdf", 1); 535 | TOKENIZE("\"\\n\\\\\\r\"", "\n\\\r", 1); 536 | TOKENIZE("\"\\\"\"", "\"", 1); 537 | TOKENIZE("\"\\0\"", "\\0", 1); 538 | TOKENIZE("\"\\\0\"", "\\", 1); 539 | TOKENIZE_N("\"\\\0\"", 4, "\\0", 1); 540 | TOKENIZE("\"\\0\\\0\"", "\\0\\", 1); 541 | TOKENIZE_N("\"\\0\\\0\"", 6, "\\0\\0", 1); 542 | TOKENIZE("content-length: 100", "content-length\0:\0""100", 3); 543 | TOKENIZE("content-args: (50+10)", "content-args\0:\0(\0""50+10\0)", 5); 544 | TOKENIZE("content-args: (50 + 10)", "content-args\0:\0(\0""50\0+\0""10\0)", 7); 545 | } 546 | END_TEST 547 | 548 | TEST_DEFINE_CASE(decoder) 549 | TEST(test_wfd_rtsp_decoder) 550 | TEST(test_wfd_rtsp_tokenizer) 551 | TEST_END_CASE 552 | 553 | TEST_DEFINE( 554 | TEST_SUITE(rtsp, 555 | TEST_CASE(decoder), 556 | TEST_END 557 | ) 558 | ) 559 | -------------------------------------------------------------------------------- /test/test_wpa.c: -------------------------------------------------------------------------------- 1 | /* 2 | * libwfd - Wifi-Display/Miracast Protocol Implementation 3 | * 4 | * Copyright (c) 2013-2014 David Herrmann 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining 7 | * a copy of this software and associated documentation files 8 | * (the "Software"), to deal in the Software without restriction, including 9 | * without limitation the rights to use, copy, modify, merge, publish, 10 | * distribute, sublicense, and/or sell copies of the Software, and to 11 | * permit persons to whom the Software is furnished to do so, subject to 12 | * the following conditions: 13 | * 14 | * The above copyright notice and this permission notice shall be included 15 | * in all copies or substantial portions of the Software. 16 | * 17 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 18 | * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | */ 25 | 26 | #include "test_common.h" 27 | 28 | static void parse(struct wfd_wpa_event *ev, const char *event) 29 | { 30 | int r; 31 | 32 | wfd_wpa_event_init(ev); 33 | r = wfd_wpa_event_parse(ev, event); 34 | ck_assert_msg(!r, "cannot parse event %s", event); 35 | ck_assert(ev->priority < WFD_WPA_EVENT_P_CNT); 36 | } 37 | 38 | static const char *event_list[] = { 39 | [WFD_WPA_EVENT_UNKNOWN] = "", 40 | [WFD_WPA_EVENT_AP_STA_CONNECTED] = "AP-STA-CONNECTED 00:00:00:00:00:00 p2p_dev_addr=00:00:00:00:00:00", 41 | [WFD_WPA_EVENT_AP_STA_DISCONNECTED] = "AP-STA-DISCONNECTED 00:00:00:00:00:00 p2p_dev_addr=00:00:00:00:00:00", 42 | [WFD_WPA_EVENT_CTRL_EVENT_SCAN_STARTED] = "CTRL-EVENT-SCAN-STARTED", 43 | [WFD_WPA_EVENT_CTRL_EVENT_TERMINATING] = "CTRL-EVENT-TERMINATING", 44 | [WFD_WPA_EVENT_P2P_DEVICE_FOUND] = "P2P-DEVICE-FOUND 00:00:00:00:00:00 name=some-name", 45 | [WFD_WPA_EVENT_P2P_DEVICE_LOST] = "P2P-DEVICE-LOST p2p_dev_addr=00:00:00:00:00:00", 46 | [WFD_WPA_EVENT_P2P_FIND_STOPPED] = "P2P-FIND-STOPPED", 47 | [WFD_WPA_EVENT_P2P_GO_NEG_REQUEST] = "P2P-GO-NEG-REQUEST", 48 | [WFD_WPA_EVENT_P2P_GO_NEG_SUCCESS] = "P2P-GO-NEG-SUCCESS role=GO peer_dev=00:00:00:00:00:00 peer_iface=00:00:00:00:00:00", 49 | [WFD_WPA_EVENT_P2P_GO_NEG_FAILURE] = "P2P-GO-NEG-FAILURE", 50 | [WFD_WPA_EVENT_P2P_GROUP_FORMATION_SUCCESS] = "P2P-GROUP-FORMATION-SUCCESS", 51 | [WFD_WPA_EVENT_P2P_GROUP_FORMATION_FAILURE] = "P2P-GROUP-FORMATION-FAILURE", 52 | [WFD_WPA_EVENT_P2P_GROUP_STARTED] = "P2P-GROUP-STARTED p2p-wlan0-0 client go_dev_addr=00:00:00:00:00:00", 53 | [WFD_WPA_EVENT_P2P_GROUP_REMOVED] = "P2P-GROUP-REMOVED p2p-wlan0-0 GO", 54 | [WFD_WPA_EVENT_P2P_PROV_DISC_SHOW_PIN] = "P2P-PROV-DISC-SHOW-PIN 00:00:00:00:00:00 pin", 55 | [WFD_WPA_EVENT_P2P_PROV_DISC_ENTER_PIN] = "P2P-PROV-DISC-ENTER-PIN 00:00:00:00:00:00", 56 | [WFD_WPA_EVENT_P2P_PROV_DISC_PBC_REQ] = "P2P-PROV-DISC-PBC-REQ 00:00:00:00:00:00", 57 | [WFD_WPA_EVENT_P2P_PROV_DISC_PBC_RESP] = "P2P-PROV-DISC-PBC-RESP 00:00:00:00:00:00", 58 | [WFD_WPA_EVENT_P2P_SERV_DISC_REQ] = "P2P-SERV-DISC-REQ", 59 | [WFD_WPA_EVENT_P2P_SERV_DISC_RESP] = "P2P-SERV-DISC-RESP", 60 | [WFD_WPA_EVENT_P2P_INVITATION_RECEIVED] = "P2P-INVITATION-RECEIVED", 61 | [WFD_WPA_EVENT_P2P_INVITATION_RESULT] = "P2P-INVITATION-RESULT", 62 | [WFD_WPA_EVENT_CNT] = NULL 63 | }; 64 | 65 | START_TEST(test_wpa_parser) 66 | { 67 | struct wfd_wpa_event ev; 68 | int i; 69 | 70 | parse(&ev, ""); 71 | ck_assert(ev.type == WFD_WPA_EVENT_UNKNOWN); 72 | 73 | parse(&ev, "asdf"); 74 | ck_assert(ev.type == WFD_WPA_EVENT_UNKNOWN); 75 | 76 | for (i = 0; i < WFD_WPA_EVENT_CNT; ++i) { 77 | ck_assert_msg(event_list[i] != NULL, "event %d missing", i); 78 | parse(&ev, event_list[i]); 79 | ck_assert_msg(ev.type == i, "event %d invalid", i); 80 | } 81 | 82 | parse(&ev, "<5>AP-STA-CONNECTED 0:0:0:0:0:0"); 83 | ck_assert(ev.priority == WFD_WPA_EVENT_P_MSGDUMP); 84 | ck_assert(ev.type == WFD_WPA_EVENT_AP_STA_CONNECTED); 85 | 86 | parse(&ev, "<4>AP-STA-CONNECTED 0:0:0:0:0:0 p2p_dev_addr=ff:ff:ff:ff:ff:ff"); 87 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 88 | ck_assert(ev.type == WFD_WPA_EVENT_AP_STA_CONNECTED); 89 | 90 | parse(&ev, "<4>AP-STA-CONNECTED2"); 91 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 92 | ck_assert(ev.type == WFD_WPA_EVENT_UNKNOWN); 93 | 94 | parse(&ev, "<4asdf>AP-STA-CONNECTED 0:0:0:0:0:0"); 95 | ck_assert(ev.priority == WFD_WPA_EVENT_P_MSGDUMP); 96 | ck_assert(ev.type == WFD_WPA_EVENT_AP_STA_CONNECTED); 97 | 98 | parse(&ev, "<4>AP-STA-CONNECTED 0:0:0:0:0:0"); 99 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 100 | ck_assert(ev.type == WFD_WPA_EVENT_AP_STA_CONNECTED); 101 | ck_assert(ev.raw != NULL); 102 | ck_assert(!strcmp(ev.raw, "0:0:0:0:0:0")); 103 | 104 | parse(&ev, "<4>AP-STA something else"); 105 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 106 | ck_assert(ev.type == WFD_WPA_EVENT_UNKNOWN); 107 | ck_assert(!ev.raw); 108 | } 109 | END_TEST 110 | 111 | START_TEST(test_wpa_parser_payload) 112 | { 113 | struct wfd_wpa_event ev; 114 | 115 | parse(&ev, "<4>P2P-DEVICE-FOUND 0:0:0:0:0:0 name=some-name"); 116 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 117 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_DEVICE_FOUND); 118 | ck_assert(ev.raw != NULL); 119 | ck_assert(!strcmp(ev.raw, "0:0:0:0:0:0 name=some-name")); 120 | ck_assert(!strcmp(ev.p.p2p_device_found.peer_mac, "0:0:0:0:0:0")); 121 | ck_assert(!strcmp(ev.p.p2p_device_found.name, "some-name")); 122 | 123 | parse(&ev, "<4>P2P-DEVICE-FOUND 0:0:0:0:0:0 name=some-'name\\\\\\''"); 124 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 125 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_DEVICE_FOUND); 126 | ck_assert(ev.raw != NULL); 127 | ck_assert(!strcmp(ev.p.p2p_device_found.peer_mac, "0:0:0:0:0:0")); 128 | ck_assert(!strcmp(ev.p.p2p_device_found.name, "some-name\\'")); 129 | 130 | parse(&ev, "<4>P2P-DEVICE-LOST dummy=sth p2p_dev_addr=0:0:0:0:0:0"); 131 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 132 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_DEVICE_LOST); 133 | ck_assert(ev.raw != NULL); 134 | ck_assert(!strcmp(ev.p.p2p_device_lost.peer_mac, "0:0:0:0:0:0")); 135 | 136 | parse(&ev, "<4>P2P-PROV-DISC-SHOW-PIN 0:0:0:0:0:0 1234567890"); 137 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 138 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_PROV_DISC_SHOW_PIN); 139 | ck_assert(ev.raw != NULL); 140 | ck_assert(!strcmp(ev.p.p2p_prov_disc_show_pin.peer_mac, "0:0:0:0:0:0")); 141 | ck_assert(!strcmp(ev.p.p2p_prov_disc_show_pin.pin, "1234567890")); 142 | 143 | parse(&ev, "<4>P2P-GO-NEG-SUCCESS role=GO peer_dev=0:0:0:0:0:0 peer_iface=ff:ff:ff:ff:ff:ff"); 144 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 145 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_GO_NEG_SUCCESS); 146 | ck_assert(ev.raw != NULL); 147 | ck_assert(!strcmp(ev.p.p2p_go_neg_success.peer_mac, "0:0:0:0:0:0")); 148 | ck_assert(!strcmp(ev.p.p2p_go_neg_success.peer_iface, "ff:ff:ff:ff:ff:ff")); 149 | ck_assert(ev.p.p2p_go_neg_success.role == WFD_WPA_EVENT_ROLE_GO); 150 | 151 | parse(&ev, "<4>P2P-GROUP-STARTED p2p-wlan0-0 client go_dev_addr=0:0:0:0:0:0"); 152 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 153 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_GROUP_STARTED); 154 | ck_assert(ev.raw != NULL); 155 | ck_assert(!strcmp(ev.p.p2p_group_started.go_mac, "0:0:0:0:0:0")); 156 | ck_assert(!strcmp(ev.p.p2p_group_started.ifname, "p2p-wlan0-0")); 157 | ck_assert(ev.p.p2p_group_started.role == WFD_WPA_EVENT_ROLE_CLIENT); 158 | 159 | parse(&ev, "<4>P2P-GROUP-REMOVED p2p-wlan0-1 GO"); 160 | ck_assert(ev.priority == WFD_WPA_EVENT_P_ERROR); 161 | ck_assert(ev.type == WFD_WPA_EVENT_P2P_GROUP_REMOVED); 162 | ck_assert(ev.raw != NULL); 163 | ck_assert(!strcmp(ev.p.p2p_group_removed.ifname, "p2p-wlan0-1")); 164 | ck_assert(ev.p.p2p_group_removed.role == WFD_WPA_EVENT_ROLE_GO); 165 | } 166 | END_TEST 167 | 168 | TEST_DEFINE_CASE(parser) 169 | TEST(test_wpa_parser) 170 | TEST(test_wpa_parser_payload) 171 | TEST_END_CASE 172 | 173 | TEST_DEFINE( 174 | TEST_SUITE(wpa, 175 | TEST_CASE(parser), 176 | TEST_END 177 | ) 178 | ) 179 | --------------------------------------------------------------------------------