├── clients.png ├── design.png ├── overview.png ├── Makefile.am ├── include ├── Makefile.am ├── ios-webkit-debug-proxy │ ├── idevice_ext.h │ ├── device_listener.h │ ├── socket_manager.h │ ├── webinspector.h │ ├── websocket.h │ └── ios_webkit_debug_proxy.h ├── strndup.h ├── strcasestr.h ├── getline.h └── validate_utf8.h ├── autogen.sh ├── .gitignore ├── examples ├── ws_client.html ├── README.md ├── ws_echo_common.h ├── wdp_client.js ├── Makefile.am ├── dl_client.c ├── ws_echo2.c ├── ws_echo1.c ├── wdp_client.html ├── wi_client.c └── ws_echo_common.c ├── LICENSE.md ├── src ├── hash_table.h ├── Makefile.am ├── port_config.h ├── char_buffer.h ├── base64.h ├── rpc.h ├── idevice_ext.c ├── sha1.h ├── hash_table.c ├── port_config.c ├── base64.c ├── char_buffer.c ├── device_listener.c ├── ios_webkit_debug_proxy_main.c ├── webinspector.c ├── sha1.c └── socket_manager.c ├── configure.ac ├── design.md └── README.md /clients.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2Cmo/ios-webkit-debug-proxy/master/clients.png -------------------------------------------------------------------------------- /design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2Cmo/ios-webkit-debug-proxy/master/design.png -------------------------------------------------------------------------------- /overview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2Cmo/ios-webkit-debug-proxy/master/overview.png -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | # Google BSD license https://developers.google.com/google-bsd-license 2 | # Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | AUTOMAKE_OPTIONS = foreign 5 | SUBDIRS = src include examples 6 | -------------------------------------------------------------------------------- /include/Makefile.am: -------------------------------------------------------------------------------- 1 | # Google BSD license https://developers.google.com/google-bsd-license 2 | # Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | nobase_include_HEADERS = \ 5 | ios-webkit-debug-proxy/device_listener.h \ 6 | ios-webkit-debug-proxy/ios_webkit_debug_proxy.h \ 7 | ios-webkit-debug-proxy/socket_manager.h \ 8 | ios-webkit-debug-proxy/webinspector.h \ 9 | ios-webkit-debug-proxy/websocket.h 10 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Google BSD license https://developers.google.com/google-bsd-license 4 | # Copyright 2012 Google Inc. wrightt@google.com 5 | 6 | run () { 7 | echo $* 8 | $* || exit 1 9 | } 10 | 11 | gprefix=`which glibtoolize 2>&1 >/dev/null` 12 | if [ $? -eq 0 ]; then 13 | run glibtoolize --force 14 | else 15 | run libtoolize --force 16 | fi 17 | run aclocal 18 | run autoheader 19 | run automake --add-missing 20 | run autoconf 21 | 22 | if [ -z "$NOCONFIGURE" ]; then 23 | run ./configure "$@" 24 | fi 25 | -------------------------------------------------------------------------------- /include/ios-webkit-debug-proxy/idevice_ext.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // libimobildevice extensions 6 | // 7 | 8 | #ifndef IDEVICE_EXT_H 9 | #define IDEVICE_EXT_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | int idevice_ext_connection_enable_ssl(const char *device_id, int fd, SSL **to_session); 19 | 20 | #ifdef __cplusplus 21 | } 22 | #endif 23 | 24 | #endif /* IDEVICE_EXT_H */ 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled source 2 | *.o 3 | *.lo 4 | *.la 5 | .deps 6 | .libs 7 | src/ios_webkit_debug_proxy 8 | examples/dl_client 9 | examples/wi_client 10 | examples/ws_echo1 11 | examples/ws_echo2 12 | *.exe 13 | 14 | # OS generated files 15 | .DS_Store 16 | 17 | # Build generated files 18 | Makefile 19 | Makefile.in 20 | aclocal.m4 21 | autom4te.cache 22 | autoscan.log 23 | compile 24 | config.guess 25 | config.sub 26 | config.h 27 | config.h.in 28 | config.log 29 | config.status 30 | configure 31 | configure.scan 32 | depcomp 33 | ltmain.sh 34 | install-sh 35 | missing 36 | stamp-h1 37 | libtool 38 | *~ 39 | 40 | # Vim 41 | *.sw[o-p] 42 | -------------------------------------------------------------------------------- /examples/ws_client.html: -------------------------------------------------------------------------------- 1 | 7 |
32 | Run WebSocket Test 33 |
34 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | Google BSD license 2 | Copyright 2012 Google Inc. 3 | 4 | Example Proxy Clients 5 | --------------------- 6 | 7 | - Browser HTML 8 | \- supports HTML-formatted list of devices, pages, and DevTools UI. 9 | 10 | - Browser JSON 11 | \- supports JSON-formatted output. 12 | 13 | - Browser JS 14 | \- [wdp_client.html](wdp_client.html) demonstrates DevTools command I/O, e.g. "Page.navigate". 15 | 16 | - NodeJS 17 | \- [wdp_client.js](wdp_client.js) is the command-line equivalent of [wdp_client.html](wdp_client.html). 18 | 19 | - C 20 | \- [wi_client.c](wi_client.c) a minimal webinspector client. 21 | 22 | 23 | Example System Clients 24 | ---------------------- 25 | 26 | - Device attach/remove listener 27 | \- [dl_client.c](dl_client.c) 28 | 29 | - WebSocket "echo" client (for ws_echo* testing) 30 | \- [ws_client.html](ws_client.html) 31 | 32 | - WebSocket "echo" servers 33 | \- [ws_echo1.c](ws_echo1.c) uses blocking I/O 34 | \- [ws_echo2.c](ws_echo2.c) uses non-blocking I/O 35 | -------------------------------------------------------------------------------- /examples/ws_echo_common.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A minimal websocket "echo" server 6 | // 7 | 8 | #ifndef WS_ECHO_COMMON_H 9 | #define WS_ECHO_COMMON_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "ios-webkit-debug-proxy/websocket.h" 21 | 22 | struct my_struct { 23 | int fd; 24 | int port; 25 | ws_t ws; 26 | }; 27 | typedef struct my_struct *my_t; 28 | my_t my_new(int fd, int port); 29 | void my_free(my_t my); 30 | 31 | 32 | ws_status send_data(ws_t ws, const char *data, size_t length); 33 | 34 | ws_status on_http_request(ws_t ws, 35 | const char *method, const char *resource, const char *version, 36 | const char *host, const char *headers, size_t headers_length, 37 | bool is_websocket, bool *to_keep_alive); 38 | 39 | ws_status on_upgrade(ws_t ws, 40 | const char *resource, const char *protocol, 41 | int version, const char *sec_key); 42 | 43 | ws_status on_frame(ws_t ws, 44 | bool is_fin, uint8_t opcode, bool is_masking, 45 | const char *payload_data, size_t payload_length, 46 | bool *to_keep); 47 | 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | 53 | #endif /* WS_ECHO_COMMON_H */ 54 | -------------------------------------------------------------------------------- /examples/wdp_client.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | // Google BSD license https://developers.google.com/google-bsd-license 3 | // Copyright 2012 Google Inc. wrightt@google.com 4 | 5 | // Node.js example client 6 | // 7 | // Requires `npm install optimist ws` 8 | 9 | var argv = require('optimist') 10 | .default({ port: 9222, page: 1, url: 'http://www.google.com' }) 11 | .usage('Usage: $0 [OPTIONS]\n\nSends a Page.navigate command to the proxy.') 12 | .boolean('help') 13 | .check(function() { return !arguments[0].help; }) 14 | .argv 15 | 16 | var url = 'ws://localhost:' + argv.port + '/devtools/page/' + argv.page 17 | var commands = [ 18 | '{"id": 1, "method": "Page.navigate", "params":{"url": "' + argv.url + '"}}' 19 | ] 20 | 21 | var WebSocket = require('ws'); 22 | console.log('open '+url); 23 | var ws = new WebSocket(url) 24 | var count = 0 25 | 26 | ws.on('open', function() { 27 | console.log('connected'); 28 | if (count < commands.length) { 29 | console.log('send '+commands[count]) 30 | ws.send(commands[count++]) 31 | } 32 | }); 33 | ws.on('close', function() { 34 | console.log('disconnected'); 35 | ws.close() 36 | }); 37 | ws.on('message', function(data, flags) { 38 | console.log('recv %s', data) 39 | if (count < commands.length) { 40 | console.log('send '+commands[count]) 41 | ws.send(commands[count++]) 42 | } else { 43 | // if we sent Page.enable, we could listen for Page.loadEventFired to 44 | // see if our nav succeeded. 45 | ws.close() 46 | } 47 | }); 48 | -------------------------------------------------------------------------------- /include/strndup.h: -------------------------------------------------------------------------------- 1 | /* Implement the strndup function. 2 | Copyright (C) 2005 Free Software Foundation, Inc. 3 | Written by Kaveh R. Ghazi . 4 | 5 | This file is part of the libiberty library. 6 | Libiberty is free software; you can redistribute it and/or 7 | modify it under the terms of the GNU Library General Public 8 | License as published by the Free Software Foundation; either 9 | version 2 of the License, or (at your option) any later version. 10 | 11 | Libiberty is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 | Library General Public License for more details. 15 | 16 | You should have received a copy of the GNU Library General Public 17 | License along with libiberty; see the file COPYING.LIB. If 18 | not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, 19 | Boston, MA 02110-1301, USA. */ 20 | 21 | #ifndef __STRNDUP_H 22 | #define __STRNDUP_H 23 | 24 | #ifdef HAVE_CONFIG_H 25 | #include 26 | #endif 27 | 28 | #include 29 | 30 | #ifndef HAVE_STRNDUP 31 | static inline char* strndup(const char *s, size_t n) 32 | { 33 | char *result; 34 | size_t len = strlen (s); 35 | 36 | if (n < len) 37 | len = n; 38 | 39 | result = (char *) malloc (len + 1); 40 | if (!result) 41 | return 0; 42 | 43 | result[len] = '\0'; 44 | return (char *) memcpy (result, s, len); 45 | } 46 | #endif 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | https://developers.google.com/google-bsd-license 2 | 3 | Copyright 2012, Google Inc. 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are 8 | met: 9 | 10 | * Redistributions of source code must retain the above copyright 11 | notice, this list of conditions and the following disclaimer. 12 | * Redistributions in binary form must reproduce the above 13 | copyright notice, this list of conditions and the following disclaimer 14 | in the documentation and/or other materials provided with the 15 | distribution. 16 | * Neither the name of Google Inc. nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | -------------------------------------------------------------------------------- /examples/Makefile.am: -------------------------------------------------------------------------------- 1 | # Google BSD license https://developers.google.com/google-bsd-license 2 | # Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/src 5 | 6 | AM_CFLAGS = $(GLOBAL_CFLAGS) $(libimobiledevice_CFLAGS) $(libplist_CFLAGS) $(libusbmuxd_CFLAGS) $(openssl_CFLAGS) 7 | AM_LDFLAGS = $(libimobiledevice_LIBS) $(libplist_LIBS) $(libusbmuxd_LIBS) $(openssl_LIBS) 8 | 9 | noinst_PROGRAMS = ws_echo1 ws_echo2 wi_client dl_client 10 | 11 | ws_echo1_SOURCES = ws_echo1.c \ 12 | ws_echo_common.c ws_echo_common.h 13 | ws_echo1_CFLAGS = $(AM_CFLAGS) 14 | ws_echo1_LDFLAGS = $(top_builddir)/src/libios_webkit_debug_proxy.la $(AM_LDFLAGS) 15 | ws_echo1_LDADD = $(top_builddir)/src/libios_webkit_debug_proxy.la 16 | 17 | ws_echo2_SOURCES = ws_echo2.c \ 18 | ws_echo_common.c ws_echo_common.h \ 19 | base64.h \ 20 | char_buffer.h \ 21 | sha1.h \ 22 | socket_manager.h \ 23 | hash_table.h \ 24 | websocket.h 25 | ws_echo2_LDADD = \ 26 | ../src/base64.o \ 27 | ../src/char_buffer.o \ 28 | ../src/hash_table.o \ 29 | ../src/sha1.o \ 30 | ../src/socket_manager.o \ 31 | ../src/websocket.o 32 | 33 | wi_client_SOURCES = \ 34 | wi_client.c \ 35 | char_buffer.h \ 36 | rpc.h \ 37 | idevice_ext.h \ 38 | webinspector.h 39 | wi_client_LDADD = \ 40 | ../src/char_buffer.o \ 41 | ../src/rpc.o \ 42 | ../src/idevice_ext.o \ 43 | ../src/webinspector.o 44 | 45 | dl_client_SOURCES = \ 46 | dl_client.c \ 47 | char_buffer.h \ 48 | device_listener.h \ 49 | hash_table.h 50 | dl_client_LDADD = \ 51 | ../src/char_buffer.o \ 52 | ../src/device_listener.o \ 53 | ../src/hash_table.o 54 | -------------------------------------------------------------------------------- /src/hash_table.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A generic hash table implementation 6 | // 7 | 8 | #ifndef HASH_TABLE_H 9 | #define HASH_TABLE_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | 17 | // cast int to void* 18 | #define HT_KEY(i) ((void *)(intptr_t)i) 19 | #define HT_VALUE(i) HT_KEY(i) 20 | 21 | enum ht_key_type { 22 | HT_INT_KEYS, 23 | HT_STRING_KEYS 24 | }; 25 | 26 | struct ht_entry_struct; 27 | typedef struct ht_entry_struct *ht_entry_t; 28 | 29 | struct ht_struct; 30 | typedef struct ht_struct *ht_t; 31 | 32 | ht_t ht_new(enum ht_key_type type); 33 | 34 | // note: doesn't free keys or values! 35 | void ht_clear(ht_t self); 36 | 37 | void ht_free(ht_t self); 38 | 39 | // @result Returns number of keys 40 | size_t ht_size(ht_t self); 41 | 42 | void *ht_get_key(ht_t self, const void *key); 43 | void *ht_get_value(ht_t self, const void *key); 44 | 45 | void *ht_remove(ht_t self, const void *key); 46 | 47 | void *ht_put(ht_t self, void *key, void *value); 48 | 49 | // @result Returns a dynamically-allocated array of length ht_size+1 50 | void **ht_keys(ht_t self); 51 | void **ht_values(ht_t self); 52 | 53 | struct ht_struct { 54 | // Only need to set these if your using non-int keys: 55 | intptr_t (*on_hash)(ht_t self, const void *key); 56 | intptr_t (*on_cmp)(ht_t self, const void *key1, const void *key2); 57 | 58 | // For internal use only: 59 | size_t num_keys; 60 | ht_entry_t *buckets; 61 | size_t num_buckets; 62 | }; 63 | 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | #endif /* HASH_TABLE_H */ 70 | 71 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | # Google BSD license https://developers.google.com/google-bsd-license 2 | # Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/include/ios-webkit-debug-proxy 5 | 6 | AM_CFLAGS = $(GLOBAL_CFLAGS) $(libimobiledevice_CFLAGS) $(libplist_CFLAGS) $(libusbmuxd_CFLAGS) $(libpcreposix_CFLAGS) $(openssl_CFLAGS) 7 | AM_LDFLAGS = $(libimobiledevice_LIBS) $(libplist_LIBS) $(libusbmuxd_LIBS) $(libpcreposix_LIBS) $(openssl_LIBS) 8 | 9 | lib_LTLIBRARIES = libios_webkit_debug_proxy.la 10 | libios_webkit_debug_proxy_la_LIBADD = 11 | libios_webkit_debug_proxy_la_LDFLAGS = $(AM_LDFLAGS) 12 | libios_webkit_debug_proxy_la_SOURCES = ios_webkit_debug_proxy_main.c \ 13 | base64.c base64.h \ 14 | char_buffer.c char_buffer.h \ 15 | device_listener.c device_listener.h \ 16 | hash_table.c hash_table.h \ 17 | ios_webkit_debug_proxy.c ios_webkit_debug_proxy.h \ 18 | port_config.c port_config.h \ 19 | rpc.c rpc.h \ 20 | sha1.c sha1.h \ 21 | socket_manager.c socket_manager.h \ 22 | validate_utf8.h \ 23 | idevice_ext.c idevice_ext.h \ 24 | webinspector.c webinspector.h \ 25 | websocket.c websocket.h 26 | 27 | bin_PROGRAMS = ios_webkit_debug_proxy 28 | ios_webkit_debug_proxy_SOURCES = ios_webkit_debug_proxy_main.c \ 29 | base64.c base64.h \ 30 | char_buffer.c char_buffer.h \ 31 | device_listener.c device_listener.h \ 32 | hash_table.c hash_table.h \ 33 | ios_webkit_debug_proxy.c ios_webkit_debug_proxy.h \ 34 | port_config.c port_config.h \ 35 | rpc.c rpc.h \ 36 | sha1.c sha1.h \ 37 | socket_manager.c socket_manager.h \ 38 | validate_utf8.h \ 39 | idevice_ext.c idevice_ext.h \ 40 | webinspector.c webinspector.h \ 41 | websocket.c websocket.h 42 | ios_webkit_debug_proxy_CFLAGS = $(AM_CFLAGS) 43 | ios_webkit_debug_proxy_LDFLAGS = $(AM_LDFLAGS) 44 | -------------------------------------------------------------------------------- /include/ios-webkit-debug-proxy/device_listener.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // iOS device add/remove listener. 6 | // 7 | 8 | #ifndef DEVICE_LISTENER_H 9 | #define DEVICE_LISTENER_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | typedef uint8_t dl_status; 21 | #define DL_ERROR 1 22 | #define DL_SUCCESS 0 23 | 24 | 25 | // Create a device add/remove connection. 26 | // @param recv_timeout milliseconds, negative for non_blocking 27 | // @result fd, or -1 for error 28 | int dl_connect(int recv_timeout); 29 | 30 | 31 | struct dl_struct; 32 | typedef struct dl_struct *dl_t; 33 | dl_t dl_new(); 34 | void dl_free(dl_t self); 35 | 36 | struct dl_private; 37 | typedef struct dl_private *dl_private_t; 38 | 39 | // iOS device add/remove listener. 40 | struct dl_struct { 41 | 42 | // 43 | // Use these API: 44 | // 45 | 46 | // Call once after startup. 47 | dl_status (*start)(dl_t self); 48 | 49 | // Call to append data, calls on_attach/on_detach when we have a full 50 | // input packet. 51 | dl_status (*on_recv)(dl_t self, const char *buf, ssize_t length); 52 | 53 | void *state; 54 | bool *is_debug; 55 | 56 | // 57 | // Set these callbacks: 58 | // 59 | 60 | // Called to send "listen" and other output packets. 61 | dl_status (*send_packet)(dl_t self, const char *buf, size_t length); 62 | 63 | // Called by on_recv. 64 | // @param device_id 40-character hex iOS device identifier. 65 | // @param device_num usbmuxd device identifier 66 | dl_status (*on_attach)(dl_t self, const char *device_id, int device_num); 67 | 68 | dl_status (*on_detach)(dl_t self, const char *device_id, int device_num); 69 | 70 | // For internal use only: 71 | dl_private_t private_state; 72 | }; 73 | 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | 79 | #endif /* DEVICE_LISTENER_H */ 80 | 81 | -------------------------------------------------------------------------------- /src/port_config.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // device_id-to-port(s) config file reader. 6 | // 7 | 8 | #ifndef PORT_CONFIG_H 9 | #define PORT_CONFIG_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | 19 | struct pc_struct; 20 | typedef struct pc_struct *pc_t; 21 | 22 | pc_t pc_new(); 23 | void pc_free(pc_t self); 24 | 25 | void pc_clear(pc_t self); 26 | 27 | // Add a rule. 28 | void pc_add(pc_t self, const char *device_id, int min_port, int max_port); 29 | 30 | // Parse a line and add all rules. 31 | // 32 | // Lines should match: 33 | // [ITEM ("," ITEM)*] ["#" comment] 34 | // where each ITEM is: 35 | // [40-char-hex or "*" or "null"] [" " or ":""] min_port[-max_port] 36 | // 37 | // Examples: 38 | // # comments and blank lines are ignored 39 | // 4ea8dd11e8c4fbc1a2deadbeefa0fd3bbbb268c7:9227 40 | // 4ea8dd11e8c4fbc1a2deadbeefa0fd3bbbb268c7 9227 # same as above 41 | // ddc86a51827948e13bdeadbeef5bc588ea35fcf2:9225-9340 # scan range 42 | // 007007deadbeefe724327890fda98434dabcdeff:9229-9229 # same as 9229 43 | // 4223489deadbeef123478432098342039abcdabc:9322 # iphoneX 44 | // 123478934adcee0000000000000000000000000c:-1 # explicit ignore 45 | // null:9221 # sets the "9221" device list 46 | // *:9222-9299 # default to scan 47 | // * 9222-9299 # same as above 48 | // :9222-9299 # same as above 49 | // Example with comma: 50 | // 4ea8dd11e8c4fbc1a2deadbeefa0fd3bbbb268c7:9227,:9222-9299 51 | // 52 | // @result NULL if success, else pointer in line of first invalid item 53 | const char *pc_add_line(pc_t self, const char *line, size_t len); 54 | 55 | // Calls pc_add_line for every line in a file. 56 | // @param filename path 57 | // @result 0 if success 58 | int pc_add_file(pc_t self, const char *filename); 59 | 60 | // Looks up the device_id and sets the to_*ports. 61 | int pc_select_port(pc_t self, const char *device_id, 62 | int *to_port, int *to_min_port, int *to_max_port); 63 | 64 | 65 | #ifdef __cplusplus 66 | } 67 | #endif 68 | 69 | #endif /* PORT_CONFIG_H */ 70 | 71 | -------------------------------------------------------------------------------- /include/ios-webkit-debug-proxy/socket_manager.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A generic select-based socket manager. 6 | // 7 | 8 | #ifndef SOCKET_SELECTOR_H 9 | #define SOCKET_SELECTOR_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | // Bind a server port, return the file descriptor (or -1 for error). 19 | int sm_listen(int port); 20 | 21 | // Connect to a server, return the file descriptor (or -1 for error). 22 | int sm_connect(const char *socket_addr); 23 | 24 | 25 | typedef uint8_t sm_status; 26 | #define SM_ERROR 1 27 | #define SM_SUCCESS 0 28 | 29 | 30 | struct sm_private; 31 | typedef struct sm_private *sm_private_t; 32 | 33 | struct sm_struct; 34 | typedef struct sm_struct *sm_t; 35 | sm_t sm_new(size_t buffer_length); 36 | void sm_free(sm_t self); 37 | 38 | struct sm_struct { 39 | 40 | // Call these APIs: 41 | 42 | // @param value a value to associate with this fd, which will be passed 43 | // in future on_accept/on_recv/on_close callbacks. 44 | sm_status (*add_fd)(sm_t self, int fd, void *ssl_session, void *value, bool 45 | is_server); 46 | 47 | sm_status (*remove_fd)(sm_t self, int fd); 48 | 49 | // @param value a value for the on_sent callback 50 | sm_status (*send)(sm_t self, int fd, const char *data, size_t length, 51 | void* value); 52 | 53 | int (*select)(sm_t self, int timeout_secs); 54 | 55 | sm_status (*cleanup)(sm_t self); 56 | 57 | void *state; 58 | bool *is_debug; 59 | 60 | // Set these callbacks: 61 | 62 | // @param server_value specified in the add_fd call 63 | // @param to_value will be used in future on_recv calls 64 | sm_status (*on_accept)(sm_t self, 65 | int server_fd, void *server_value, 66 | int fd, void **to_value); 67 | 68 | sm_status (*on_sent)(sm_t self, int fd, void *value, 69 | const char *buf, ssize_t length); 70 | 71 | sm_status (*on_recv)(sm_t self, int fd, void *value, 72 | const char *buf, ssize_t length); 73 | 74 | sm_status (*on_close)(sm_t self, int fd, void *value, bool is_server); 75 | 76 | // For internal use only: 77 | sm_private_t private_state; 78 | }; 79 | 80 | #ifdef __cplusplus 81 | } 82 | #endif 83 | 84 | #endif /* SOCKET_SELECTOR_H */ 85 | 86 | -------------------------------------------------------------------------------- /examples/dl_client.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // An example device_listener client 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef WIN32 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | #include "ios-webkit-debug-proxy/device_listener.h" 25 | 26 | #define BUF_LEN 1024 27 | 28 | struct my_struct { 29 | int fd; 30 | }; 31 | typedef struct my_struct *my_t; 32 | 33 | // callbacks: 34 | dl_status my_send_packet(dl_t dl, const char *buf, size_t length) { 35 | int fd = ((my_t)dl->state)->fd; 36 | ssize_t send_bytes = send(fd, buf, length, 0); 37 | return (send_bytes == length ? DL_SUCCESS : DL_ERROR); 38 | } 39 | dl_status my_on_attach(dl_t dl, const char *device_id, int device_num) { 40 | printf("on_attach %s %d\n", device_id, device_num); 41 | return DL_SUCCESS; 42 | } 43 | dl_status my_on_detach(dl_t dl, const char *device_id, int device_num) { 44 | printf("on_detach %s %d\n", device_id, device_num); 45 | return DL_SUCCESS; 46 | } 47 | 48 | int main(int argc, char** argv) { 49 | #ifdef WIN32 50 | WSADATA wsa_data; 51 | int res = WSAStartup(MAKEWORD(2,2), &wsa_data); 52 | if (res) { 53 | fprintf(stderr, "WSAStartup failed with error: %d\n", res); 54 | exit(1); 55 | } 56 | #endif 57 | 58 | int fd = dl_connect(3000); 59 | if (fd < 0) { 60 | return -1; 61 | } 62 | 63 | dl_t dl = dl_new(); 64 | dl->send_packet = my_send_packet; 65 | dl->on_attach = my_on_attach; 66 | dl->on_detach = my_on_detach; 67 | my_t my = (my_t)malloc(sizeof(struct my_struct)); 68 | my->fd = fd; 69 | dl->state = my; 70 | 71 | if (dl->start(dl)) { 72 | return -1; 73 | } 74 | 75 | char buf[BUF_LEN]; 76 | while (1) { 77 | ssize_t read_bytes = recv(fd, buf, BUF_LEN, 0); 78 | if (read_bytes < 0) { 79 | #ifdef WIN32 80 | if (WSAGetLastError() == WSAETIMEDOUT) { 81 | #else 82 | if (errno == EAGAIN) { 83 | #endif 84 | continue; 85 | } 86 | break; 87 | } 88 | if (dl->on_recv(dl, buf, read_bytes)) { 89 | break; 90 | } 91 | } 92 | free(dl->state); 93 | dl_free(dl); 94 | #ifdef WIN32 95 | closesocket(fd); 96 | WSACleanup(); 97 | #else 98 | close(fd); 99 | #endif 100 | return 0; 101 | } 102 | 103 | -------------------------------------------------------------------------------- /examples/ws_echo2.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A select-based websocket "echo" server 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef WIN32 21 | #include 22 | #else 23 | #include 24 | #include 25 | #endif 26 | 27 | #include "ios-webkit-debug-proxy/socket_manager.h" 28 | #include "ws_echo_common.h" 29 | 30 | struct my_sm_struct { 31 | int port; 32 | }; 33 | typedef struct my_sm_struct *my_sm_t; 34 | 35 | sm_status on_accept(sm_t sm, int server_fd, void *server_value, 36 | int fd, void **to_value) { 37 | int port = ((my_sm_t)server_value)->port; 38 | *to_value = my_new(fd, port); 39 | return (*to_value ? SM_SUCCESS : SM_ERROR); 40 | } 41 | 42 | sm_status on_recv(sm_t sm, int fd, void *value, 43 | const char *buf, ssize_t length) { 44 | ws_t ws = ((my_t)value)->ws; 45 | return ws->on_recv(ws, buf, length); 46 | } 47 | 48 | sm_status on_close(sm_t sm, int fd, void *value, bool is_server) { 49 | if (!is_server) { 50 | my_free((my_t)value); 51 | } 52 | return SM_SUCCESS; 53 | } 54 | 55 | static int quit_flag = 0; 56 | 57 | static void on_signal(int sig) { 58 | fprintf(stderr, "Exiting...\n"); 59 | quit_flag++; 60 | } 61 | 62 | int main(int argc, char** argv) { 63 | signal(SIGINT, on_signal); 64 | signal(SIGTERM, on_signal); 65 | 66 | #ifdef WIN32 67 | WSADATA wsa_data; 68 | int res = WSAStartup(MAKEWORD(2,2), &wsa_data); 69 | if (res) { 70 | fprintf(stderr, "WSAStartup failed with error: %d\n", res); 71 | exit(1); 72 | } 73 | #endif 74 | 75 | int port = 8080; 76 | 77 | int s_fd = sm_listen(port); 78 | if (s_fd < 0) { 79 | return -1; 80 | } 81 | 82 | sm_t sm = sm_new(4096); 83 | sm->on_accept = on_accept; 84 | sm->on_recv = on_recv; 85 | sm->on_close = on_close; 86 | 87 | my_sm_t my_sm = (my_sm_t)malloc(sizeof(struct my_sm_struct)); 88 | my_sm->port = port; 89 | //sm->state = my_sm; // optional 90 | 91 | sm->add_fd(sm, s_fd, NULL, my_sm, true); 92 | 93 | int ret = 0; 94 | while (!quit_flag) { 95 | if (sm->select(sm, 2) < 0) { 96 | ret = -1; 97 | break; 98 | } 99 | } 100 | sm->cleanup(sm); 101 | free(my_sm); 102 | sm_free(sm); 103 | #ifdef WIN32 104 | WSACleanup(); 105 | #endif 106 | return ret; 107 | } 108 | 109 | -------------------------------------------------------------------------------- /examples/ws_echo1.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A minimal websocket "echo" server 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef WIN32 19 | #include 20 | #else 21 | #include 22 | #include 23 | #include 24 | #endif 25 | 26 | #include "ws_echo_common.h" 27 | #include "ios-webkit-debug-proxy/websocket.h" 28 | 29 | #define BUF_LEN 1024 30 | #define PORT 8080 31 | 32 | int main(int argc, char** argv) { 33 | int port = PORT; 34 | 35 | #ifdef WIN32 36 | WSADATA wsa_data; 37 | int res = WSAStartup(MAKEWORD(2,2), &wsa_data); 38 | if (res) { 39 | fprintf(stderr, "WSAStartup failed with error: %d\n", res); 40 | exit(1); 41 | } 42 | #endif 43 | 44 | int sfd = socket(AF_INET, SOCK_STREAM, 0); 45 | if (sfd < 0) { 46 | return -1; 47 | } 48 | 49 | struct sockaddr_in local; 50 | local.sin_family = AF_INET; 51 | local.sin_addr.s_addr = INADDR_ANY; 52 | local.sin_port = htons(port); 53 | int on = 1; 54 | #ifdef WIN32 55 | if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) || 56 | bind(sfd, (struct sockaddr*)&local, sizeof(local)) == SOCKET_ERROR || 57 | listen(sfd, 1)) { 58 | fprintf(stderr, "Unable to bind: %d\n", WSAGetLastError()); 59 | closesocket(sfd); 60 | #else 61 | if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0 || 62 | bind(sfd, (struct sockaddr*)&local, sizeof(local)) < 0 || 63 | listen(sfd, 1)) { 64 | perror("Unable to bind"); 65 | close(sfd); 66 | #endif 67 | return -1; 68 | } 69 | 70 | int ret = 0; 71 | while (1) { 72 | int fd = accept(sfd, NULL, NULL); 73 | if (fd < 0) { 74 | perror("Accept failed"); 75 | ret = -1; 76 | break; 77 | } 78 | 79 | my_t my = my_new(fd, port); 80 | if (!my) { 81 | ret = -1; 82 | break; 83 | } 84 | ws_t ws = my->ws; 85 | 86 | char buf[BUF_LEN]; 87 | while (1) { 88 | ssize_t read_bytes = recv(fd, buf, BUF_LEN, 0); 89 | if (ws->on_recv(ws, buf, read_bytes)) { 90 | break; 91 | } 92 | } 93 | #ifdef WIN32 94 | closesocket(fd); 95 | #else 96 | close(fd); 97 | #endif 98 | my_free(my); 99 | } 100 | 101 | #ifdef WIN32 102 | closesocket(sfd); 103 | WSACleanup(); 104 | #else 105 | close(sfd); 106 | #endif 107 | return ret; 108 | } 109 | -------------------------------------------------------------------------------- /include/strcasestr.h: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1990, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * This code is derived from software contributed to Berkeley by 6 | * Chris Torek. 7 | * 8 | * Redistribution and use in source and binary forms, with or without 9 | * modification, are permitted provided that the following conditions 10 | * are met: 11 | * 1. Redistributions of source code must retain the above copyright 12 | * notice, this list of conditions and the following disclaimer. 13 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. All advertising materials mentioning features or use of this software 17 | * must display the following acknowledgement: 18 | * This product includes software developed by the University of 19 | * California, Berkeley and its contributors. 20 | * 4. Neither the name of the University nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 | * SUCH DAMAGE. 35 | */ 36 | 37 | #ifndef __STRCASESTR_H 38 | #define __STRCASESTR_H 39 | 40 | #ifdef HAVE_CONFIG_H 41 | #include 42 | #endif 43 | 44 | #include 45 | #include 46 | 47 | #ifndef HAVE_STRCASESTR 48 | static inline char* strcasestr(const char *s, const char *find) 49 | { 50 | char c, sc; 51 | size_t len; 52 | 53 | if ((c = *find++) != 0) { 54 | c = tolower((unsigned char)c); 55 | len = strlen(find); 56 | do { 57 | do { 58 | if ((sc = *s++) == 0) 59 | return (NULL); 60 | } while ((char)tolower((unsigned char)sc) != c); 61 | } while (strncasecmp(s, find, len) != 0); 62 | s--; 63 | } 64 | return ((char *)s); 65 | } 66 | #endif 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /include/ios-webkit-debug-proxy/webinspector.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // iOS WebInspector 6 | // 7 | 8 | #ifndef WEBINSPECTOR_H 9 | #define WEBINSPECTOR_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | typedef uint8_t wi_status; 21 | #define WI_ERROR 1 22 | #define WI_SUCCESS 0 23 | 24 | 25 | // Create a webinspector connection. 26 | // 27 | // @param device_id iOS 40-character device id, or NULL for any device 28 | // @param to_device_id selected device_id (copy of device_id if set) 29 | // @param to_device_name selected device name 30 | // @param recv_timeout Set the socket receive timeout for future recv calls: 31 | // negative for non-blocking, 32 | // zero for the system default (5000 millis), or 33 | // positive for milliseconds. 34 | // @result fd, or -1 for error 35 | int wi_connect(const char *device_id, char **to_device_id, 36 | char **to_device_name, int *to_device_os_version, 37 | void **to_ssl_session, int recv_timeout); 38 | 39 | struct wi_struct; 40 | typedef struct wi_struct *wi_t; 41 | wi_t wi_new(bool partials_supported); 42 | void wi_free(wi_t self); 43 | 44 | struct wi_private; 45 | typedef struct wi_private *wi_private_t; 46 | 47 | // iOS WebInspector. 48 | struct wi_struct { 49 | 50 | // 51 | // Call these APIs: 52 | // 53 | 54 | // Appends an arbitrary number of bytes to our input buffer, 55 | // calls recv_packet when the buffer contains one or more packets. 56 | wi_status (*on_recv)(wi_t self, const char *buf, ssize_t length); 57 | 58 | // Calls recv_plist if the packet is a full plist, otherwise appends 59 | // the partial packet to our pending buffer. 60 | wi_status (*recv_packet)(wi_t self, const char *packet, ssize_t length); 61 | 62 | // Calls send_packet with the serialized rpc packet(s). 63 | wi_status (*send_plist)(wi_t self, const plist_t rpc_dict); 64 | 65 | // Optional state for use in your callbacks. 66 | void *state; 67 | bool *is_debug; 68 | 69 | // 70 | // Set these callbacks: 71 | // 72 | 73 | // Send a serialized rpc (full or partial). 74 | wi_status (*send_packet)(wi_t self, const char *packet, size_t length); 75 | 76 | // Receive a deserialized full rpc. 77 | wi_status (*recv_plist)(wi_t self, const plist_t rpc_dict); 78 | 79 | // For internal use only: 80 | wi_status (*on_error)(wi_t self, const char *format, ...); 81 | wi_private_t private_state; 82 | }; 83 | 84 | 85 | #ifdef __cplusplus 86 | } 87 | #endif 88 | 89 | #endif /* WEBINSPECTOR_H */ 90 | -------------------------------------------------------------------------------- /src/char_buffer.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifndef CHAR_BUFFER_H 5 | #define CHAR_BUFFER_H 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | 12 | #include 13 | 14 | 15 | struct cb_struct { 16 | char *begin; 17 | char *head; 18 | char *tail; 19 | char *end; 20 | 21 | const char *in_head; 22 | const char *in_tail; 23 | }; 24 | typedef struct cb_struct *cb_t; 25 | 26 | cb_t cb_new(); 27 | 28 | void cb_free(cb_t buffer); 29 | 30 | void cb_clear(cb_t buffer); 31 | 32 | int cb_ensure_capacity(cb_t self, size_t needed); 33 | 34 | // Instead of copying our input into our my->in, e.g.: 35 | // cb_ensure_capacity(my->in, length); 36 | // memcpy(my->in->tail, buf, length); 37 | // my->in->tail += length; 38 | // we'll avoid the memcpy, if possible. The shared pointer is 39 | // "my->in_head". 40 | int cb_begin_input(cb_t self, const char *buf, ssize_t length); 41 | 42 | int cb_end_input(cb_t self); 43 | 44 | 45 | // Print a buffer to a new string. 46 | // 47 | // @param to_buf 48 | // Output buffer, e.g. will be set to: 49 | // " 61 62 0A ab.\n"+ 50 | // " 63 0A c. +3\0"+ 51 | // If NULL then nothing will be written but the return value will be the 52 | // required minimal length. 53 | // @param buf 54 | // Input buffer, e.g. "ab\nc\ndef" 55 | // @param length 56 | // Input length, e.g. 8 57 | // @param max_width 58 | // Max line length, e.g. 25, or negative for no limit 59 | // @param max_lines 60 | // Max number of lines, e.g. 2, or negative for no limit 61 | // @result 62 | // Number of characters written, not including the trailing '\0', e.g. 51 63 | size_t cb_sprint(char *to_buf, const char *buf, ssize_t length, 64 | ssize_t max_width, ssize_t max_lines); 65 | 66 | // Like cb_sprint, but dynamically allocates the *to_buf_ptr. 67 | int cb_asprint(char **to_buf_ptr, const char *buf, ssize_t length, 68 | ssize_t max_width, ssize_t max_lines); 69 | 70 | 71 | // Scans a printed string back into a buffer. 72 | // 73 | // @param to_buf Output buffer, e.g. will be set to: 74 | // Input buffer, e.g. "ab\nc\ndef" 75 | // @param to_length Output length 76 | // @param buf Input buffer, must match the cb_sprint no-limit format, e.g.: 77 | // " 61 62 0A ab.\n"+ 78 | // " 63 0A c.\n"+ 79 | // " 64 65 66 def\0" 80 | // @result 0 for success 81 | int cb_sscan(char *to_buf, size_t *to_length, const char *buf); 82 | 83 | // Like cb_sscan, but dynamically allocates the *to_buf_ptr. 84 | int cb_asscan(char **to_buf_ptr, size_t *to_length, const char *buf); 85 | 86 | 87 | #ifndef __MACH__ 88 | char *strnstr(const char *s1, const char *s2, size_t n); 89 | #endif 90 | 91 | #ifdef __cplusplus 92 | } 93 | #endif 94 | 95 | #endif /* CHAR_BUFFER_H */ 96 | 97 | -------------------------------------------------------------------------------- /include/ios-webkit-debug-proxy/websocket.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifndef WEBSOCKET_H 5 | #define WEBSOCKET_H 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | #include 12 | 13 | 14 | typedef uint8_t ws_opcode; 15 | #define OPCODE_CONTINUATION 0x0 16 | #define OPCODE_TEXT 0x1 17 | #define OPCODE_BINARY 0x2 18 | #define OPCODE_CLOSE 0x8 19 | #define OPCODE_PING 0x9 20 | #define OPCODE_PONG 0xA 21 | 22 | typedef uint16_t ws_close; 23 | #define CLOSE_NORMAL 1000 24 | #define CLOSE_GOING_AWAY 1001 25 | #define CLOSE_PROTOCOL_ERROR 1002 26 | #define CLOSE_BAD_DATA_TYPE 1003 27 | #define CLOSE_INVALID_DATA 1007 28 | #define CLOSE_POLICY_ERROR 1008 29 | #define CLOSE_SIZE_ERROR 1009 30 | #define CLOSE_NO_EXTENSION 1010 31 | #define CLOSE_SERVER_ERROR 1011 32 | 33 | typedef uint8_t ws_status; 34 | #define WS_ERROR 1 35 | #define WS_SUCCESS 0 36 | 37 | 38 | struct ws_struct; 39 | typedef struct ws_struct *ws_t; 40 | ws_t ws_new(); 41 | void ws_free(ws_t self); 42 | 43 | struct ws_private; 44 | typedef struct ws_private *ws_private_t; 45 | 46 | struct ws_struct { 47 | 48 | // 49 | // Use these APIs: 50 | // 51 | 52 | ws_status (*on_recv)(ws_t self, const char *buf, ssize_t length); 53 | 54 | ws_status (*send_connect)(ws_t self, 55 | const char *resource, const char *protocol, 56 | const char *host, const char *origin); 57 | 58 | ws_status (*send_upgrade)(ws_t self); 59 | 60 | ws_status (*send_frame)(ws_t self, 61 | bool is_fin, ws_opcode opcode, bool is_masking, 62 | const char *payload_data, size_t payload_length); 63 | 64 | ws_status (*send_close)(ws_t self, ws_close close_code, 65 | const char *reason); 66 | 67 | void *state; 68 | bool *is_debug; 69 | 70 | // 71 | // Set these callbacks: 72 | // 73 | 74 | ws_status (*send_data)(ws_t self, 75 | const char *data, size_t length); 76 | 77 | ws_status (*on_http_request)(ws_t self, 78 | const char *method, const char *resource, const char *version, 79 | const char *host, const char *headers, size_t headers_length, 80 | bool is_websocket, bool *to_keep_alive); 81 | 82 | ws_status (*on_upgrade)(ws_t self, 83 | const char *resource, const char *protocol, 84 | int version, const char *sec_key); 85 | 86 | ws_status (*on_frame)(ws_t self, 87 | bool is_fin, ws_opcode opcode, bool is_masking, 88 | const char *payload_data, size_t payload_length, 89 | bool *to_keep); 90 | 91 | // For internal use only: 92 | ws_status (*on_error)(ws_t self, const char *format, ...); 93 | ws_private_t private_state; 94 | }; 95 | 96 | 97 | #ifdef __cplusplus 98 | } 99 | #endif 100 | 101 | #endif /* WEBSOCKET_H */ 102 | 103 | -------------------------------------------------------------------------------- /examples/wdp_client.html: -------------------------------------------------------------------------------- 1 | 7 | 76 | 84 | 85 |
86 | Inspect localhost: 87 | ?/devtools/page/? 91 |

92 | 95 |

96 | 97 | 104 | 105 | -------------------------------------------------------------------------------- /src/base64.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file base64.h 3 | * 4 | * \brief RFC 1521 base64 encoding/decoding 5 | * 6 | * Copyright (C) 2006-2010, Brainspark B.V. 7 | * 8 | * This file is part of PolarSSL (http://www.polarssl.org) 9 | * Lead Maintainer: Paul Bakker 10 | * 11 | * All rights reserved. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License along 24 | * with this program; if not, write to the Free Software Foundation, Inc., 25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | */ 27 | #ifndef POLARSSL_BASE64_H 28 | #define POLARSSL_BASE64_H 29 | 30 | #include 31 | 32 | #define POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL -0x002A /**< Output buffer too small. */ 33 | #define POLARSSL_ERR_BASE64_INVALID_CHARACTER -0x002C /**< Invalid character in input. */ 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | /** 40 | * \brief Encode a buffer into base64 format 41 | * 42 | * \param dst destination buffer 43 | * \param dlen size of the buffer 44 | * \param src source buffer 45 | * \param slen amount of data to be encoded 46 | * 47 | * \return 0 if successful, or POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL. 48 | * *dlen is always updated to reflect the amount 49 | * of data that has (or would have) been written. 50 | * 51 | * \note Call this function with *dlen = 0 to obtain the 52 | * required buffer size in *dlen 53 | */ 54 | int base64_encode( unsigned char *dst, size_t *dlen, 55 | const unsigned char *src, size_t slen ); 56 | 57 | /** 58 | * \brief Decode a base64-formatted buffer 59 | * 60 | * \param dst destination buffer 61 | * \param dlen size of the buffer 62 | * \param src source buffer 63 | * \param slen amount of data to be decoded 64 | * 65 | * \return 0 if successful, POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL, or 66 | * POLARSSL_ERR_BASE64_INVALID_DATA if the input data is not 67 | * correct. *dlen is always updated to reflect the amount 68 | * of data that has (or would have) been written. 69 | * 70 | * \note Call this function with *dlen = 0 to obtain the 71 | * required buffer size in *dlen 72 | */ 73 | int base64_decode( unsigned char *dst, size_t *dlen, 74 | const unsigned char *src, size_t slen ); 75 | 76 | /** 77 | * \brief Checkup routine 78 | * 79 | * \return 0 if successful, or 1 if the test failed 80 | */ 81 | int base64_self_test( int verbose ); 82 | 83 | #ifdef __cplusplus 84 | } 85 | #endif 86 | 87 | #endif /* base64.h */ 88 | -------------------------------------------------------------------------------- /include/getline.h: -------------------------------------------------------------------------------- 1 | /* getline.c -- Replacement for GNU C library function getline 2 | 3 | Copyright (C) 1993 Free Software Foundation, Inc. 4 | 5 | This program is free software; you can redistribute it and/or 6 | modify it under the terms of the GNU General Public License as 7 | published by the Free Software Foundation; either version 2 of the 8 | License, or (at your option) any later version. 9 | 10 | This program is distributed in the hope that it will be useful, but 11 | WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 | General Public License for more details. */ 14 | 15 | /* Written by Jan Brittenson, bson@gnu.ai.mit.edu. */ 16 | 17 | #ifndef __GETLINE_H 18 | #define __GETLINE_H 19 | 20 | #ifdef HAVE_CONFIG_H 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #ifndef HAVE_GETLINE 30 | /* Always add at least this many bytes when extending the buffer. */ 31 | #define GETLINE_MIN_CHUNK 256 32 | 33 | static inline int getstr(char **lineptr, size_t *n, FILE *stream, 34 | char terminator, int offset) { 35 | int nchars_avail; /* Allocated but unused chars in *LINEPTR. */ 36 | char *read_pos; /* Where we're reading into *LINEPTR. */ 37 | int ret; 38 | 39 | if (!lineptr || !n || !stream) 40 | { 41 | errno = EINVAL; 42 | return -1; 43 | } 44 | 45 | if (!*lineptr) 46 | { 47 | *n = GETLINE_MIN_CHUNK; 48 | *lineptr = malloc (*n); 49 | if (!*lineptr) 50 | { 51 | errno = ENOMEM; 52 | return -1; 53 | } 54 | } 55 | 56 | nchars_avail = *n - offset; 57 | read_pos = *lineptr + offset; 58 | 59 | for (;;) 60 | { 61 | int save_errno; 62 | register int c = getc (stream); 63 | 64 | save_errno = errno; 65 | 66 | /* We always want at least one char left in the buffer, since we 67 | always (unless we get an error while reading the first char) 68 | NUL-terminate the line buffer. */ 69 | 70 | assert((*lineptr + *n) == (read_pos + nchars_avail)); 71 | if (nchars_avail < 2) 72 | { 73 | if (*n > GETLINE_MIN_CHUNK) 74 | *n *= 2; 75 | else 76 | *n += GETLINE_MIN_CHUNK; 77 | 78 | nchars_avail = *n + *lineptr - read_pos; 79 | *lineptr = realloc (*lineptr, *n); 80 | if (!*lineptr) 81 | { 82 | errno = ENOMEM; 83 | return -1; 84 | } 85 | read_pos = *n - nchars_avail + *lineptr; 86 | assert((*lineptr + *n) == (read_pos + nchars_avail)); 87 | } 88 | 89 | if (ferror (stream)) 90 | { 91 | /* Might like to return partial line, but there is no 92 | place for us to store errno. And we don't want to just 93 | lose errno. */ 94 | errno = save_errno; 95 | return -1; 96 | } 97 | 98 | if (c == EOF) 99 | { 100 | /* Return partial line, if any. */ 101 | if (read_pos == *lineptr) 102 | return -1; 103 | else 104 | break; 105 | } 106 | 107 | *read_pos++ = c; 108 | nchars_avail--; 109 | 110 | if (c == terminator) 111 | /* Return the line. */ 112 | break; 113 | } 114 | 115 | /* Done - NUL terminate and return the number of chars read. */ 116 | *read_pos = '\0'; 117 | 118 | ret = read_pos - (*lineptr + offset); 119 | return ret; 120 | } 121 | 122 | static inline int getline(char **lineptr, size_t *n, FILE *stream) 123 | { 124 | return getstr(lineptr, n, stream, '\n', 0); 125 | } 126 | #endif 127 | 128 | #endif 129 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | # Google BSD license https://developers.google.com/google-bsd-license 2 | # Copyright 2012 Google Inc. wrightt@google.com 3 | # -*- Autoconf -*- 4 | # Process this file with autoconf to produce a configure script. 5 | 6 | AC_PREREQ([2.63]) 7 | AC_INIT([ios_webkit_debug_proxy], [1.9.0], [https://github.com/google/ios-webkit-debug-proxy/issues]) 8 | AM_INIT_AUTOMAKE([1.10 no-define]) 9 | AC_CONFIG_SRCDIR([src/]) 10 | AC_CONFIG_HEADERS([config.h]) 11 | 12 | # Checks for programs. 13 | AC_PROG_CC 14 | AM_PROG_CC_C_O 15 | AC_PROG_INSTALL 16 | PKG_PROG_PKG_CONFIG 17 | 18 | # Checks for libraries. 19 | PKG_CHECK_MODULES(libimobiledevice, libimobiledevice-1.0 >= 1.3.0) 20 | PKG_CHECK_MODULES(libplist, libplist-2.0 >= 2.2.0) 21 | PKG_CHECK_MODULES(libusbmuxd, libusbmuxd-2.0 >= 2.0.0) 22 | PKG_CHECK_MODULES(openssl, openssl >= 1.1.0) 23 | AC_CHECK_LIB([plist-2.0], [plist_to_xml], 24 | [ ], [AC_MSG_FAILURE([*** Unable to link with libplist])], 25 | [$libplist_LIBS]) 26 | AC_CHECK_LIB([m], [log10]) 27 | AC_CHECK_LIB([imobiledevice-1.0], [idevice_new], 28 | [ ], [AC_MSG_FAILURE([*** Unable to link with libimobiledevice])], 29 | [$libimobiledevice_LIBS]) 30 | LT_INIT 31 | 32 | # Defines versions of required modules 33 | libimobiledevice_version=`$PKG_CONFIG --modversion libimobiledevice-1.0` 34 | libplist_version=`$PKG_CONFIG --modversion libplist-2.0` 35 | libusbmuxd_version=`$PKG_CONFIG --modversion libusbmuxd-2.0` 36 | 37 | AC_DEFINE_UNQUOTED([LIBIMOBILEDEVICE_VERSION], ["$libimobiledevice_version"], [ ]) 38 | AC_DEFINE_UNQUOTED([LIBPLIST_VERSION], ["$libplist_version"], [ ]) 39 | AC_DEFINE_UNQUOTED([LIBUSBMUXD_VERSION], ["$libusbmuxd_version"], [ ]) 40 | 41 | [libplist_version_major=`expr "$libplist_version" : '\([0-9]*\)'`] 42 | [libplist_version_minor=`expr "$libplist_version" : '[0-9]*\.\([0-9]*\)'`] 43 | AC_DEFINE_UNQUOTED([LIBPLIST_VERSION_MAJOR], [$libplist_version_major], [ ]) 44 | AC_DEFINE_UNQUOTED([LIBPLIST_VERSION_MINOR], [$libplist_version_minor], [ ]) 45 | 46 | # Checks for header files. 47 | AC_HEADER_STDC 48 | AC_HEADER_RESOLV 49 | AC_CHECK_HEADERS([arpa/inet.h inttypes.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/socket.h sys/time.h]) 50 | 51 | # Checks for typedefs, structures, and compiler characteristics. 52 | AC_TYPE_INT8_T 53 | AC_TYPE_SIZE_T 54 | AC_TYPE_SSIZE_T 55 | AC_TYPE_UINT16_T 56 | AC_TYPE_UINT32_T 57 | AC_TYPE_UINT64_T 58 | AC_TYPE_UINT8_T 59 | 60 | # Checks for library functions. 61 | AC_FUNC_MALLOC 62 | AC_FUNC_REALLOC 63 | 64 | # Check for operating system 65 | AC_MSG_CHECKING([whether to enable WIN32 build settings]) 66 | case ${host_os} in 67 | *mingw*|*msys*|*cygwin*) 68 | win32=true 69 | AC_MSG_RESULT([yes]) 70 | AC_DEFINE(WIN32_LEAN_AND_MEAN, 1, [Define to limit the scope of windows.h]) 71 | AC_DEFINE(__USE_MINGW_ANSI_STDIO, 1, [Define to use C99 printf/snprintf in MinGW]) 72 | ;; 73 | *) 74 | win32=false 75 | AC_MSG_RESULT([no]) 76 | ;; 77 | esac 78 | AM_CONDITIONAL(WIN32, test "x$win32" = "xtrue") 79 | 80 | # Check for pcre presence if regex.h is absent 81 | AC_CHECK_HEADER(regex.h, [ac_have_regex_h="yes"], [ac_have_regex_h="no"]) 82 | if test "x$ac_have_regex_h" = "xno"; then 83 | PKG_CHECK_MODULES(libpcreposix, libpcreposix, [], [AC_MSG_ERROR([Neither regex.h nor pcre headers were found])]) 84 | else 85 | AC_DEFINE(HAVE_REGEX_H, 1, [regex.h is present]) 86 | fi 87 | 88 | AC_CHECK_FUNCS([memmove memset regcomp select socket strcasecmp strncasecmp strchr strdup strndup strrchr strstr strtol strcasestr getline]) 89 | 90 | AC_CONFIG_FILES([Makefile src/Makefile include/Makefile examples/Makefile]) 91 | 92 | CFLAGS="${CFLAGS} -Wall -Werror" 93 | 94 | AC_OUTPUT 95 | -------------------------------------------------------------------------------- /src/rpc.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2014 Google Inc. wrightt@google.com 3 | 4 | // 5 | // WebInspector Remote Procedure Call formatter. 6 | // 7 | 8 | #ifndef RPC_H 9 | #define RPC_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | 20 | typedef uint8_t rpc_status; 21 | #define RPC_ERROR 1 22 | #define RPC_SUCCESS 0 23 | 24 | // Create a UUID, e.g. "4B2550E4-13D6-4902-A48E-B45D5B23215B". 25 | rpc_status rpc_new_uuid(char **to_uuid); 26 | 27 | struct rpc_app_struct { 28 | char *app_id; 29 | char *app_name; 30 | bool is_proxy; 31 | }; 32 | typedef struct rpc_app_struct *rpc_app_t; 33 | rpc_app_t rpc_new_app(); 34 | void rpc_free_app(rpc_app_t app); 35 | rpc_status rpc_copy_app(rpc_app_t app, rpc_app_t *to_app); 36 | 37 | struct rpc_page_struct { 38 | uint32_t page_id; 39 | char *connection_id; 40 | char *title; 41 | char *url; 42 | }; 43 | typedef struct rpc_page_struct *rpc_page_t; 44 | 45 | struct rpc_struct; 46 | typedef struct rpc_struct *rpc_t; 47 | rpc_t rpc_new(); 48 | void rpc_free(rpc_t self); 49 | 50 | // WebInspector Remote Procedure Call formatter. 51 | struct rpc_struct { 52 | 53 | // 54 | // Call these APIs: 55 | // 56 | 57 | // Calls on_*. 58 | rpc_status (*recv_plist)(rpc_t self, const plist_t rpc_dict); 59 | 60 | // Calls send_plist. 61 | rpc_status (*send_reportIdentifier)(rpc_t self, 62 | const char *connection_id); 63 | 64 | rpc_status (*send_getConnectedApplications)(rpc_t self, 65 | const char *connection_id); 66 | 67 | rpc_status (*send_forwardGetListing)(rpc_t self, 68 | const char *connection_id, const char *app_id); 69 | 70 | rpc_status (*send_forwardIndicateWebView)(rpc_t self, 71 | const char *connection_id, const char *app_id, 72 | uint32_t page_id, bool is_enabled); 73 | 74 | rpc_status (*send_forwardSocketSetup)(rpc_t self, 75 | const char *connection_id, const char *app_id, 76 | uint32_t page_id, const char *sender_id); 77 | 78 | rpc_status (*send_forwardSocketData)(rpc_t self, 79 | const char *connection_id, const char *app_id, 80 | uint32_t page_id, const char *sender_id, 81 | const char *data, size_t length); 82 | 83 | rpc_status (*send_forwardDidClose)(rpc_t self, 84 | const char *connection_id, const char *app_id, 85 | uint32_t page_id, const char *sender_id); 86 | 87 | void *state; 88 | 89 | // 90 | // Set these callbacks: 91 | // 92 | 93 | rpc_status (*send_plist)(rpc_t self, plist_t rpc_dict); 94 | 95 | rpc_status (*on_reportSetup)(rpc_t self); 96 | 97 | rpc_status (*on_reportConnectedApplicationList)(rpc_t self, 98 | const rpc_app_t *apps); 99 | 100 | rpc_status (*on_applicationConnected)(rpc_t self, 101 | const rpc_app_t app); 102 | 103 | rpc_status (*on_applicationDisconnected)(rpc_t self, 104 | const rpc_app_t app); 105 | 106 | rpc_status (*on_applicationSentListing)(rpc_t self, 107 | const char *app_id, const rpc_page_t *pages); 108 | 109 | rpc_status (*on_applicationSentData)(rpc_t self, 110 | const char *app_id, const char *dest_id, 111 | const char *data, size_t length); 112 | 113 | rpc_status (*on_applicationUpdated)(rpc_t self, 114 | const char *app_id, const char *dest_id); 115 | 116 | // For internal use only: 117 | rpc_status (*on_error)(rpc_t self, const char *format, ...); 118 | }; 119 | 120 | 121 | #ifdef __cplusplus 122 | } 123 | #endif 124 | 125 | #endif /* RPC_H */ 126 | -------------------------------------------------------------------------------- /include/ios-webkit-debug-proxy/ios_webkit_debug_proxy.h: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // iOS WebKit Remote Debugging Protocol Proxy 6 | // 7 | 8 | #ifndef IOS_WEBKIT_DEBUG_PROXY_H 9 | #define IOS_WEBKIT_DEBUG_PROXY_H 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | #include 16 | #include 17 | 18 | typedef uint8_t iwdp_status; 19 | #define IWDP_ERROR 1 20 | #define IWDP_SUCCESS 0 21 | 22 | 23 | struct iwdp_private; 24 | typedef struct iwdp_private *iwdp_private_t; 25 | 26 | struct iwdp_struct; 27 | typedef struct iwdp_struct *iwdp_t; 28 | iwdp_t iwdp_new(const char* frontend, const char* sim_wi_socket_addr); 29 | void iwdp_free(iwdp_t self); 30 | 31 | struct iwdp_struct { 32 | 33 | // Use these APIs: 34 | 35 | // Start the proxy. 36 | iwdp_status (*start)(iwdp_t self); 37 | 38 | // Accept new client. 39 | // @param s_fd server fd from our add_fd call 40 | // @param s_value value from our add_fd call 41 | // @param fd 42 | // @param to_value will be set, must be passed to subsequent on_recv and 43 | // on_close calls. 44 | iwdp_status (*on_accept)(iwdp_t self, int s_fd, void *s_value, 45 | int fd, void **to_value); 46 | 47 | // Receive bytes from fd. 48 | // @param value the *to_value set by our on_accept or add_fd 49 | iwdp_status (*on_recv)(iwdp_t self, int fd, void *value, 50 | const char *buf, ssize_t length); 51 | 52 | iwdp_status (*on_close)(iwdp_t self, int fd, void *value, 53 | bool is_server); 54 | 55 | void *state; 56 | bool *is_debug; 57 | 58 | 59 | // Provide these callbacks: 60 | 61 | // Subscribe to device add/remove callbacks. 62 | // @result fd, or -1 for error 63 | int (*subscribe)(iwdp_t iwdp); 64 | 65 | // Attach to an iOS device by UUID. 66 | // @param device_id optional 40-character hex UUID, or NULL for any device 67 | // @param to_device_id optional selected device UUID 68 | // @param to_device_name optional selected device name 69 | // @result fd, or -1 for error 70 | int (*attach)(iwdp_t iwdp, const char *device_id, char **to_device_id, 71 | char **to_device_name, int *to_device_os_version, 72 | void **to_ssl_session); 73 | 74 | // Select the port-scan range for the browser listener. 75 | // @param to_port preferred port, e.g. 9227. If a device is re-attached 76 | // then this will be set to the previously-selected port 77 | // @param to_min_port e.g. set to 9222 78 | // @param to_max_port e.g. set to 9322 79 | iwdp_status (*select_port)(iwdp_t self, const char *device_id, int *to_port, 80 | int *to_min_port, int *to_max_port); 81 | 82 | // Bind and listen to a server port. 83 | // @param port e.g. 9222 84 | int (*listen)(iwdp_t self, int port); 85 | 86 | // Connect to a host:port for static data. 87 | // @param hostname_with_port e.g. "chrome-devtools-frontend.appspot.com:8080" 88 | int (*connect)(iwdp_t self, const char *hostname_with_port); 89 | 90 | // Send bytes to fd. 91 | iwdp_status (*send)(iwdp_t self, int fd, const char *data, size_t length); 92 | 93 | // Add a fd that was returned from attach/listen/connect. 94 | iwdp_status (*add_fd)(iwdp_t self, int fd, void *ssl_session, void *value, 95 | bool is_server); 96 | 97 | iwdp_status (*remove_fd)(iwdp_t self, int fd); 98 | 99 | 100 | // For internal use only: 101 | iwdp_status (*on_error)(iwdp_t self, const char *format, ...); 102 | iwdp_private_t private_state; 103 | }; 104 | 105 | #ifdef __cplusplus 106 | } 107 | #endif 108 | 109 | #endif /* IOS_WEBKIT_DEBUG_PROXY_H */ 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/idevice_ext.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #ifdef WIN32 13 | #include 14 | #endif 15 | 16 | #include 17 | 18 | #include "idevice_ext.h" 19 | 20 | typedef struct { 21 | unsigned char *data; 22 | unsigned int size; 23 | } key_data_t; 24 | 25 | int read_pair_record(const char *udid, plist_t *pair_record) { 26 | char* record_data = NULL; 27 | uint32_t record_size = 0; 28 | 29 | int res = usbmuxd_read_pair_record(udid, &record_data, &record_size); 30 | if (res < 0) { 31 | free(record_data); 32 | return -1; 33 | } 34 | 35 | *pair_record = NULL; 36 | #if LIBPLIST_VERSION_MAJOR >= 2 && LIBPLIST_VERSION_MINOR >= 3 37 | plist_from_memory(record_data, record_size, pair_record, NULL); 38 | #else 39 | plist_from_memory(record_data, record_size, pair_record); 40 | #endif 41 | free(record_data); 42 | 43 | if (!*pair_record) { 44 | return -1; 45 | } 46 | 47 | return 0; 48 | } 49 | 50 | int pair_record_get_item_as_key_data(plist_t pair_record, const char* name, key_data_t *value) { 51 | char* buffer = NULL; 52 | uint64_t length = 0; 53 | plist_t node = plist_dict_get_item(pair_record, name); 54 | 55 | if (node && plist_get_node_type(node) == PLIST_DATA) { 56 | plist_get_data_val(node, &buffer, &length); 57 | value->data = (unsigned char*)malloc(length+1); 58 | memcpy(value->data, buffer, length); 59 | value->data[length] = '\0'; 60 | value->size = length+1; 61 | free(buffer); 62 | return 0; 63 | } 64 | 65 | return -1; 66 | } 67 | 68 | int idevice_ext_connection_enable_ssl(const char *device_id, int fd, SSL **to_session) { 69 | plist_t pair_record = NULL; 70 | if (read_pair_record(device_id, &pair_record)) { 71 | fprintf(stderr, "Failed to read pair record\n"); 72 | return -1; 73 | } 74 | 75 | key_data_t root_cert = { NULL, 0 }; 76 | key_data_t root_privkey = { NULL, 0 }; 77 | pair_record_get_item_as_key_data(pair_record, "RootCertificate", &root_cert); 78 | pair_record_get_item_as_key_data(pair_record, "RootPrivateKey", &root_privkey); 79 | plist_free(pair_record); 80 | 81 | BIO *ssl_bio = BIO_new(BIO_s_socket()); 82 | if (!ssl_bio) { 83 | fprintf(stderr, "Could not create SSL bio\n"); 84 | return -1; 85 | } 86 | 87 | BIO_set_fd(ssl_bio, fd, BIO_NOCLOSE); 88 | SSL_CTX *ssl_ctx = SSL_CTX_new(TLS_method()); 89 | if (ssl_ctx == NULL) { 90 | fprintf(stderr, "Could not create SSL context\n"); 91 | BIO_free(ssl_bio); 92 | } 93 | 94 | SSL_CTX_set_security_level(ssl_ctx, 0); 95 | SSL_CTX_set_min_proto_version(ssl_ctx, TLS1_VERSION); 96 | 97 | BIO* membp; 98 | X509* rootCert = NULL; 99 | membp = BIO_new_mem_buf(root_cert.data, root_cert.size); 100 | PEM_read_bio_X509(membp, &rootCert, NULL, NULL); 101 | BIO_free(membp); 102 | SSL_CTX_use_certificate(ssl_ctx, rootCert); 103 | X509_free(rootCert); 104 | free(root_cert.data); 105 | 106 | EVP_PKEY* rootPrivKey = NULL; 107 | membp = BIO_new_mem_buf(root_privkey.data, root_privkey.size); 108 | PEM_read_bio_PrivateKey(membp, &rootPrivKey, NULL, NULL); 109 | BIO_free(membp); 110 | SSL_CTX_use_PrivateKey(ssl_ctx, rootPrivKey); 111 | EVP_PKEY_free(rootPrivKey); 112 | 113 | free(root_privkey.data); 114 | 115 | SSL *ssl = SSL_new(ssl_ctx); 116 | if (!ssl) { 117 | fprintf(stderr, "Could not create SSL object\n"); 118 | BIO_free(ssl_bio); 119 | SSL_CTX_free(ssl_ctx); 120 | return -1; 121 | } 122 | 123 | SSL_set_connect_state(ssl); 124 | SSL_set_verify(ssl, 0, NULL); 125 | SSL_set_bio(ssl, ssl_bio, ssl_bio); 126 | 127 | int ssl_error = 0; 128 | while (1) { 129 | ssl_error = SSL_get_error(ssl, SSL_do_handshake(ssl)); 130 | if (ssl_error == 0 || ssl_error != SSL_ERROR_WANT_READ) { 131 | break; 132 | } 133 | #ifdef WIN32 134 | Sleep(100); 135 | #else 136 | struct timespec ts = { 0, 100000000 }; 137 | nanosleep(&ts, NULL); 138 | #endif 139 | } 140 | 141 | if (ssl_error != 0) { 142 | SSL_free(ssl); 143 | SSL_CTX_free(ssl_ctx); 144 | return ssl_error; 145 | } 146 | 147 | *to_session = ssl; 148 | return 0; 149 | } 150 | -------------------------------------------------------------------------------- /examples/wi_client.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A minimal webinspector client 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #ifdef WIN32 21 | #include 22 | #else 23 | #include 24 | #endif 25 | 26 | #include "ios-webkit-debug-proxy/webinspector.h" 27 | 28 | // our state 29 | struct my_wi_struct { 30 | char *device_id; 31 | int fd; 32 | wi_t wi; 33 | }; 34 | typedef struct my_wi_struct *my_wi_t; 35 | 36 | // 37 | // inspector callbacks: 38 | // 39 | 40 | wi_status send_packet(wi_t wi, const char *packet, size_t length) { 41 | my_wi_t my_wi = (my_wi_t)wi->state; 42 | ssize_t sent_bytes = send(my_wi->fd, (void*)packet, length, 0); 43 | return (sent_bytes == length ? WI_SUCCESS : WI_ERROR); 44 | } 45 | 46 | wi_status recv_plist(wi_t wi, const plist_t rpc_dict) { 47 | char *xml = NULL; 48 | uint32_t length = 0; 49 | plist_to_xml(rpc_dict, &xml, &length); 50 | puts(xml); 51 | free(xml); 52 | return WI_SUCCESS; 53 | } 54 | 55 | // 56 | // Main: 57 | // 58 | 59 | static int quit_flag = 0; 60 | 61 | static void on_signal(int sig) { 62 | fprintf(stderr, "Exiting...\n"); 63 | quit_flag++; 64 | } 65 | 66 | int main(int argc, char **argv) { 67 | // map ctrl-c to quit_flag=1 68 | signal(SIGINT, on_signal); 69 | signal(SIGTERM, on_signal); 70 | 71 | #ifdef WIN32 72 | WSADATA wsa_data; 73 | int res = WSAStartup(MAKEWORD(2,2), &wsa_data); 74 | if (res) { 75 | fprintf(stderr, "WSAStartup failed with error: %d\n", res); 76 | exit(1); 77 | } 78 | #endif 79 | 80 | // parse args 81 | char *device_id = NULL; 82 | bool is_debug = false; 83 | int i = 0; 84 | for (i = 1; i < argc; i++) { 85 | if ((!strcmp(argv[i], "-u") || !strcmp(argv[i], "--udid")) && 86 | i + 1 < argc) { 87 | free(device_id); 88 | device_id = strdup(argv[++i]); 89 | } else if (!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")) { 90 | is_debug = true; 91 | } else { 92 | bool is_help = (!strcmp(argv[i], "h") || !strcmp(argv[i], "--help")); 93 | char *name = strrchr(argv[0], '/'); 94 | printf("Usage: %s OPTIONS\n" 95 | "Minimal iOS webinspector client.\n\n" 96 | " -u, --udid UDID\ttarget device by its 40-digit device UDID\n" 97 | " -d, --debug\t\tenable communication debugging\n", 98 | (name ? name + 1 : argv[0])); 99 | return (is_help ? 0 : 1); 100 | } 101 | } 102 | 103 | // connect to device 104 | char *device_id2 = NULL; 105 | int recv_timeout = 1000; 106 | int fd = wi_connect(device_id, &device_id2, NULL, NULL, NULL, recv_timeout); 107 | if (fd < 0) { 108 | return -1; 109 | } 110 | 111 | // create inspector 112 | my_wi_t my_wi = (my_wi_t)malloc(sizeof(struct my_wi_struct)); 113 | wi_t wi = wi_new(false); 114 | memset(my_wi, 0, sizeof(struct my_wi_struct)); 115 | my_wi->device_id = device_id2; 116 | my_wi->fd = fd; 117 | my_wi->wi = wi; 118 | wi->send_packet = send_packet; 119 | wi->recv_plist = recv_plist; 120 | wi->state = my_wi; 121 | wi->is_debug = &is_debug; 122 | 123 | // send "reportIdentifier" 124 | const char *xml = "" 125 | "\n" 126 | "\n" 127 | " __selector_rpc_reportIdentifier:\n" 128 | " __argument\n" 129 | " WIRConnectionIdentifierKey\n" 130 | " 077BA242-564F-443B-B83A-EFBB337DAE35\n" 131 | ""; 132 | plist_t rpc_dict = NULL; 133 | plist_from_xml(xml, strlen(xml), &rpc_dict); 134 | wi->send_plist(wi, rpc_dict); 135 | plist_free(rpc_dict); 136 | 137 | // read responses until user presses ctrl-c 138 | char buf[1024]; 139 | size_t buf_length = 1024; 140 | while (!quit_flag) { 141 | ssize_t read_bytes = recv(fd, buf, buf_length, 0); 142 | if (read_bytes < 0 && errno == EWOULDBLOCK) { 143 | continue; 144 | } 145 | if (wi->on_recv(wi, buf, read_bytes)) { 146 | break; 147 | } 148 | } 149 | 150 | // cleanup 151 | free(my_wi->device_id); 152 | wi_free(my_wi->wi); 153 | memset(my_wi, 0, sizeof(struct my_wi_struct)); 154 | free(my_wi); 155 | if (fd >= 0) { 156 | #ifdef WIN32 157 | closesocket(fd); 158 | #else 159 | close(fd); 160 | #endif 161 | } 162 | #ifdef WIN32 163 | WSACleanup(); 164 | #endif 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /design.md: -------------------------------------------------------------------------------- 1 | Google BSD license 2 | Copyright 2012 Google Inc. 3 | 4 | 5 | iOS WebKit Debug Proxy Design 6 | ============================= 7 | 8 | See the [README.md](README.md) for an overview. 9 | 10 | Source 11 | ------ 12 | 13 | - [src/ios_webkit_debug_proxy_main.c](src/ios_webkit_debug_proxy_main.c) 14 | \- The "main" 15 | 16 | - [src/ios_webkit_debug_proxy.c](src/ios_webkit_debug_proxy.c) 17 | \- WebInspector to WebKit Remote Debugging Protocol translator 18 | \- See [examples/wdp_client.js](examples/wdp_client.js) and 19 | 20 | - [src/webinspector.c](src/webinspector.c) 21 | \- iOS WebInspector library 22 | \- See [examples/wi_client.c](examples/wi_client.c) 23 | \- See [src/rpc.c](src/rpc.c) parser 24 | 25 | - [src/device_listener.c](src/device_listener.c) 26 | \- iOS device add/remove listener 27 | \- See [examples/dl_client.c](examples/dl_client.c) 28 | 29 | - [src/websocket.c](src/websocket.c) 30 | \- A generic WebSocket library 31 | \- Uses base64.c and sha1.c from [PolarSSL](http://www.polarssl.org) 32 | \- See [examples/ws_echo1.c](examples/ws_echo1.c) and [examples/ws_echo2.c](examples/ws_echo2.c) 33 | 34 | - Utilities: 35 | \- [src/char_buffer.c](src/char_buffer.c) byte buffer 36 | \- [src/hash_table.c](src/hash_table.c) dictionary 37 | \- [src/port_config.c](src/port_config.c) parses device_id:port config files 38 | \- [src/socket_manager.c](src/socket_manager.c) select-based socket controller 39 | 40 | 41 | Architecture 42 | ------------ 43 | 44 | The high-level design is shown below: 45 | 46 | ![Alt overview](overview.png "Overview") 47 | 48 | The various clients are shown below: 49 | 50 | ![Alt clients](clients.png "Clients") 51 | 52 | 53 | The major components of the ios_webkit_debug_proxy are: 54 | 55 | 1. A device_listener that listens for iOS device add/remove events 56 | 1. A (port, webinspector) pair for each device, e.g.: 57 | - [(port 9222 <--> iphoneX's inspector), 58 | - (port 9223 <--> iphoneY's inspector), ...] 59 | 1. Zero or more active WebSocket clients, e.g.: 60 | - [websocketA is connected to :9222/devtools/page/7, ...] 61 | 1. A socket_manager that handles all the socket I/O 62 | 63 | 64 | The code is object-oriented via the use of structs and function pointers. 65 | For example, the device_listener struct defines two "public API" functions: 66 | 67 | dl_status (*start)(dl_t self); 68 | dl_status (*on_recv)(dl_t self, const char *buf, ); 69 | 70 | and three "abstract" callback functions: 71 | 72 | dl_status (*send)(dl_t self, const char *buf, size_t length); 73 | dl_status (*on_attach)(dl_t self, const char *device_id); 74 | dl_status (*on_detach)(dl_t self, const char *device_id); 75 | 76 | plus a field for client use: 77 | 78 | void *state; 79 | 80 | For example, [examples/dl_client.c](examples/dl_client.c) creates a listener and sets the missing callbacks: 81 | 82 | int fd = dl_connect(); 83 | dl_t dl = dl_new(); // sets the "start" and "on_recv" functions 84 | dl->state = fd; // for use by "my_send" 85 | dl->send = my_send; // --> send((int)dl->state, buf, length); 86 | dl->on_attach = my_on_attach; // --> printf("%s", device_id); 87 | dl->on_detach = my_on_detach; // --> ditto 88 | 89 | then does: 90 | 91 | dl->start(); 92 | 93 | Lastly, the client forwards all socket input to the listener's "on_recv" 94 | handler: 95 | 96 | char buf[1024]; 97 | while (1) { 98 | int len = recv(fd, buf, 1024); 99 | if (dl->on_recv(dl, buf, len)) break; 100 | } 101 | 102 | where "on_recv" buffers the input and calls our "my_on_message" when it has a 103 | full message. 104 | 105 | Note that the "on_recv" and "send" functions abstract the I/O from the 106 | interface, which simplifies debugging and unit testing. 107 | 108 | 109 | The detailed design is shown below: 110 | 111 | ![Alt design](design.png "Design") 112 | 113 | Lines in red are controlled by the main "ios_webkit_debug_proxy". For example, although the figure shows a direct red line from the socket_manager's "on_recv" to the ios_webkit_debug_proxy's handler, this is implemented as a callback through ios_webkit_debug_proxy_main's "iwdpm_on_recv(...)". This design isolate the components from one another and simplifies both offline and per-component unit testing. 114 | 115 | 116 | The code is single-threaded and uses non-blocking I/O. Instead of having a thread per socket that does blocking reads, the single socket_manager's non-blocking select forwards data to the "on_recv" function of websocket/webinspector/etc. This improves system scalability and makes it easier to debug and unit test. 117 | 118 | -------------------------------------------------------------------------------- /src/sha1.h: -------------------------------------------------------------------------------- 1 | /** 2 | * \file sha1.h 3 | * 4 | * \brief SHA-1 cryptographic hash function 5 | * 6 | * Copyright (C) 2006-2010, Brainspark B.V. 7 | * 8 | * This file is part of PolarSSL (http://www.polarssl.org) 9 | * Lead Maintainer: Paul Bakker 10 | * 11 | * All rights reserved. 12 | * 13 | * This program is free software; you can redistribute it and/or modify 14 | * it under the terms of the GNU General Public License as published by 15 | * the Free Software Foundation; either version 2 of the License, or 16 | * (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License along 24 | * with this program; if not, write to the Free Software Foundation, Inc., 25 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 26 | */ 27 | #ifndef POLARSSL_SHA1_H 28 | #define POLARSSL_SHA1_H 29 | 30 | #include 31 | 32 | #ifdef _MSC_VER 33 | #include 34 | typedef UINT32 uint32_t; 35 | #else 36 | #include 37 | #endif 38 | 39 | #define POLARSSL_ERR_SHA1_FILE_IO_ERROR -0x0076 /**< Read/write error in file. */ 40 | 41 | /** 42 | * \brief SHA-1 context structure 43 | */ 44 | typedef struct 45 | { 46 | uint32_t total[2]; /*!< number of bytes processed */ 47 | uint32_t state[5]; /*!< intermediate digest state */ 48 | unsigned char buffer[64]; /*!< data block being processed */ 49 | 50 | unsigned char ipad[64]; /*!< HMAC: inner padding */ 51 | unsigned char opad[64]; /*!< HMAC: outer padding */ 52 | } 53 | sha1_context; 54 | 55 | #ifdef __cplusplus 56 | extern "C" { 57 | #endif 58 | 59 | /** 60 | * \brief SHA-1 context setup 61 | * 62 | * \param ctx context to be initialized 63 | */ 64 | void sha1_starts( sha1_context *ctx ); 65 | 66 | /** 67 | * \brief SHA-1 process buffer 68 | * 69 | * \param ctx SHA-1 context 70 | * \param input buffer holding the data 71 | * \param ilen length of the input data 72 | */ 73 | void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); 74 | 75 | /** 76 | * \brief SHA-1 final digest 77 | * 78 | * \param ctx SHA-1 context 79 | * \param output SHA-1 checksum result 80 | */ 81 | void sha1_finish( sha1_context *ctx, unsigned char output[20] ); 82 | 83 | /** 84 | * \brief Output = SHA-1( input buffer ) 85 | * 86 | * \param input buffer holding the data 87 | * \param ilen length of the input data 88 | * \param output SHA-1 checksum result 89 | */ 90 | void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ); 91 | 92 | /** 93 | * \brief Output = SHA-1( file contents ) 94 | * 95 | * \param path input file name 96 | * \param output SHA-1 checksum result 97 | * 98 | * \return 0 if successful, or POLARSSL_ERR_SHA1_FILE_IO_ERROR 99 | */ 100 | int sha1_file( const char *path, unsigned char output[20] ); 101 | 102 | /** 103 | * \brief SHA-1 HMAC context setup 104 | * 105 | * \param ctx HMAC context to be initialized 106 | * \param key HMAC secret key 107 | * \param keylen length of the HMAC key 108 | */ 109 | void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ); 110 | 111 | /** 112 | * \brief SHA-1 HMAC process buffer 113 | * 114 | * \param ctx HMAC context 115 | * \param input buffer holding the data 116 | * \param ilen length of the input data 117 | */ 118 | void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ); 119 | 120 | /** 121 | * \brief SHA-1 HMAC final digest 122 | * 123 | * \param ctx HMAC context 124 | * \param output SHA-1 HMAC checksum result 125 | */ 126 | void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ); 127 | 128 | /** 129 | * \brief SHA-1 HMAC context reset 130 | * 131 | * \param ctx HMAC context to be reset 132 | */ 133 | void sha1_hmac_reset( sha1_context *ctx ); 134 | 135 | /** 136 | * \brief Output = HMAC-SHA-1( hmac key, input buffer ) 137 | * 138 | * \param key HMAC secret key 139 | * \param keylen length of the HMAC key 140 | * \param input buffer holding the data 141 | * \param ilen length of the input data 142 | * \param output HMAC-SHA-1 result 143 | */ 144 | void sha1_hmac( const unsigned char *key, size_t keylen, 145 | const unsigned char *input, size_t ilen, 146 | unsigned char output[20] ); 147 | 148 | /** 149 | * \brief Checkup routine 150 | * 151 | * \return 0 if successful, or 1 if the test failed 152 | */ 153 | int sha1_self_test( int verbose ); 154 | 155 | #ifdef __cplusplus 156 | } 157 | #endif 158 | 159 | #endif /* sha1.h */ 160 | -------------------------------------------------------------------------------- /examples/ws_echo_common.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A minimal websocket "echo" server 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef WIN32 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | #include "ws_echo_common.h" 25 | 26 | // websocket callbacks: 27 | 28 | ws_status send_data(ws_t ws, const char *data, size_t length) { 29 | int fd = ((my_t)ws->state)->fd; 30 | ssize_t sent_bytes = send(fd, (void*)data, length, 0); 31 | return (sent_bytes == length ? WS_SUCCESS : WS_ERROR); 32 | } 33 | 34 | char *create_root_response(int port, int count) { 35 | char *html = NULL; 36 | if (asprintf(&html, 37 | "
\n" 62 | " Run WebSocket\n" 63 | "
\n", port, count) < 0) { 64 | return NULL; // asprintf failed 65 | } 66 | char *ret = NULL; 67 | if (asprintf(&ret, 68 | "HTTP/1.1 200 OK\r\n" 69 | "Content-length: %zd\r\n" 70 | "Connection: close\r\n" 71 | "Content-Type: text/html; charset=UTF-8\r\n" 72 | "\r\n%s", 73 | (html ? strlen(html) : 0), html) < 0) { 74 | return NULL; // asprintf failed 75 | } 76 | free(html); 77 | return ret; 78 | } 79 | 80 | ws_status on_http_request(ws_t ws, 81 | const char *method, const char *resource, const char *version, 82 | const char *host, const char *headers, size_t headers_length, 83 | bool is_websocket, bool *to_keep_alive) { 84 | if (strcmp(method, "GET") || strcmp(resource, "/")) { 85 | return WS_ERROR; 86 | } 87 | if (!is_websocket) { 88 | char *data = create_root_response(((my_t)ws->state)->port, 3); 89 | ws_status ret = ws->send_data(ws, data, strlen(data)); 90 | free(data); 91 | return ret; 92 | } 93 | return WS_SUCCESS; 94 | } 95 | 96 | ws_status on_upgrade(ws_t ws, 97 | const char *resource, const char *protocol, 98 | int version, const char *sec_key) { 99 | return ws->send_upgrade(ws); 100 | } 101 | 102 | ws_status on_frame(ws_t ws, 103 | bool is_fin, uint8_t opcode, bool is_masking, 104 | const char *payload_data, size_t payload_length, 105 | bool *to_keep) { 106 | switch (opcode) { 107 | case OPCODE_TEXT: 108 | case OPCODE_BINARY: 109 | if (!is_fin) { 110 | // wait for full data 111 | *to_keep = true; 112 | return WS_SUCCESS; 113 | } 114 | if (!is_masking) { 115 | return ws->send_close(ws, CLOSE_PROTOCOL_ERROR, 116 | "Clients must mask"); 117 | } 118 | // echo 119 | return ws->send_frame(ws, 120 | true, opcode, false, 121 | payload_data, payload_length); 122 | 123 | case OPCODE_CLOSE: 124 | // ack close 125 | return ws->send_close(ws, CLOSE_NORMAL, NULL); 126 | 127 | case OPCODE_PING: 128 | // ack ping 129 | return ws->send_frame(ws, 130 | true, OPCODE_PONG, false, 131 | payload_data, payload_length); 132 | 133 | case OPCODE_PONG: 134 | return WS_SUCCESS; 135 | 136 | default: 137 | return WS_ERROR; 138 | } 139 | } 140 | 141 | // struct: 142 | 143 | my_t my_new(int fd, int port) { 144 | my_t my = (my_t)malloc(sizeof(struct my_struct)); 145 | ws_t ws = ws_new(); 146 | if (!ws || !my) { 147 | free(ws); 148 | return NULL; 149 | } 150 | memset(my, 0, sizeof(struct my_struct)); 151 | my->fd = fd; 152 | my->port = port; 153 | my->ws = ws; 154 | ws->send_data = send_data; 155 | ws->on_http_request = on_http_request; 156 | ws->on_upgrade = on_upgrade; 157 | ws->on_frame = on_frame; 158 | ws->state = my; 159 | return my; 160 | } 161 | 162 | void my_free(my_t my) { 163 | if (my) { 164 | ws_free(my->ws); 165 | memset(my, 0, sizeof(struct my_struct)); 166 | free(my); 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /src/hash_table.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // A basic hash table, could be easily enhanced... 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #include 13 | #include 14 | 15 | #include "hash_table.h" 16 | 17 | // constant for now, but we could easily resize & rehash 18 | #define NUM_BUCKETS 3 19 | 20 | struct ht_entry_struct { 21 | intptr_t hc; 22 | void *key; 23 | void *value; 24 | ht_entry_t next; 25 | }; 26 | 27 | 28 | intptr_t on_strhash(ht_t ht, const void *key) { 29 | int hc = 0; 30 | char *s = (char *)key; 31 | if (s) { 32 | int ch; 33 | while ((ch = *s++)) { 34 | hc = ((hc << 5) + hc) ^ ch; 35 | } 36 | } 37 | return hc; 38 | } 39 | intptr_t on_strcmp(ht_t ht, const void *key1, const void *key2) { 40 | if (key1 == key2 || !key1 || !key2) { 41 | return (key1 == key2 ? 0 : key1 ? -1 : 1); 42 | } 43 | return strcmp(key1, key2); 44 | } 45 | 46 | void ht_clear(ht_t self) { 47 | size_t i; 48 | for (i = 0; i < self->num_buckets; i++) { 49 | ht_entry_t curr = self->buckets[i]; 50 | while (curr) { 51 | ht_entry_t next = curr->next; 52 | memset(curr, 0, sizeof(struct ht_entry_struct)); 53 | free(curr); 54 | self->num_keys--; 55 | curr = next; 56 | } 57 | self->buckets[i] = NULL; 58 | } 59 | } 60 | 61 | void ht_free(ht_t self) { 62 | if (self) { 63 | ht_clear(self); 64 | free(self->buckets); 65 | memset(self, 0, sizeof(struct ht_struct)); 66 | free(self); 67 | } 68 | } 69 | 70 | ht_t ht_new(enum ht_key_type type) { 71 | ht_t self = (ht_t)malloc(sizeof(struct ht_struct)); 72 | if (self) { 73 | memset(self, 0, sizeof(struct ht_struct)); 74 | self->num_buckets = NUM_BUCKETS; 75 | self->buckets = (ht_entry_t *)calloc(self->num_buckets, 76 | sizeof(ht_entry_t)); 77 | if (type == HT_STRING_KEYS) { 78 | self->on_hash = on_strhash; 79 | self->on_cmp = on_strcmp; 80 | } 81 | } 82 | return self; 83 | } 84 | 85 | size_t ht_size(ht_t self) { 86 | return self->num_keys; 87 | } 88 | 89 | void ht_find(ht_t self, const void *key, intptr_t *to_hc, 90 | ht_entry_t **to_head, ht_entry_t *to_prev, ht_entry_t *to_curr) { 91 | intptr_t hc = (self->on_hash ? self->on_hash(self, key) : (intptr_t)key); 92 | ht_entry_t *head = self->buckets + (hc % self->num_buckets); 93 | ht_entry_t prev = NULL; 94 | ht_entry_t curr = *head; 95 | for (; curr && !(curr->hc == hc && 96 | (self->on_cmp ? !self->on_cmp(self, curr->key, key) : 97 | curr->key == key)); 98 | prev = curr, curr = curr->next) { 99 | } 100 | *to_head = head; 101 | *to_prev = prev; 102 | *to_curr = curr; 103 | if (to_hc) { 104 | *to_hc = hc; 105 | } 106 | // Instead of setting a "prev", we could set a "pointer-to-current": 107 | // pp = head if no prev else &prev->next 108 | // which would (e.g.) simplify our caller's removal code from: 109 | // if (prev) prev->next = curr->next; else *head = curr->next; 110 | // to: 111 | // *pp = curr->next; 112 | // but I think the explicit prev is easier to understand. 113 | } 114 | 115 | void *ht_get(ht_t self, const void *key, int want_key) { 116 | ht_entry_t *head; 117 | ht_entry_t prev; 118 | ht_entry_t curr; 119 | ht_find(self, key, NULL, &head, &prev, &curr); 120 | if (!curr) { 121 | return NULL; 122 | } 123 | if (prev) { 124 | // optional move-to-front 125 | prev->next = curr->next; 126 | curr->next = *head; 127 | *head = curr; 128 | } 129 | return (want_key ? curr->key : curr->value); 130 | } 131 | void *ht_get_key(ht_t self, const void *key) { 132 | return ht_get(self, key, 1); 133 | } 134 | void *ht_get_value(ht_t self, const void *key) { 135 | return ht_get(self, key, 0); 136 | } 137 | 138 | void *ht_remove(ht_t self, const void *key) { 139 | ht_entry_t *head; 140 | ht_entry_t prev; 141 | ht_entry_t curr; 142 | ht_find(self, key, NULL, &head, &prev, &curr); 143 | void *ret = (curr ? curr->value : NULL); 144 | if (curr) { 145 | if (prev) { 146 | prev->next = curr->next; 147 | } else { 148 | *head = curr->next; 149 | } 150 | free(curr); 151 | self->num_keys--; 152 | } 153 | return ret; 154 | } 155 | 156 | void *ht_put(ht_t self, void *key, void *value) { 157 | ht_entry_t *head; 158 | ht_entry_t prev; 159 | ht_entry_t curr; 160 | intptr_t hc; 161 | ht_find(self, key, &hc, &head, &prev, &curr); 162 | void *ret = (curr ? curr->value : NULL); 163 | if (curr) { 164 | if (value) { 165 | curr->value = value; 166 | } else { 167 | if (prev) { 168 | prev->next = curr->next; 169 | } else { 170 | *head = curr->next; 171 | } 172 | free(curr); 173 | self->num_keys--; 174 | } 175 | } else if (value) { 176 | curr = (ht_entry_t)malloc(sizeof(struct ht_entry_struct)); 177 | // if (!curr) ? 178 | memset(curr, 0, sizeof(struct ht_entry_struct)); 179 | curr->hc = hc; 180 | curr->key = key; 181 | curr->value = value; 182 | curr->next = *head; 183 | *head = curr; 184 | self->num_keys++; 185 | } 186 | return ret; 187 | } 188 | 189 | void **ht_get_all(ht_t self, int want_key) { 190 | void **ret = (void **)calloc(self->num_keys+1, sizeof(void *)); 191 | if (ret) { 192 | void **tail = ret; 193 | size_t i; 194 | for (i = 0; i < self->num_buckets; i++) { 195 | ht_entry_t curr; 196 | for (curr = self->buckets[i]; curr; curr = curr->next) { 197 | *tail++ = (want_key ? curr->key : curr->value); 198 | } 199 | } 200 | } 201 | return ret; 202 | } 203 | void **ht_keys(ht_t self) { 204 | return ht_get_all(self, 1); 205 | } 206 | void **ht_values(ht_t self) { 207 | return ht_get_all(self, 0); 208 | } 209 | -------------------------------------------------------------------------------- /src/port_config.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #ifndef HAVE_REGEX_H 13 | #include 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | #include "port_config.h" 20 | #include "strndup.h" 21 | #include "getline.h" 22 | 23 | 24 | struct pc_entry_struct; 25 | typedef struct pc_entry_struct *pc_entry_t; 26 | 27 | struct pc_entry_struct { 28 | const char *device_id; 29 | int min_port; 30 | int max_port; 31 | 32 | // we need a list of these, so put the link here 33 | pc_entry_t next; 34 | }; 35 | 36 | struct pc_struct { 37 | regex_t *re; 38 | regmatch_t *groups; 39 | pc_entry_t head; 40 | pc_entry_t tail; 41 | }; 42 | 43 | pc_t pc_new() { 44 | pc_t self = malloc(sizeof(struct pc_struct)); 45 | if (self) { 46 | memset(self, 0, sizeof(struct pc_struct)); 47 | } 48 | return self; 49 | } 50 | 51 | void pc_clear(pc_t self) { 52 | if (self) { 53 | pc_entry_t e = self->head; 54 | while (e) { 55 | pc_entry_t next = e->next; 56 | memset(e, 0, sizeof(struct pc_entry_struct)); 57 | free(e); 58 | e = next; 59 | } 60 | self->head = NULL; 61 | self->tail = NULL; 62 | } 63 | } 64 | 65 | void pc_free(pc_t self) { 66 | if (self) { 67 | pc_clear(self); 68 | free(self->groups); 69 | if (self->re) { 70 | regfree(self->re); 71 | } 72 | memset(self, 0, sizeof(struct pc_struct)); 73 | free(self); 74 | } 75 | } 76 | 77 | void pc_add(pc_t self, const char *device_id, int min_port, int max_port) { 78 | pc_entry_t e = malloc(sizeof(struct pc_entry_struct)); 79 | e->device_id = device_id; 80 | e->min_port = min_port; 81 | e->max_port = max_port; 82 | e->next = NULL; 83 | if (self->tail) { 84 | self->tail->next = e; 85 | } else { 86 | self->head = e; 87 | } 88 | self->tail = e; 89 | } 90 | 91 | int pc_parse(pc_t self, const char *line, size_t len, 92 | char **to_device_id, int *to_min_port, int *to_max_port) { 93 | if (!self->re) { 94 | self->re = malloc(sizeof(regex_t)); 95 | if (regcomp(self->re, 96 | "^[ \t]*" 97 | "(([a-fA-F0-9-]{25,}|\\*|null)[ \t]*:?|:)" 98 | "[ \t]*(-?[0-9]+)" 99 | "([ \t]*-[ \t]*([0-9]+))?" 100 | "[ \t]*$", REG_EXTENDED | REG_ICASE)) { 101 | perror("Internal error: bad regex?"); 102 | return -1; 103 | } 104 | size_t ngroups = self->re->re_nsub + 1; 105 | self->groups = calloc(ngroups, sizeof(regmatch_t)); 106 | } 107 | size_t ngroups = self->re->re_nsub + 1; 108 | regmatch_t *groups = self->groups; 109 | char *line2 = calloc(len+1, sizeof(char)); 110 | memcpy(line2, line, len); 111 | int is_not_match = regexec(self->re, line2, ngroups, groups, 0); 112 | free(line2); 113 | if (is_not_match) { 114 | return -1; 115 | } 116 | char *device_id; 117 | if (groups[2].rm_so >= 0) { 118 | size_t len = groups[2].rm_eo - groups[2].rm_so; 119 | if (strncasecmp("null", line + groups[2].rm_so, len)) { 120 | device_id = strndup(line + groups[2].rm_so, len); 121 | } else { 122 | device_id = NULL; 123 | } 124 | } else { 125 | device_id = strdup("*"); 126 | } 127 | int min_port = strtol(line + groups[3].rm_so, NULL, 0); 128 | int max_port = min_port; 129 | if (groups[4].rm_so >= 0 && groups[5].rm_so >= 0) { 130 | max_port = strtol(line + groups[5].rm_so, NULL, 0); 131 | } 132 | *to_device_id = device_id; 133 | *to_min_port = min_port; 134 | *to_max_port = max_port; 135 | return 0; 136 | } 137 | 138 | const char *pc_add_line(pc_t self, const char *line, size_t len) { 139 | const char *curr = line; 140 | const char *stop = line + len; 141 | while (curr < stop) { 142 | while (curr < stop && (*curr == ' ' || *curr == '\t')) { 143 | curr++; 144 | } 145 | const char *end = curr; 146 | while (end < stop && 147 | *end && *end != '\n' && *end != '#' && *end != ',') { 148 | end++; 149 | } 150 | if (curr < end) { 151 | char *device_id; 152 | int min_port; 153 | int max_port; 154 | if (pc_parse(self, curr, end - curr, 155 | &device_id, &min_port, &max_port)) { 156 | return curr; 157 | } 158 | pc_add(self, device_id, min_port, max_port); 159 | } 160 | if (*end != ',') break; 161 | curr = end+1; 162 | } 163 | return NULL; 164 | } 165 | 166 | int pc_add_file(pc_t self, const char *filename) { 167 | FILE *f = fopen(filename, "rt"); 168 | if (!f) { 169 | fprintf(stderr, "Unknown file: %s\n", filename); 170 | return -1; 171 | } 172 | 173 | int ret = 0; 174 | int line_num; 175 | char *line = NULL; 176 | size_t line_capacity = 0; 177 | for (line_num = 0; ; line_num++) { 178 | ssize_t len = getline(&line, &line_capacity, f); 179 | if (len < 0) break; 180 | const char *error = pc_add_line(self, line, len); 181 | if (error) { 182 | ret = -1; 183 | fprintf(stderr, "Ignoring %s:%d: %.*s", filename, line_num, 184 | (int)(error-line), error); 185 | } 186 | } 187 | free(line); 188 | fclose(f); 189 | return ret; 190 | } 191 | 192 | const pc_entry_t pc_find(pc_t self, const char *device_id) { 193 | pc_entry_t e; 194 | for (e = self->head; e; e = e->next) { 195 | const char *s = e->device_id; 196 | if ((s && !strcmp(s, "*")) || 197 | (device_id ? (s && !strcasecmp(s, device_id)) : !s)) { 198 | return e; 199 | } 200 | } 201 | return NULL; 202 | } 203 | 204 | int pc_select_port(pc_t self, const char *device_id, 205 | int *to_port, int *to_min_port, int *to_max_port) { 206 | const pc_entry_t config = pc_find(self, device_id); 207 | if (!config) { 208 | *to_min_port = -1; 209 | *to_max_port = -1; 210 | *to_port = -1; 211 | return -1; 212 | } 213 | *to_min_port = config->min_port; 214 | *to_max_port = config->max_port; 215 | if (*to_port >= 0 && 216 | (*to_port < *to_min_port || *to_port > *to_max_port)) { 217 | *to_port = -1; 218 | } 219 | return 0; 220 | } 221 | 222 | -------------------------------------------------------------------------------- /src/base64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * RFC 1521 base64 encoding/decoding 3 | * 4 | * Copyright (C) 2006-2010, Brainspark B.V. 5 | * 6 | * This file is part of PolarSSL (http://www.polarssl.org) 7 | * Lead Maintainer: Paul Bakker 8 | * 9 | * All rights reserved. 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License along 22 | * with this program; if not, write to the Free Software Foundation, Inc., 23 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 24 | */ 25 | 26 | // BEGIN CHANGES off of revision=1398 11/01/2012 27 | //#include "polarssl/config.h" 28 | #define POLARSSL_BASE64_C 29 | 30 | #if defined(POLARSSL_BASE64_C) 31 | 32 | #ifdef HAVE_CONFIG_H 33 | #include 34 | #endif 35 | 36 | //#include "polarssl/base64.h" 37 | #include "base64.h" 38 | #include "stdio.h" 39 | // END CHANGES 40 | 41 | #ifdef _MSC_VER 42 | #include 43 | typedef UINT32 uint32_t; 44 | #else 45 | #include 46 | #endif 47 | 48 | static const unsigned char base64_enc_map[64] = 49 | { 50 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 51 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 52 | 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 53 | 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 54 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 55 | 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', 56 | '8', '9', '+', '/' 57 | }; 58 | 59 | static const unsigned char base64_dec_map[128] = 60 | { 61 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 63 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 64 | 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 65 | 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, 66 | 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 67 | 127, 64, 127, 127, 127, 0, 1, 2, 3, 4, 68 | 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 69 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 70 | 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 71 | 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 72 | 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 73 | 49, 50, 51, 127, 127, 127, 127, 127 74 | }; 75 | 76 | /* 77 | * Encode a buffer into base64 format 78 | */ 79 | int base64_encode( unsigned char *dst, size_t *dlen, 80 | const unsigned char *src, size_t slen ) 81 | { 82 | size_t i, n; 83 | int C1, C2, C3; 84 | unsigned char *p; 85 | 86 | if( slen == 0 ) 87 | return( 0 ); 88 | 89 | n = (slen << 3) / 6; 90 | 91 | switch( (slen << 3) - (n * 6) ) 92 | { 93 | case 2: n += 3; break; 94 | case 4: n += 2; break; 95 | default: break; 96 | } 97 | 98 | if( *dlen < n + 1 ) 99 | { 100 | *dlen = n + 1; 101 | return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); 102 | } 103 | 104 | n = (slen / 3) * 3; 105 | 106 | for( i = 0, p = dst; i < n; i += 3 ) 107 | { 108 | C1 = *src++; 109 | C2 = *src++; 110 | C3 = *src++; 111 | 112 | *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; 113 | *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; 114 | *p++ = base64_enc_map[(((C2 & 15) << 2) + (C3 >> 6)) & 0x3F]; 115 | *p++ = base64_enc_map[C3 & 0x3F]; 116 | } 117 | 118 | if( i < slen ) 119 | { 120 | C1 = *src++; 121 | C2 = ((i + 1) < slen) ? *src++ : 0; 122 | 123 | *p++ = base64_enc_map[(C1 >> 2) & 0x3F]; 124 | *p++ = base64_enc_map[(((C1 & 3) << 4) + (C2 >> 4)) & 0x3F]; 125 | 126 | if( (i + 1) < slen ) 127 | *p++ = base64_enc_map[((C2 & 15) << 2) & 0x3F]; 128 | else *p++ = '='; 129 | 130 | *p++ = '='; 131 | } 132 | 133 | *dlen = p - dst; 134 | *p = 0; 135 | 136 | return( 0 ); 137 | } 138 | 139 | /* 140 | * Decode a base64-formatted buffer 141 | */ 142 | int base64_decode( unsigned char *dst, size_t *dlen, 143 | const unsigned char *src, size_t slen ) 144 | { 145 | size_t i, n; 146 | uint32_t j, x; 147 | unsigned char *p; 148 | 149 | for( i = j = n = 0; i < slen; i++ ) 150 | { 151 | if( ( slen - i ) >= 2 && 152 | src[i] == '\r' && src[i + 1] == '\n' ) 153 | continue; 154 | 155 | if( src[i] == '\n' ) 156 | continue; 157 | 158 | if( src[i] == '=' && ++j > 2 ) 159 | return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); 160 | 161 | if( src[i] > 127 || base64_dec_map[src[i]] == 127 ) 162 | return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); 163 | 164 | if( base64_dec_map[src[i]] < 64 && j != 0 ) 165 | return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); 166 | 167 | n++; 168 | } 169 | 170 | if( n == 0 ) 171 | return( 0 ); 172 | 173 | n = ((n * 6) + 7) >> 3; 174 | 175 | if( *dlen < n ) 176 | { 177 | *dlen = n; 178 | return( POLARSSL_ERR_BASE64_BUFFER_TOO_SMALL ); 179 | } 180 | 181 | for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) 182 | { 183 | if( *src == '\r' || *src == '\n' ) 184 | continue; 185 | 186 | j -= ( base64_dec_map[*src] == 64 ); 187 | x = (x << 6) | ( base64_dec_map[*src] & 0x3F ); 188 | 189 | if( ++n == 4 ) 190 | { 191 | n = 0; 192 | if( j > 0 ) *p++ = (unsigned char)( x >> 16 ); 193 | if( j > 1 ) *p++ = (unsigned char)( x >> 8 ); 194 | if( j > 2 ) *p++ = (unsigned char)( x ); 195 | } 196 | } 197 | 198 | *dlen = p - dst; 199 | 200 | return( 0 ); 201 | } 202 | 203 | #if defined(POLARSSL_SELF_TEST) 204 | 205 | #include 206 | #include 207 | 208 | static const unsigned char base64_test_dec[64] = 209 | { 210 | 0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD, 211 | 0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01, 212 | 0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09, 213 | 0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13, 214 | 0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31, 215 | 0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38, 216 | 0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B, 217 | 0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97 218 | }; 219 | 220 | static const unsigned char base64_test_enc[] = 221 | "JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK" 222 | "swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw=="; 223 | 224 | /* 225 | * Checkup routine 226 | */ 227 | int base64_self_test( int verbose ) 228 | { 229 | size_t len; 230 | unsigned char *src, buffer[128]; 231 | 232 | if( verbose != 0 ) 233 | printf( " Base64 encoding test: " ); 234 | 235 | len = sizeof( buffer ); 236 | src = (unsigned char *) base64_test_dec; 237 | 238 | if( base64_encode( buffer, &len, src, 64 ) != 0 || 239 | memcmp( base64_test_enc, buffer, 88 ) != 0 ) 240 | { 241 | if( verbose != 0 ) 242 | printf( "failed\n" ); 243 | 244 | return( 1 ); 245 | } 246 | 247 | if( verbose != 0 ) 248 | printf( "passed\n Base64 decoding test: " ); 249 | 250 | len = sizeof( buffer ); 251 | src = (unsigned char *) base64_test_enc; 252 | 253 | if( base64_decode( buffer, &len, src, 88 ) != 0 || 254 | memcmp( base64_test_dec, buffer, 64 ) != 0 ) 255 | { 256 | if( verbose != 0 ) 257 | printf( "failed\n" ); 258 | 259 | return( 1 ); 260 | } 261 | 262 | if( verbose != 0 ) 263 | printf( "passed\n\n" ); 264 | 265 | return( 0 ); 266 | } 267 | 268 | #endif 269 | 270 | #endif 271 | -------------------------------------------------------------------------------- /src/char_buffer.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "char_buffer.h" 14 | 15 | #define MIN_LENGTH 1024 16 | 17 | cb_t cb_new() { 18 | cb_t self = (cb_t)malloc(sizeof(struct cb_struct)); 19 | if (self) { 20 | memset(self, 0, sizeof(struct cb_struct)); 21 | } 22 | return self; 23 | } 24 | 25 | void cb_free(cb_t self) { 26 | if (self) { 27 | if (self->begin) { 28 | free(self->begin); 29 | } 30 | free(self); 31 | } 32 | } 33 | 34 | void cb_clear(cb_t self) { 35 | self->head = self->begin; 36 | self->tail = self->begin; 37 | } 38 | 39 | int cb_ensure_capacity(cb_t self, size_t needed) { 40 | if (!self->begin) { 41 | size_t length = (needed > MIN_LENGTH ? needed : MIN_LENGTH); 42 | self->begin = (char *)malloc(length * sizeof(char)); 43 | if (!self->begin) { 44 | perror("Unable to allocate buffer"); 45 | return -1; 46 | } 47 | self->head = self->begin; 48 | self->tail = self->begin; 49 | self->end = self->begin + length; 50 | return 0; 51 | } 52 | size_t used = self->tail - self->head; 53 | if (!used) { 54 | self->head = self->begin; 55 | self->tail = self->begin; 56 | } 57 | size_t avail = self->end - self->tail; 58 | if (needed > avail) { 59 | size_t offset = self->head - self->begin; 60 | if (offset) { 61 | if (used) { 62 | memmove(self->begin, self->head, used); 63 | } 64 | self->head = self->begin; 65 | self->tail = self->begin + used; 66 | avail += offset; 67 | } 68 | if (needed > avail) { 69 | size_t length = self->end - self->begin; 70 | size_t new_length = used + needed; 71 | if (new_length < 1.5 * length) { 72 | new_length = 1.5 * length; 73 | } 74 | char *new_begin = (char*)realloc(self->begin, 75 | new_length * sizeof(char)); 76 | if (!new_begin) { 77 | perror("Unable to resize buffer"); 78 | return -1; 79 | } 80 | self->begin = new_begin; 81 | self->head = new_begin; 82 | self->tail = new_begin + used; 83 | self->end = new_begin + new_length; 84 | } 85 | } 86 | return 0; 87 | } 88 | 89 | int cb_begin_input(cb_t self, const char *buf, ssize_t length) { 90 | if (!buf || length < 0) { 91 | return -1; 92 | } 93 | // Instead of always doing a memcpy into our buffer, see if we can 94 | // use the buf as-is 95 | int can_share = (!self->begin || self->tail == self->head); 96 | if (can_share) { 97 | self->in_head = buf; 98 | self->in_tail = buf + length; 99 | } else { 100 | if (cb_ensure_capacity(self, length)) { 101 | return -1; 102 | } 103 | if (length > 0) { 104 | memcpy(self->tail, buf, length); 105 | self->tail += length; 106 | } 107 | self->in_head = self->head; 108 | self->in_tail = self->tail; 109 | } 110 | return 0; 111 | } 112 | 113 | int cb_end_input(cb_t self) { 114 | int did_share = (!self->begin || self->in_tail != self->tail); 115 | if (did_share) { 116 | size_t length = self->in_tail - self->in_head; 117 | if (length > 0) { 118 | // We used the input buf as-is, but some bytes remain, so save them 119 | if (cb_ensure_capacity(self, length)) { 120 | return -1; 121 | } 122 | memcpy(self->tail, self->in_head, length); 123 | self->tail += length; 124 | } 125 | } else { 126 | self->head += self->in_head - self->head; 127 | } 128 | self->in_head = NULL; 129 | self->in_tail = NULL; 130 | return 0; 131 | } 132 | 133 | // similar to socat output, e.g.: 134 | // 47 45 54 20 2F 64 65 76 74 6F 6F 6C 73 2F 49 6D 61 67 65 GET /devtools/Image 135 | // ... 136 | size_t cb_sprint(char *to_buf, const char *buf, ssize_t length, 137 | ssize_t max_width, ssize_t max_lines) { 138 | if (length <= 0) { 139 | if (to_buf) { 140 | *to_buf = '\0'; 141 | } 142 | return 0; 143 | } 144 | 145 | char *s = to_buf; 146 | size_t n = 0; 147 | 148 | #define APPEND(v) if (s) { *s++ = (v); } n++ 149 | 150 | size_t i = 0; 151 | size_t num_lines = 0; 152 | 153 | size_t chars_per_line; 154 | if (max_width >= 0) { 155 | chars_per_line = (max_width > 6 ? ((max_width - 2) >> 2) : 1); 156 | } else { 157 | size_t max_cpl = 1; 158 | size_t curr_cpl = 0; 159 | for (i = 0; i < length; i++) { 160 | unsigned char ch = buf[i++]; 161 | curr_cpl++; 162 | if (ch == '\n') { 163 | if (curr_cpl > max_cpl) { 164 | max_cpl = curr_cpl; 165 | } 166 | if (max_lines >= 0 && ++num_lines > max_lines) { 167 | break; 168 | } 169 | curr_cpl = 0; 170 | } 171 | } 172 | chars_per_line = max_cpl; 173 | } 174 | 175 | i = 0; 176 | num_lines = 0; 177 | while (1) { 178 | size_t j; 179 | size_t rem = chars_per_line; 180 | for (j = i; j < length && rem; ) { 181 | unsigned char ch = buf[j++]; 182 | static const char* hexchars = "0123456789ABCDEF"; 183 | APPEND(' '); 184 | APPEND(hexchars[(ch >> 4) & 0xF]); 185 | APPEND(hexchars[ch & 0xF]); 186 | rem--; 187 | if (ch == '\n') { 188 | break; 189 | } 190 | } 191 | size_t rem2 = rem; 192 | for (rem2 = rem; rem2 > 0; rem2--) { 193 | APPEND(' '); 194 | APPEND(' '); 195 | APPEND(' '); 196 | } 197 | APPEND(' '); 198 | APPEND(' '); 199 | while (i < j) { 200 | unsigned char ch = buf[i++]; 201 | APPEND(ch < 32 || ch > 126 ? '.' : ch); 202 | } 203 | if (i >= length) { 204 | break; 205 | } 206 | if (max_lines >= 0 && ++num_lines > max_lines) { 207 | for (rem2 = rem; rem2 > 0; rem2--) { 208 | APPEND(' '); 209 | } 210 | APPEND(' '); 211 | APPEND('+'); 212 | if (s) { 213 | size_t k = sprintf(s, "%zd", length - i); 214 | s += k; 215 | n += k; 216 | } else { 217 | n += (int)(log10(length - i) + 0.5) + 1; 218 | } 219 | break; 220 | } 221 | APPEND('\n'); 222 | } 223 | if (s) { 224 | *s++ = '\0'; 225 | } 226 | return n; 227 | } 228 | 229 | int cb_asprint(char **ret, const char *buf, ssize_t length, 230 | ssize_t max_width, ssize_t max_lines) { 231 | size_t n = cb_sprint(NULL, buf, length, max_width, max_lines); 232 | *ret = (char *)malloc((n+1) * sizeof(char)); 233 | if (!*ret) { 234 | return -1; 235 | } 236 | return cb_sprint(*ret, buf, length, max_width, max_lines); 237 | } 238 | 239 | int hex2int(char c) { 240 | if (c >= '0' && c <= '9') 241 | return c - '0'; 242 | else if (c >= 'a' && c <= 'f') 243 | return 10 + c - 'a'; 244 | else if (c >= 'A' && c <= 'F') 245 | return 10 + c - 'A'; 246 | else 247 | return -1; 248 | } 249 | 250 | int cb_sscan(char *to_buf, size_t *to_length, const char *buf) { 251 | if (!to_buf || !to_length || !buf) { 252 | return -1; 253 | } 254 | *to_length = 0; 255 | const char *f = buf; 256 | char *t = to_buf; 257 | while (*f) { 258 | for (; *f == ' '; f++) { 259 | } 260 | while (*f != ' ' && *f != '\n') { 261 | int h0 = (*f ? hex2int(*f++) : -1); 262 | int h1 = (*f ? hex2int(*f++) : -1); 263 | char ch = (*f ? *f++ : '\0'); 264 | if (h0 < 0 || h1 < 0 || ch != ' ') { 265 | return -1; 266 | } 267 | *t++ = (h0 << 4) | h1; 268 | *to_length += 1; 269 | } 270 | if (*f == ' ') { 271 | while (*++f && *f != '\n') { 272 | } 273 | } 274 | if (*f && *f++ != '\n') { 275 | return -1; 276 | } 277 | } 278 | return 0; 279 | } 280 | 281 | int cb_asscan(char **ret, size_t *to_length, const char *buf) { 282 | if (!ret || !*ret || !to_length || !buf) { 283 | return -1; 284 | } 285 | *ret = (char *)calloc(strlen(buf) + 1, sizeof(char)); 286 | int rval = cb_sscan(*ret, to_length, buf); 287 | if (*ret && to_length) { 288 | *ret = (char*)realloc(*ret, *to_length * sizeof(char)); 289 | } 290 | return rval; 291 | } 292 | 293 | #ifndef __MACH__ 294 | char *strnstr(const char *s1, const char *s2, size_t n) { 295 | size_t len = strlen(s2); 296 | if (n >= len) { 297 | char c = *s2; 298 | const char *end = s1 + (n - len); 299 | const char *s; 300 | for (s = s1; *s && s <= end; s++) { 301 | if (*s == c && !strncmp(s, s2, len)) { 302 | return (char *)s; 303 | } 304 | } 305 | } 306 | return NULL; 307 | } 308 | #endif 309 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # iOS WebKit Debug Proxy 2 | 3 | The ios_webkit_debug_proxy (aka _iwdp_) proxies requests from usbmuxd daemon over a websocket connection, allowing developers to send commands to MobileSafari and UIWebViews on real and simulated iOS devices. 4 | 5 | ## Installation 6 | 7 | iOS WebKit Debug Proxy works on Linux, MacOS & Windows. 8 | 9 | ### MacOS 10 | 11 | It's easiest to install with [homebrew](http://brew.sh/): 12 | 13 | ```console 14 | brew install ios-webkit-debug-proxy 15 | ``` 16 | ### Windows 17 | It's easiest to install with [scoop](http://scoop.sh/): 18 | ``` 19 | scoop bucket add extras 20 | scoop install ios-webkit-debug-proxy 21 | ``` 22 | Note: you also need the latest version of [iTunes](https://www.apple.com/il/itunes/download/) installed. 23 | 24 | ### Linux 25 | 26 | Install dependencies available in apt repository: 27 | ```console 28 | sudo apt-get install autoconf automake libusb-dev libusb-1.0-0-dev libplist-dev libtool libssl-dev 29 | ``` 30 | 31 | Build and install dependencies that require more recent versions: 32 | - [libplist](https://github.com/libimobiledevice/libplist) 33 | - [libimobiledevice-glue](https://github.com/libimobiledevice/libimobiledevice-glue) 34 | - [libusbmuxd](https://github.com/libimobiledevice/libusbmuxd) 35 | - [libimobiledevice](https://github.com/libimobiledevice/libimobiledevice) 36 | - [usbmuxd](https://github.com/libimobiledevice/usbmuxd) 37 | 38 | Build and install `ios-webkit-debug-proxy`: 39 | ```console 40 | git clone https://github.com/google/ios-webkit-debug-proxy.git 41 | cd ios-webkit-debug-proxy 42 | 43 | ./autogen.sh 44 | make 45 | sudo make install 46 | ``` 47 | 48 | ## Usage 49 | 50 | On Linux, you must run the `usbmuxd` daemon. The above install adds a /lib/udev rule to start the daemon whenever a device is attached. 51 | 52 | To verify that usbmuxd can list your attached device(s), ensure that `libimobiledevice-utils` is installed and then run `idevice_id -l`. 53 | 54 | ### Start the simulator or device 55 | 56 | The iOS Simulator is supported, but it must be started **before** the proxy. The simulator can be started in XCode, standalone, or via the command line: 57 | 58 | ```sh 59 | # Xcode changes these paths frequently, so doublecheck them 60 | SDK_DIR="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs" 61 | SIM_APP="/Applications/Xcode.app/Contents/Developer/Applications/Simulator.app/Contents/MacOS/Simulator" 62 | $SIM_APP -SimulateApplication $SDK_DIR/iPhoneSimulator8.4.sdk/Applications/MobileSafari.app/MobileSafari 63 | ``` 64 | 65 | #### Enable the inspector 66 | 67 | Your attached iOS devices must have ≥1 open browser tabs and the inspector enabled via: 68 | `Settings > Safari > Advanced > Web Inspector = ON` 69 | 70 | ### Start the proxy 71 | 72 | ```console 73 | ios_webkit_debug_proxy 74 | ``` 75 | 76 | * `--debug` for verbose output. 77 | * `--frontend` to specify a frontend 78 | * `--help` for more options. 79 | * `Ctrl-C` to quit. Also, the proxy can be left running as a background process. 80 | 81 | ### Using with DevTools 82 | 83 | ios_webkit_debug_proxy can be used with many tools such as Chrome DevTools and Safari Web Inspector. 84 | 85 | #### Chrome Devtools 86 | 87 | In recent versions of Chrome and Safari there're major discrepancies between [Chrome Remote Debugging Protocol](https://developer.chrome.com/devtools/docs/debugger-protocol) and [Webkit Inspector Protocol](https://github.com/WebKit/webkit/tree/master/Source/JavaScriptCore/inspector/protocol), which means that newer versions of Chrome DevTools aren't compatible with Safari. 88 | 89 | #### Safari Web Inspector 90 | You can use Safari Web Inspector extracted from Webkit sources, e.g. [artygus/webkit-webinspector](https://github.com/artygus/webkit-webinspector) or [HimbeersaftLP/ios-safari-remote-debug-kit](https://github.com/HimbeersaftLP/ios-safari-remote-debug-kit). 91 | 92 | #### Firefox DevTools via Valence 93 | Another option is [mozilla/valence](https://github.com/mozilla/valence) which enables Firefox DevTools to be used with iOS. 94 | 95 | ## Configuration 96 | 97 | ### View and inspect debuggable tabs 98 | 99 | Navigate to [localhost:9221](http://localhost:9221). You'll see a listing of all connected devices. 100 | 101 | Click through to view tabs available on each, and click through again to open the DevTools for a tab. 102 | 103 | ### Setting the DevTools UI URL 104 | 105 | [Chrome DevTools UI](https://developers.google.com/chrome-developer-tools/) used as a default frontend: 106 | 107 | http://chrome-devtools-frontend.appspot.com/static/27.0.1453.93/devtools.html 108 | 109 | You can use the `-f` argument to specify different frontend source, like Chrome's local DevTools, a local 110 | [Chromium checkout](https://chromium.googlesource.com/chromium/src/+/master/third_party/WebKit/Source/devtools/) or another URL: 111 | 112 | ```console 113 | # examples: 114 | ios_webkit_debug_proxy -f chrome-devtools://devtools/bundled/inspector.html 115 | ios_webkit_debug_proxy -f ~/chromium/src/third_party/WebKit/Source/devtools/front_end/inspector.html 116 | ios_webkit_debug_proxy -f http://foo.com:1234/bar/inspector.html 117 | ``` 118 | 119 | If you use `-f chrome-devtools://devtools/bundled/inspector.html`, you won't be able to click the links shown in `localhost:9222` as Chrome blocks clicking these URLs. However, you can copy/paste them into the address bar. 120 | 121 | Just the same, you can apply the appropriate port (9222) and page (2) values below. 122 | 123 | chrome-devtools://devtools/bundled/inspector.html?ws=localhost:9222/devtools/page/1 124 | 125 | The `-f` value must end in ".html". Due to security reasons, `https` URLs will not work; use `http` or force-allow with the URL bar's shield icon. As of Chrome 45, the primary URL [changed](https://codereview.chromium.org/1144393004/) from `devtools.html` to `inspector.html`. 126 | 127 | To disable the frontend proxy, use the `--no-frontend` argument. 128 | 129 | #### Port assigment 130 | 131 | The default configuration works well for most developers. The device_id-to-port assignment defaults to: 132 | 133 | :9221 for the device list 134 | :9222 for the first iOS device that is attached 135 | :9223 for the second iOS device that is attached 136 | ... 137 | :9322 for the max device 138 | 139 | If a port is in use then the next available port will be used, up to the range limit. 140 | 141 | The port assignment is first-come-first-serve but is preserved if a device is detached and reattached, assuming that the proxy is not restarted, e.g.: 142 | 143 | 1. start the proxy 144 | 1. the device list gets :9221 145 | 1. attach A gets :9222 146 | 1. attach B gets :9223 147 | 1. detach A, doesn't affect B's port 148 | 1. attach C gets :9224 (not :9222) 149 | 1. reattach A gets :9222 again (not :9225) 150 | 151 | The port assignment rules can be set via the command line with `-c`. The default is equivalent to: 152 | 153 | ios_webkit_debug_proxy -c null:9221,:9222-9322 154 | 155 | where "null" represents the device list. The following example restricts the proxy to a single device and port: 156 | 157 | ios_webkit_debug_proxy -c 4ea8dd11e8c4fbc1a2deadbeefa0fd3bbbb268c7:9227 158 | 159 | 160 | ### Troubleshooting 161 | 162 | ##### undefined reference to symbol 'log10@@GLIBC_2.2.5' 163 | ```console 164 | /usr/bin/ld: ios_webkit_debug_proxy-char_buffer.o: undefined reference to symbol 'log10@@GLIBC_2.2.5' 165 | //lib/x86_64-linux-gnu/libm.so.6: error adding symbols: DSO missing from command line 166 | ``` 167 | 168 | Run this before `make`: `./configure LIBS="-lm"` 169 | 170 | ##### error while loading shared libraries: libimobiledevice.so.6 171 | ```console 172 | ios_webkit_debug_proxy: error while loading shared libraries: libimobiledevice.so.6: cannot open shared object file: No such file or directory 173 | ``` 174 | 175 | Run `sudo ldconfig` 176 | 177 | ##### ssl sendq retry failed: Undefined error: 0 178 | 179 | should only happen with versions > 1.8.5, make sure ios-webkit-debug-proxy is built with same version of libssl that libimobildevice was built with 180 | 181 | ##### idevice_id not found 182 | 183 | The `idevice_id` executable may be found as part of the libimobiledevice-utils package. 184 | 185 | ##### could not start com.apple.webinspector! success 186 | 187 | [Remove and rebuild libimobiledevice](https://github.com/google/ios-webkit-debug-proxy/issues/82#issuecomment-74205898). 188 | 189 | ##### Could not connect to lockdownd (or doesn't work with iOS10+) 190 | > Could not connect to lockdownd. Exiting.: No such file or directory. Unable to attach inspector ios_webkit_debug_proxy 191 | 192 | Check the device for [a prompt to trust the connected computer](http://i.stack.imgur.com/hPaqX.png). Choose "Trust" and try again. 193 | 194 | > Could not connect to lockdownd. Exiting.: Broken pipe. Unable to attach inspector 195 | 196 | or 197 | 198 | > Could not connect to lockdownd, error code -\. Exiting. 199 | 200 | Make sure you're using latest version of ios-webkit-debug-proxy 201 | 202 | ##### Inspectable pages list is empty for iOS >= 12.2 203 | 204 | Make sure you're using latest version of ios-webkit-debug-proxy 205 | 206 | ##### Can not see Simulator 207 | 208 | - Make sure you started simulator before the proxy 209 | - Check that webinspector switch is enabled (Settings -> Safari -> Advanced -> Web Inspector) 210 | - Most likely simulator's web inspector daemon listens on ipv6 interface, check that you have `::1 localhost` line in `/etc/hosts` 211 | 212 | ##### Building under Rosetta (OS X) 213 | 214 | libimobildevice formulae [depends on](https://github.com/Homebrew/homebrew-core/blob/d6c416caf0622f2aac47742bca679c3510d0b1d9/Formula/libimobiledevice.rb#L30) openssl@1.1, which is key-only and requires the following env paths for the build 215 | 216 | ```console 217 | export PKG_CONFIG_PATH="/usr/local/opt/openssl@1.1/lib/pkgconfig" 218 | export LDFLAGS="-L/usr/local/opt/openssl@1.1/lib" 219 | export CPPFLAGS="-I/usr/local/opt/openssl@1.1/include" 220 | ``` 221 | 222 | ##### If no luck so far... 223 | Lastly, always try replugging in the USB cable. 224 | 225 | 226 | ## IWDP Clients 227 | 228 | JSON-formatted APIs are provided for programmatic clients. 229 | * will list all devices 230 | * to list device ":9222"'s tabs 231 | * [ws://localhost:9222/devtools/page/1]() to inspect a tab. 232 | 233 | See the [examples/README](examples/README.md) for example clients: NodeJS, C, clientside JS, websocket and more. 234 | 235 | ## Design 236 | 237 | ![Alt overview](overview.png "Overview") 238 | 239 | View the [design document](design.md) for an overview of the source layout and architecture. 240 | 241 | ## License and Copyright 242 | 243 | Google BSD license 244 | Copyright 2012 Google Inc. 245 | 246 | The proxy uses the following open-source packages: 247 | - [libplist 2.2.0](http://cgit.sukimashita.com/libplist.git) 248 | - [libusbmuxd 2.0.0](http://cgit.sukimashita.com/usbmuxd.git/) 249 | - [libimobiledevice 1.3.0](http://cgit.sukimashita.com/libimobiledevice.git) 250 | -------------------------------------------------------------------------------- /src/device_listener.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #ifdef WIN32 19 | #include 20 | #else 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #endif 27 | 28 | #include 29 | 30 | #include "char_buffer.h" 31 | #include "hash_table.h" 32 | #include "device_listener.h" 33 | 34 | // 35 | // We can't use libusbmuxd's 36 | // int usbmuxd_subscribe(usbmuxd_event_cb_t callback, void *user_data) 37 | // because it's threaded and does blocking reads, but we want a 38 | // select-friendly fd that we can loop-unroll. Fortunately this is relatively 39 | // straight-forward. 40 | // 41 | 42 | #define USBMUXD_SOCKET_PORT 27015 43 | #define USBMUXD_FILE_PATH "/var/run/usbmuxd" 44 | #define TYPE_PLIST 8 45 | #define LIBUSBMUX_VERSION 3 46 | 47 | struct dl_private { 48 | cb_t in; 49 | ht_t device_num_to_device_id; 50 | bool has_length; 51 | size_t body_length; 52 | }; 53 | 54 | int dl_connect(int recv_timeout) { 55 | int fd = -1; 56 | #ifdef WIN32 57 | fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 58 | if (fd == INVALID_SOCKET) { 59 | fprintf(stderr, "device_listener: socket function failed with\ 60 | error %d\n", WSAGetLastError()); 61 | return -1; 62 | } 63 | 64 | struct hostent *host; 65 | host = gethostbyname("localhost"); 66 | if (host == NULL) { 67 | fprintf(stderr, "device_listener: gethostbyname function failed with\ 68 | error %d\n", WSAGetLastError()); 69 | closesocket(fd); 70 | return -2; 71 | } 72 | 73 | struct sockaddr_in local; 74 | local.sin_family = AF_INET; 75 | local.sin_addr.s_addr = *(uint32_t *)host->h_addr; 76 | local.sin_port = htons(USBMUXD_SOCKET_PORT); 77 | 78 | if (connect(fd, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR) { 79 | fprintf(stderr, "device_listener: connect function failed with\ 80 | error %d\n", WSAGetLastError()); 81 | closesocket(fd); 82 | return -2; 83 | } 84 | 85 | if (recv_timeout < 0) { 86 | u_long nb = 1; 87 | if (ioctlsocket(fd, FIONBIO, &nb)) { 88 | fprintf(stderr, "device_listener: could not set socket to non-blocking"); 89 | } 90 | } 91 | #else 92 | const char *filename = USBMUXD_FILE_PATH; 93 | struct stat fst; 94 | if (stat(filename, &fst) || 95 | !S_ISSOCK(fst.st_mode) || 96 | (fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) { 97 | return -1; 98 | } 99 | 100 | struct sockaddr_un name; 101 | name.sun_family = AF_LOCAL; 102 | strncpy(name.sun_path, filename, sizeof(name.sun_path)); 103 | name.sun_path[sizeof(name.sun_path) - 1] = 0; 104 | size_t size = SUN_LEN(&name); 105 | if (connect(fd, (struct sockaddr *)&name, size) < 0) { 106 | close(fd); 107 | return -1; 108 | } 109 | 110 | if (recv_timeout < 0) { 111 | int opts = fcntl(fd, F_GETFL); 112 | if (!opts || fcntl(fd, F_SETFL, (opts | O_NONBLOCK)) < 0) { 113 | perror("Could not set socket to non-blocking"); 114 | } 115 | } 116 | #endif 117 | else { 118 | long millis = (recv_timeout > 0 ? recv_timeout : 5000); 119 | struct timeval tv; 120 | tv.tv_sec = (time_t) (millis / 1000); 121 | tv.tv_usec = (time_t) ((millis - (tv.tv_sec * 1000)) * 1000); 122 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, 123 | sizeof(tv))) { 124 | perror("Could not set socket receive timeout"); 125 | } 126 | } 127 | return fd; 128 | } 129 | 130 | char *dl_sprintf_uint32(char *buf, uint32_t value) { 131 | char *tail = buf; 132 | int8_t i; 133 | for (i = 0; i < 4; i++) { 134 | *tail++ = (unsigned char)((value >> (i<<3)) & 0xFF); 135 | } 136 | return tail; 137 | } 138 | 139 | dl_status dl_start(dl_t self) { 140 | // Assume usbmuxd supports proto_version 1. If not then we'd need to 141 | // send a binary listen request, check for failure, then retry this: 142 | plist_t dict = plist_new_dict(); 143 | plist_dict_set_item(dict, "ClientVersionString", plist_new_string( 144 | "device_listener")); 145 | if (plist_dict_get_size(dict) != 1) { 146 | perror("Detected an old copy of libplist?! For a fix, see:\n" 147 | "https://github.com/libimobiledevice/libimobiledevice/issues/" 148 | "68#issuecomment-38994545"); 149 | return DL_ERROR; 150 | } 151 | plist_dict_set_item(dict, "MessageType", plist_new_string("Listen")); 152 | plist_dict_set_item(dict, "ProgName", plist_new_string("libusbmuxd")); 153 | plist_dict_set_item(dict, "kLibUSBMuxVersion", plist_new_uint(LIBUSBMUX_VERSION)); 154 | char *xml = NULL; 155 | uint32_t xml_length = 0; 156 | plist_to_xml(dict, &xml, &xml_length); 157 | plist_free(dict); 158 | 159 | size_t length = 16 + xml_length; 160 | char *packet = (char *)calloc(length, sizeof(char)); 161 | if (!packet) { 162 | return DL_ERROR; 163 | } 164 | char *tail = packet; 165 | tail = dl_sprintf_uint32(tail, length); 166 | tail = dl_sprintf_uint32(tail, 1); // version: 1 167 | tail = dl_sprintf_uint32(tail, TYPE_PLIST); // type: plist 168 | tail = dl_sprintf_uint32(tail, 1); // tag: 1 169 | strncpy(tail, xml, xml_length); 170 | free(xml); 171 | 172 | dl_status ret = self->send_packet(self, packet, length); 173 | free(packet); 174 | return ret; 175 | } 176 | 177 | uint32_t dl_sscanf_uint32(const char *buf) { 178 | uint32_t ret = 0; 179 | const char *tail = buf; 180 | int8_t i; 181 | for (i = 0; i < 4; i++) { 182 | ret |= ((((unsigned char) *tail++) & 0xFF) << (i<<3)); 183 | } 184 | return ret; 185 | } 186 | 187 | dl_status dl_recv_packet(dl_t self, const char *packet, size_t length) { 188 | dl_private_t my = self->private_state; 189 | 190 | const char *tail = packet; 191 | uint32_t len = dl_sscanf_uint32(tail); 192 | tail += 4; 193 | if (len != length || len < 16) { 194 | return DL_ERROR; 195 | } 196 | uint32_t version = dl_sscanf_uint32(tail); 197 | tail += 4; 198 | uint32_t type = dl_sscanf_uint32(tail); 199 | tail += 4; 200 | (void)dl_sscanf_uint32(tail); 201 | tail += 4; 202 | const char *xml = tail; 203 | size_t xml_length = length - 16; 204 | 205 | if (version != 1 || type != TYPE_PLIST) { 206 | return DL_SUCCESS; // ignore? 207 | } 208 | 209 | plist_t dict = NULL; 210 | plist_from_xml(xml, xml_length, &dict); 211 | char *message = NULL; 212 | if (dict) { 213 | plist_t node = plist_dict_get_item(dict, "MessageType"); 214 | if (plist_get_node_type(node) == PLIST_STRING) { 215 | plist_get_string_val(node, &message); 216 | } 217 | } 218 | 219 | dl_status ret = DL_ERROR; 220 | if (!message) { 221 | ret = DL_ERROR; 222 | } else if (!strcmp(message, "Result")) { 223 | plist_t node = plist_dict_get_item(dict, "Number"); 224 | if (node) { 225 | uint64_t value = 0; 226 | plist_get_uint_val(node, &value); 227 | // just an ack of our Listen? 228 | ret = (value ? DL_ERROR : DL_SUCCESS); 229 | } 230 | } else if (!strcmp(message, "Attached")) { 231 | plist_t props = plist_dict_get_item(dict, "Properties"); 232 | if (props) { 233 | uint64_t device_num = 0; 234 | plist_t node = plist_dict_get_item(props, "DeviceID"); 235 | plist_get_uint_val(node, &device_num); 236 | 237 | uint64_t product_id = 0; 238 | node = plist_dict_get_item(props, "ProductID"); 239 | plist_get_uint_val(node, &product_id); 240 | 241 | char *device_id = NULL; 242 | node = plist_dict_get_item(props, "SerialNumber"); 243 | if (node) { 244 | plist_get_string_val(node, &device_id); 245 | 246 | if (device_id && strlen(device_id) == 24) { 247 | char *new_device_id = malloc(sizeof(char) * 26); 248 | 249 | memcpy(new_device_id, device_id, 8); 250 | memcpy(new_device_id + 9, device_id + 8, 17); 251 | new_device_id[8] = '-'; 252 | 253 | free(device_id); 254 | device_id = new_device_id; 255 | } 256 | } 257 | 258 | uint64_t location = 0; 259 | node = plist_dict_get_item(props, "LocationID"); 260 | plist_get_uint_val(node, &location); 261 | 262 | ht_t d_ht = my->device_num_to_device_id; 263 | ht_put(d_ht, HT_KEY(device_num), device_id); 264 | ret = self->on_attach(self, device_id, (int)device_num); 265 | } 266 | } else if (strcmp(message, "Detached") == 0) { 267 | plist_t node = plist_dict_get_item(dict, "DeviceID"); 268 | if (node) { 269 | uint64_t device_num = 0; 270 | plist_get_uint_val(node, &device_num); 271 | 272 | ht_t d_ht = my->device_num_to_device_id; 273 | char *device_id = (char *)ht_remove(d_ht, HT_KEY(device_num)); 274 | if (device_id) { 275 | ret = self->on_detach(self, device_id, (int)device_num); 276 | free(device_id); 277 | } 278 | } 279 | } 280 | free(message); 281 | plist_free(dict); 282 | return ret; 283 | } 284 | 285 | dl_status dl_recv_loop(dl_t self) { 286 | dl_private_t my = self->private_state; 287 | dl_status ret; 288 | const char *in_head = my->in->in_head; 289 | const char *in_tail = my->in->in_tail; 290 | while (1) { 291 | size_t in_length = in_tail - in_head; 292 | if (!my->has_length && in_length >= 4) { 293 | // can read body_length now 294 | size_t len = dl_sscanf_uint32(in_head); 295 | my->body_length = len; 296 | my->has_length = true; 297 | // don't advance in_head yet 298 | } else if (my->has_length && in_length >= my->body_length) { 299 | // can read body now 300 | ret = dl_recv_packet(self, in_head, my->body_length); 301 | in_head += my->body_length; 302 | my->has_length = false; 303 | my->body_length = 0; 304 | if (ret) { 305 | break; 306 | } 307 | } else { 308 | // need more input 309 | ret = DL_SUCCESS; 310 | break; 311 | } 312 | } 313 | my->in->in_head = in_head; 314 | return ret; 315 | } 316 | 317 | dl_status dl_on_recv(dl_t self, const char *buf, ssize_t length) { 318 | dl_private_t my = self->private_state; 319 | if (length < 0) { 320 | return DL_ERROR; 321 | } else if (length == 0) { 322 | return DL_SUCCESS; 323 | } 324 | if (cb_begin_input(my->in, buf, length)) { 325 | return DL_ERROR; 326 | } 327 | dl_status ret = dl_recv_loop(self); 328 | if (cb_end_input(my->in)) { 329 | return DL_ERROR; 330 | } 331 | return ret; 332 | } 333 | 334 | 335 | dl_t dl_new() { 336 | dl_t self = (dl_t)malloc(sizeof(struct dl_struct)); 337 | dl_private_t my = (dl_private_t)malloc(sizeof(struct dl_private)); 338 | cb_t in = cb_new(); 339 | ht_t d_ht = ht_new(HT_INT_KEYS); 340 | if (!self || !my || !in || !d_ht) { 341 | free(self); 342 | free(my); 343 | free(in); 344 | return NULL; 345 | } 346 | memset(self, 0, sizeof(struct dl_struct)); 347 | memset(my, 0, sizeof(struct dl_private)); 348 | self->start = dl_start; 349 | self->on_recv = dl_on_recv; 350 | self->private_state = my; 351 | my->in = in; 352 | my->device_num_to_device_id = d_ht; 353 | return self; 354 | } 355 | 356 | void dl_free(dl_t self) { 357 | if (self) { 358 | dl_private_t my = self->private_state; 359 | if (my) { 360 | cb_free(my->in); 361 | ht_free(my->device_num_to_device_id); 362 | memset(my, 0, sizeof(struct dl_private)); 363 | free(my); 364 | } 365 | memset(self, 0, sizeof(struct dl_struct)); 366 | free(self); 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /include/validate_utf8.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2011 self.disconnect 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | /* 18 | * This is the state transition table for validating UTF-8 input. 19 | * 20 | * We start in the initial state (s0) and expect to be in that state at the end 21 | * of the input sequence. If we transition to the error state (e) or end in a 22 | * state other than end state (s0), we consider that input sequence to be 23 | * invalid. In an attempt to make the table a little easier to understand, 24 | * states t1, t2, and t3 are used to signify the various tail states. The 25 | * number following the t is the number of remaining tail inputs remaining. A 26 | * tail accepts input in the range of %x80 - %xBF. With valid input, t3 27 | * transitions to t2, t2 transitions to t1, and t1 transition back to s0. 28 | * 29 | * States s1 through s4 are used to handle the more complex intermediary 30 | * transitions. 31 | * 32 | * Here is the ABNF from which the table was derived (see RFC 3629): 33 | * 34 | * UTF8-octets = *( UTF8-char ) 35 | * UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4 36 | * UTF8-1 = %x00-7F 37 | * UTF8-2 = %xC2-DF UTF8-tail 38 | * UTF8-3 = %xE0 %xA0-BF UTF8-tail / %xE1-EC 2( UTF8 - tail ) / 39 | * %xED %x80-9F UTF8-tail / %xEE-EF 2( UTF8 - tail ) 40 | * UTF8-4 = %xF0 %x90-BF 2( UTF8-tail ) / %xF1-F3 3( UTF8 - tail ) / 41 | * %xF4 %x80-8F 2( UTF8-tail ) 42 | * UTF8-tail = %x80-BF 43 | * 44 | * start end 45 | * state input state 46 | * -------------------- 47 | * s0 : %x00-7F => s0 48 | * s0 : %x80-C1 => e 49 | * s0 : %xC2-DF => t1 50 | * s0 : %xE0 => s1 51 | * s0 : %xE1-EC => t2 52 | * s0 : %xED => s2 53 | * s0 : %xEE-EF => t2 54 | * s0 : %xF0 => s3 55 | * s0 : %xF1-F3 => t3 56 | * s0 : %xF4 => s4 57 | * s0 : %xF5-FF => e 58 | * t1 : %x00-7F => e 59 | * t1 : %x80-BF => s0 60 | * t1 : %xC0-FF => e 61 | * t2 : %x00-7F => e 62 | * t2 : %x80-BF => t1 63 | * t2 : %xC0-FF => e 64 | * s1 : %x00-9F => e 65 | * s1 : %xA0-BF => t1 66 | * s1 : %xC0-FF => e 67 | * s2 : %x00-7F => e 68 | * s2 : %x80-9F => t1 69 | * s2 : %xA0-FF => e 70 | * t3 : %x00-7F => e 71 | * t3 : %x80-BF => t2 72 | * t3 : %xC0-FF => e 73 | * s3 : %x00-8F => e 74 | * s3 : %x90-BF => t2 75 | * s3 : %xC0-FF => e 76 | * s4 : %x00-7F => e 77 | * s4 : %x80-8F => t2 78 | * s4 : %x90-FF => e 79 | * 80 | * Here is an example of one way to use the UTF-8 validation table to validate 81 | * input. The input bytes must be unsigned in order to make sure that the 82 | * table is accessed properly. While processing the input sequence, only the 83 | * INVALID state corresponds to an error. After proccessing of the input has 84 | * been completed, any state other than the VALID state is an error. 85 | * 86 | * unsigned char *input = // pointer to the input sequence 87 | * unsigned int state = UTF8_VALID; 88 | * 89 | * while (*input != 0) { 90 | * state = validate_utf8[state + *input++]; 91 | * if (state == UTF8_INVALID) { 92 | * break; 93 | * } 94 | * } 95 | * if (state == UTF8_VALID) { 96 | * // Valid 97 | * } else { 98 | * // Invalid 99 | * } 100 | * 101 | */ 102 | 103 | #if !defined(_VALIDATE_UTF8_H_) 104 | #define _VALIDATE_UTF8_H_ 105 | 106 | #define S0 0x000 107 | #define T1 0x100 108 | #define T2 0x200 109 | #define S1 0x300 110 | #define S2 0x400 111 | #define T3 0x500 112 | #define S3 0x600 113 | #define S4 0x700 114 | #define ER 0x800 115 | 116 | #define UTF8_VALID 0x000 117 | #define UTF8_INVALID 0x800 118 | 119 | static const unsigned short validate_utf8[2048] = { 120 | /* S0 (0x000) */ 121 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x00-0F */ 122 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x10-1F */ 123 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x20-2F */ 124 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x30-3F */ 125 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x40-4F */ 126 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x50-5F */ 127 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x60-6F */ 128 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x70-7F */ 129 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x80-8F */ 130 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x90-9F */ 131 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xA0-AF */ 132 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xB0-BF */ 133 | ER,ER,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %xC0-CF */ 134 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %xD0-DF */ 135 | S1,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,S2,T2,T2, /* %xE0-EF */ 136 | S3,T3,T3,T3,S4,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 137 | /* T1 (0x100) */ 138 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 139 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 140 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 141 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 142 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 143 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 144 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 145 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 146 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x80-8F */ 147 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %x90-9F */ 148 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %xA0-AF */ 149 | S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0,S0, /* %xB0-BF */ 150 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 151 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 152 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 153 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 154 | /* T2 (0x200) */ 155 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 156 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 157 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 158 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 159 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 160 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 161 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 162 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 163 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %x80-8F */ 164 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %x90-9F */ 165 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %xA0-AF */ 166 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %xB0-BF */ 167 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 168 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 169 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 170 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 171 | /* S1 (0x300) */ 172 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 173 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 174 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 175 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 176 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 177 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 178 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 179 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 180 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x80-8F */ 181 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x90-9F */ 182 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %xA0-AF */ 183 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %xB0-BF */ 184 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 185 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 186 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 187 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 188 | /* S2 (0x400) */ 189 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 190 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 191 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 192 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 193 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 194 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 195 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 196 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 197 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %x80-8F */ 198 | T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1,T1, /* %x90-9F */ 199 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xA0-AF */ 200 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xB0-BF */ 201 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 202 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 203 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 204 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 205 | /* T3 (0x500) */ 206 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 207 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 208 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 209 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 210 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 211 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 212 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 213 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 214 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %x80-8F */ 215 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %x90-9F */ 216 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %xA0-AF */ 217 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %xB0-BF */ 218 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 219 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 220 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 221 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 222 | /* S3 (0x600) */ 223 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 224 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 225 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 226 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 227 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 228 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 229 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 230 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 231 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x80-8F */ 232 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %x90-9F */ 233 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %xA0-AF */ 234 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %xB0-BF */ 235 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 236 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 237 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 238 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 239 | /* S4 (0x700) */ 240 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x00-0F */ 241 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x10-1F */ 242 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x20-2F */ 243 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x30-3F */ 244 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x40-4F */ 245 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x50-5F */ 246 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x60-6F */ 247 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x70-7F */ 248 | T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2,T2, /* %x80-8F */ 249 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %x90-9F */ 250 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xA0-AF */ 251 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xB0-BF */ 252 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xC0-CF */ 253 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xD0-DF */ 254 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xE0-EF */ 255 | ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER,ER, /* %xF0-FF */ 256 | }; 257 | 258 | #undef S0 259 | #undef T1 260 | #undef T2 261 | #undef S1 262 | #undef S2 263 | #undef T3 264 | #undef S3 265 | #undef S4 266 | #undef ER 267 | 268 | #endif /* _VALIDATE_UTF8_H_ */ 269 | -------------------------------------------------------------------------------- /src/ios_webkit_debug_proxy_main.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | // 5 | // This "main" connects the debugger to our socket management backend. 6 | // 7 | 8 | #ifdef HAVE_CONFIG_H 9 | #include 10 | #endif 11 | 12 | #define _GNU_SOURCE 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #ifndef HAVE_REGEX_H 22 | #include 23 | #include 24 | #else 25 | #include 26 | #endif 27 | 28 | #ifdef WIN32 29 | #include 30 | #endif 31 | 32 | #include 33 | 34 | #include "device_listener.h" 35 | #include "hash_table.h" 36 | #include "ios_webkit_debug_proxy.h" 37 | #include "port_config.h" 38 | #include "socket_manager.h" 39 | #include "webinspector.h" 40 | #include "websocket.h" 41 | 42 | 43 | struct iwdpm_struct { 44 | char *config; 45 | char *frontend; 46 | char *sim_wi_socket_addr; 47 | bool is_debug; 48 | 49 | pc_t pc; 50 | sm_t sm; 51 | iwdp_t iwdp; 52 | }; 53 | typedef struct iwdpm_struct *iwdpm_t; 54 | iwdpm_t iwdpm_new(); 55 | void iwdpm_free(iwdpm_t self); 56 | 57 | int iwdpm_configure(iwdpm_t self, int argc, char **argv); 58 | 59 | void iwdpm_create_bridge(iwdpm_t self); 60 | 61 | static int quit_flag = 0; 62 | 63 | static void on_signal(int sig) { 64 | quit_flag++; 65 | } 66 | 67 | int main(int argc, char** argv) { 68 | signal(SIGINT, on_signal); 69 | signal(SIGTERM, on_signal); 70 | 71 | #ifdef WIN32 72 | WSADATA wsa_data; 73 | int res = WSAStartup(MAKEWORD(2,2), &wsa_data); 74 | if (res) { 75 | fprintf(stderr, "WSAStartup failed with error: %d\n", res); 76 | exit(1); 77 | } 78 | #endif 79 | 80 | iwdpm_t self = iwdpm_new(); 81 | int ret = iwdpm_configure(self, argc, argv); 82 | if (ret) { 83 | exit(ret > 0 ? ret : 0); 84 | return ret; 85 | } 86 | 87 | iwdpm_create_bridge(self); 88 | 89 | iwdp_t iwdp = self->iwdp; 90 | if (iwdp->start(iwdp)) { 91 | return -1;// TODO cleanup 92 | } 93 | 94 | sm_t sm = self->sm; 95 | while (!quit_flag) { 96 | if (sm->select(sm, 2) < 0) { 97 | ret = -1; 98 | break; 99 | } 100 | } 101 | sm->cleanup(sm); 102 | iwdpm_free(self); 103 | #ifdef WIN32 104 | WSACleanup(); 105 | #endif 106 | return ret; 107 | } 108 | // 109 | // Connect ios_webkit_debug_proxy to socket_selector/etc: 110 | // 111 | 112 | int iwdpm_subscribe(iwdp_t iwdp) { 113 | return dl_connect(-1); 114 | } 115 | int iwdpm_attach(iwdp_t iwdp, const char *device_id, char **to_device_id, 116 | char **to_device_name, int *to_device_os_version, void **to_ssl_session) { 117 | return wi_connect(device_id, to_device_id, to_device_name, 118 | to_device_os_version, to_ssl_session, -1); 119 | } 120 | iwdp_status iwdpm_select_port(iwdp_t iwdp, const char *device_id, 121 | int *to_port, int *to_min_port, int *to_max_port) { 122 | iwdpm_t self = (iwdpm_t)iwdp->state; 123 | int ret = 0; 124 | // reparse every time, in case the file has changed 125 | int is_file = 0; 126 | if (!self->pc) { 127 | self->pc = pc_new(); 128 | if (pc_add_line(self->pc, self->config, strlen(self->config))) { 129 | pc_clear(self->pc); 130 | pc_add_file(self->pc, self->config); 131 | is_file = 1; 132 | } 133 | } 134 | ret = pc_select_port(self->pc, device_id, to_port, to_min_port,to_max_port); 135 | if (is_file) { 136 | pc_free(self->pc); 137 | self->pc = NULL; 138 | } 139 | return (ret ? IWDP_ERROR : IWDP_SUCCESS); 140 | } 141 | int iwdpm_listen(iwdp_t iwdp, int port) { 142 | return sm_listen(port); 143 | } 144 | int iwdpm_connect(iwdp_t iwdp, const char *socket_addr) { 145 | return sm_connect(socket_addr); 146 | } 147 | iwdp_status iwdpm_send(iwdp_t iwdp, int fd, const char *data, size_t length) { 148 | sm_t sm = ((iwdpm_t)iwdp->state)->sm; 149 | return sm->send(sm, fd, data, length, NULL); 150 | } 151 | iwdp_status iwdpm_add_fd(iwdp_t iwdp, int fd, void *ssl_session, void *value, 152 | bool is_server) { 153 | sm_t sm = ((iwdpm_t)iwdp->state)->sm; 154 | return sm->add_fd(sm, fd, ssl_session, value, is_server); 155 | } 156 | iwdp_status iwdpm_remove_fd(iwdp_t iwdp, int fd) { 157 | sm_t sm = ((iwdpm_t)iwdp->state)->sm; 158 | return sm->remove_fd(sm, fd); 159 | } 160 | sm_status iwdpm_on_accept(sm_t sm, int s_fd, void *s_value, 161 | int fd, void **to_value) { 162 | iwdp_t iwdp = ((iwdpm_t)sm->state)->iwdp; 163 | return iwdp->on_accept(iwdp, s_fd, s_value, fd, to_value); 164 | } 165 | sm_status iwdpm_on_sent(sm_t sm, int fd, void *value, 166 | const char *buf, ssize_t length) { 167 | return SM_SUCCESS; 168 | } 169 | sm_status iwdpm_on_recv(sm_t sm, int fd, void *value, 170 | const char *buf, ssize_t length) { 171 | iwdp_t iwdp = ((iwdpm_t)sm->state)->iwdp; 172 | return iwdp->on_recv(iwdp, fd, value, buf, length); 173 | } 174 | sm_status iwdpm_on_close(sm_t sm, int fd, void *value, bool is_server) { 175 | iwdp_t iwdp = ((iwdpm_t)sm->state)->iwdp; 176 | return iwdp->on_close(iwdp, fd, value, is_server); 177 | } 178 | 179 | void iwdpm_create_bridge(iwdpm_t self) { 180 | sm_t sm = sm_new(4096); 181 | iwdp_t iwdp = iwdp_new(self->frontend, self->sim_wi_socket_addr); 182 | if (!sm || !iwdp) { 183 | sm_free(sm); 184 | return; 185 | } 186 | self->sm = sm; 187 | self->iwdp = iwdp; 188 | iwdp->subscribe = iwdpm_subscribe; 189 | iwdp->attach = iwdpm_attach; 190 | iwdp->select_port = iwdpm_select_port; 191 | iwdp->listen = iwdpm_listen; 192 | iwdp->connect = iwdpm_connect; 193 | iwdp->send = iwdpm_send; 194 | iwdp->add_fd = iwdpm_add_fd; 195 | iwdp->remove_fd = iwdpm_remove_fd; 196 | iwdp->state = self; 197 | iwdp->is_debug = &self->is_debug; 198 | sm->on_accept = iwdpm_on_accept; 199 | sm->on_sent = iwdpm_on_sent; 200 | sm->on_recv = iwdpm_on_recv; 201 | sm->on_close = iwdpm_on_close; 202 | sm->state = self; 203 | sm->is_debug = &self->is_debug; 204 | } 205 | 206 | 207 | void iwdpm_free(iwdpm_t self) { 208 | if (self) { 209 | pc_free(self->pc); 210 | iwdp_free(self->iwdp); 211 | sm_free(self->sm); 212 | free(self->config); 213 | free(self->frontend); 214 | free(self->sim_wi_socket_addr); 215 | memset(self, 0, sizeof(struct iwdpm_struct)); 216 | free(self); 217 | } 218 | } 219 | 220 | iwdpm_t iwdpm_new() { 221 | iwdpm_t self = malloc(sizeof(struct iwdpm_struct)); 222 | if (!self) { 223 | return NULL; 224 | } 225 | memset(self, 0, sizeof(struct iwdpm_struct)); 226 | return self; 227 | } 228 | 229 | int iwdpm_configure(iwdpm_t self, int argc, char **argv) { 230 | 231 | static struct option longopts[] = { 232 | {"udid", 1, NULL, 'u'}, 233 | {"config", 1, NULL, 'c'}, 234 | {"frontend", 1, NULL, 'f'}, 235 | {"no-frontend", 0, NULL, 'F'}, 236 | {"simulator-webinspector", 1, NULL, 's'}, 237 | {"debug", 0, NULL, 'd'}, 238 | {"help", 0, NULL, 'h'}, 239 | {"version", 0, NULL, 'V'}, 240 | {NULL, 0, NULL, 0} 241 | }; 242 | const char *DEFAULT_CONFIG = "null:9221,:9222-9322"; 243 | const char *DEFAULT_FRONTEND = 244 | "http://chrome-devtools-frontend.appspot.com/static/27.0.1453.93/devtools.html"; 245 | // The port 27753 is from `locate com.apple.webinspectord.plist` 246 | const char *DEFAULT_SIM_WI_SOCKET_ADDR = "localhost:27753"; 247 | 248 | self->config = strdup(DEFAULT_CONFIG); 249 | self->frontend = strdup(DEFAULT_FRONTEND); 250 | self->sim_wi_socket_addr = strdup(DEFAULT_SIM_WI_SOCKET_ADDR); 251 | 252 | int ret = 0; 253 | while (!ret) { 254 | int c = getopt_long(argc, argv, "hVu:c:f:Fs:d", longopts, (int *)0); 255 | if (c == -1) { 256 | break; 257 | } 258 | switch (c) { 259 | case 'h': 260 | ret = -1; 261 | break; 262 | case 'V': 263 | printf( 264 | "%s\n" 265 | "Built with libimobiledevice v%s, libplist v%s, libusbmuxd v%s, %s\n", 266 | PACKAGE_STRING, LIBIMOBILEDEVICE_VERSION, LIBPLIST_VERSION, 267 | LIBUSBMUXD_VERSION, OPENSSL_VERSION_TEXT); 268 | ret = -2; 269 | break; 270 | case 'u': 271 | { 272 | regex_t *re = malloc(sizeof(regex_t)); 273 | regcomp(re, "^[a-fA-F0-9-]{25,}(:[0-9]+(-[0-9]+)?)?$", REG_EXTENDED); 274 | size_t ngroups = re->re_nsub + 1; 275 | regmatch_t *groups = calloc(ngroups, sizeof(regmatch_t)); 276 | bool is_match = !regexec(re, optarg, ngroups, groups, 0); 277 | bool has_port = (is_match && groups[1].rm_so >= 0); 278 | free(groups); 279 | regfree(re); 280 | free(self->config); 281 | self->config = NULL; 282 | if (!is_match) { 283 | ret = 2; 284 | } else if (!has_port) { 285 | if (asprintf(&self->config, "%s%s", optarg, ":9222") < 0) { 286 | ret = 2; // asprintf failed 287 | } 288 | } else { 289 | self->config = strdup(optarg); 290 | } 291 | } 292 | break; 293 | case 'c': 294 | free(self->config); 295 | self->config = strdup(optarg); 296 | break; 297 | case 's': 298 | free(self->sim_wi_socket_addr); 299 | self->sim_wi_socket_addr = strdup(optarg); 300 | break; 301 | case 'f': 302 | case 'F': 303 | free(self->frontend); 304 | self->frontend = (c == 'f' ? strdup(optarg) : NULL); 305 | break; 306 | case 'd': 307 | self->is_debug = true; 308 | break; 309 | default: 310 | ret = 2; 311 | break; 312 | } 313 | } 314 | 315 | if (!ret && ((argc - optind) > 0)) { 316 | ret = 2; 317 | } 318 | 319 | if (ret && ret != -2) { 320 | char *name = strrchr(argv[0], '/'); 321 | printf( 322 | "Usage: %s [OPTIONS]\n" 323 | "iOS WebKit Remote Debugging Protocol Proxy v%s.\n" 324 | "\n" 325 | "By default, the proxy will list all attached iOS devices on:\n" 326 | " http://localhost:9221\n" 327 | "and assign each device an incremented port number, e.g.:\n" 328 | " http://localhost:9222\n" 329 | "which lists the device's pages and provides inspector access.\n" 330 | "\n" 331 | "Your attached iOS device(s) must have the inspector enabled via:\n" 332 | " Settings > Safari > Advanced > Web Inspector = ON\n" 333 | "and have one or more open browser pages.\n" 334 | "\n" 335 | "To view the DevTools UI, either use the above links (which use the" 336 | " \"frontend\"\nURL noted below) or use Chrome's built-in inspector," 337 | " e.g.:\n" 338 | " chrome-devtools://devtools/bundled/inspector.html?ws=localhost:" 339 | "9222/devtools/page/1" 340 | "\n\n" 341 | "OPTIONS:\n" 342 | "\n" 343 | " -u UDID[:minPort-[maxPort]]\tTarget a specific device by its" 344 | " digital ID.\n" 345 | " minPort defaults to 9222. maxPort defaults to minPort.\n" 346 | " This is shorthand for the following \"-c\" option.\n" 347 | "\n" 348 | " -c, --config CSV\tUDID-to-port(s) configuration.\n" 349 | " Defaults to:\n" 350 | " %s\n" 351 | " which lists devices (\"null:\") on port 9221 and assigns\n" 352 | " all other devices (\":\") to the next unused port in the\n" 353 | " 9222-9322 range, in the (somewhat random) order that the\n" 354 | " devices are detected.\n" 355 | " The value can be the path to a file in the above format.\n" 356 | "\n" 357 | " -f, --frontend URL\tDevTools frontend UI path or URL.\n" 358 | " Defaults to:\n" 359 | " %s\n" 360 | " Examples:\n" 361 | " * Use Chrome's built-in inspector:\n" 362 | " chrome-devtools://devtools/bundled/inspector.html\n" 363 | " * Use a local WebKit checkout:\n" 364 | " /usr/local/WebCore/inspector/front-end/inspector.html\n" 365 | " * Use an online copy of the inspector pages:\n" 366 | " http://chrome-devtools-frontend.appspot.com/static/" 367 | "33.0.1722.0" 368 | "/devtools.html\n" 369 | " where other online versions include:\n" 370 | " 18.0.1025.74\n" 371 | " 25.0.1364.169\n" 372 | " 28.0.1501.0\n" 373 | " 30.0.1599.92\n" 374 | " 31.0.1651.0\n" 375 | " 32.0.1689.3\n" 376 | "\n" 377 | " -F, --no-frontend\tDisable the DevTools frontend.\n" 378 | "\n" 379 | " -s, --simulator-webinspector\tSimulator web inspector socket\n" 380 | " address. Provided value value needs to be in format\n" 381 | " HOSTNAME:PORT or UNIX:PATH\n" 382 | " Defaults to:\n" 383 | " %s\n" 384 | " Examples:\n" 385 | " * TCP socket:\n" 386 | " 192.168.0.20:27753\n" 387 | " * Unix domain socket:\n" 388 | " unix:/private/tmp/com.apple.launchd.2j5k1TMh6i/" 389 | "com.apple.webinspectord_sim.socket\n" 390 | "\n" 391 | " -d, --debug\t\tEnable debug output.\n" 392 | " -h, --help\t\tPrint this usage information.\n" 393 | " -V, --version\t\tPrint version information and exit.\n" 394 | "\n", (name ? name + 1 : argv[0]), PACKAGE_VERSION, DEFAULT_CONFIG, 395 | DEFAULT_FRONTEND, DEFAULT_SIM_WI_SOCKET_ADDR); 396 | } 397 | return ret; 398 | } 399 | -------------------------------------------------------------------------------- /src/webinspector.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef WIN32 19 | #include 20 | #else 21 | #include 22 | #include 23 | #include 24 | #endif 25 | 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "char_buffer.h" 33 | #include "idevice_ext.h" 34 | #include "webinspector.h" 35 | 36 | 37 | #define WI_DEBUG 1 38 | 39 | // TODO figure out exact value 40 | #define MAX_RPC_LEN 8096 - 500 41 | 42 | // some arbitrarly limit, to catch bad packets 43 | #define MAX_BODY_LENGTH 1<<26 44 | 45 | struct wi_private { 46 | bool partials_supported; 47 | cb_t in; 48 | cb_t partial; 49 | bool has_length; 50 | size_t body_length; 51 | }; 52 | 53 | // 54 | // CONNECT 55 | // 56 | static const char *lockdownd_err_to_string(int ldret) { 57 | switch (ldret) { 58 | case LOCKDOWN_E_PASSWORD_PROTECTED: 59 | return "Please enter the passcode on the device, then try again."; 60 | case LOCKDOWN_E_PAIRING_DIALOG_RESPONSE_PENDING: 61 | return "Please accept the trust dialog on the screen of device, then try again."; 62 | case LOCKDOWN_E_USER_DENIED_PAIRING: 63 | return "User denied the trust dialog. Re-plug device and try again."; 64 | case LOCKDOWN_E_INVALID_CONF: 65 | case LOCKDOWN_E_INVALID_HOST_ID: 66 | return "Device is not paired with this host. Re-plug device and try again."; 67 | default: 68 | return "Could not connect to lockdownd, error code: %d."; 69 | } 70 | } 71 | 72 | int wi_connect(const char *device_id, char **to_device_id, 73 | char **to_device_name, int *to_device_os_version, 74 | void **to_ssl_session, int recv_timeout) { 75 | int ret = -1; 76 | 77 | idevice_t phone = NULL; 78 | plist_t node = NULL; 79 | lockdownd_service_descriptor_t service = NULL; 80 | lockdownd_client_t client = NULL; 81 | idevice_connection_t connection = NULL; 82 | int fd = -1; 83 | SSL *ssl_session = NULL; 84 | 85 | // get phone 86 | if (idevice_new_with_options(&phone, device_id, IDEVICE_LOOKUP_USBMUX | IDEVICE_LOOKUP_NETWORK)) { 87 | fprintf(stderr, "No device found, is it plugged in?\n"); 88 | goto leave_cleanup; 89 | } 90 | 91 | // connect to lockdownd 92 | lockdownd_error_t ldret; 93 | if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_client_new_with_handshake( 94 | phone, &client, "ios_webkit_debug_proxy"))) { 95 | fprintf(stderr, "%s\n", lockdownd_err_to_string(ldret)); 96 | goto leave_cleanup; 97 | } 98 | 99 | // get device info 100 | if (to_device_id && 101 | !lockdownd_get_value(client, NULL, "UniqueDeviceID", &node)) { 102 | plist_get_string_val(node, to_device_id); 103 | plist_free(node); 104 | node = NULL; 105 | } 106 | if (to_device_name && 107 | !lockdownd_get_value(client, NULL, "DeviceName", &node)) { 108 | plist_get_string_val(node, to_device_name); 109 | plist_free(node); 110 | node = NULL; 111 | } 112 | if (to_device_os_version && 113 | !lockdownd_get_value(client, NULL, "ProductVersion", &node)) { 114 | int vers[3] = {0, 0, 0}; 115 | char *s_version = NULL; 116 | plist_get_string_val(node, &s_version); 117 | if (s_version && sscanf(s_version, "%d.%d.%d", 118 | &vers[0], &vers[1], &vers[2]) >= 2) { 119 | *to_device_os_version = ((vers[0] & 0xFF) << 16) | 120 | ((vers[1] & 0xFF) << 8) | 121 | (vers[2] & 0xFF); 122 | } else { 123 | *to_device_os_version = 0; 124 | } 125 | free(s_version); 126 | plist_free(node); 127 | } 128 | 129 | // start webinspector, get port 130 | if (LOCKDOWN_E_SUCCESS != (ldret = lockdownd_start_service(client, 131 | "com.apple.webinspector", &service)) || !service || !service->port) { 132 | fprintf(stderr, "Could not start com.apple.webinspector! Error code: %d\n", ldret); 133 | goto leave_cleanup; 134 | } 135 | 136 | // connect to webinspector 137 | if (idevice_connect(phone, service->port, &connection)) { 138 | perror("idevice_connect failed!"); 139 | goto leave_cleanup; 140 | } 141 | 142 | if (client) { 143 | // not needed anymore 144 | lockdownd_client_free(client); 145 | client = NULL; 146 | } 147 | 148 | // extract the connection fd 149 | if (idevice_connection_get_fd(connection, &fd)) { 150 | perror("Unable to get connection file descriptor."); 151 | goto leave_cleanup; 152 | } 153 | 154 | // enable ssl 155 | if (service->ssl_enabled == 1) { 156 | int ssl_ret = 0; 157 | if (!to_ssl_session || (ssl_ret = idevice_ext_connection_enable_ssl(device_id, fd, &ssl_session))) { 158 | fprintf(stderr, "SSL connection failed! Error code: %d\n", ssl_ret); 159 | goto leave_cleanup; 160 | } 161 | *to_ssl_session = ssl_session; 162 | } 163 | 164 | if (recv_timeout < 0) { 165 | #ifdef WIN32 166 | u_long nb = 1; 167 | if (ioctlsocket(fd, FIONBIO, &nb)) { 168 | fprintf(stderr, "webinspector: could not set socket to non-blocking"); 169 | } 170 | #else 171 | int opts = fcntl(fd, F_GETFL); 172 | if (!opts || fcntl(fd, F_SETFL, (opts | O_NONBLOCK)) < 0) { 173 | perror("Could not set socket to non-blocking"); 174 | goto leave_cleanup; 175 | } 176 | #endif 177 | } else { 178 | long millis = (recv_timeout > 0 ? recv_timeout : 5000); 179 | struct timeval tv; 180 | tv.tv_sec = (time_t) (millis / 1000); 181 | tv.tv_usec = (time_t) ((millis - (tv.tv_sec * 1000)) * 1000); 182 | if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, 183 | sizeof(tv))) { 184 | perror("Could not set socket receive timeout"); 185 | goto leave_cleanup; 186 | } 187 | } 188 | 189 | // success 190 | ret = fd; 191 | 192 | leave_cleanup: 193 | #ifdef WIN32 194 | if (ret < 0 && fd != INVALID_SOCKET) { 195 | closesocket(fd); 196 | } 197 | #else 198 | if (ret < 0 && fd > 0) { 199 | close(fd); 200 | } 201 | #endif 202 | // don't call usbmuxd_disconnect(fd)! 203 | //idevice_disconnect(connection); 204 | free(connection); 205 | lockdownd_client_free(client); 206 | idevice_free(phone); 207 | return ret; 208 | } 209 | 210 | // 211 | // SEND 212 | // 213 | 214 | wi_status wi_on_error(wi_t self, const char *format, ...) { 215 | va_list args; 216 | va_start(args, format); 217 | vfprintf(stderr, format, args); 218 | fprintf(stderr, "\n"); 219 | va_end(args); 220 | return WI_ERROR; 221 | } 222 | 223 | wi_status wi_on_debug(wi_t self, const char *message, 224 | const char *buf, size_t length) { 225 | if (self->is_debug && *self->is_debug) { 226 | char *text; 227 | cb_asprint(&text, buf, length, 80, 30); 228 | printf("%s[%zd]:\n%s\n", message, length, text); 229 | free(text); 230 | } 231 | return WI_SUCCESS; 232 | } 233 | 234 | /* 235 | WIRFinalMessageKey 236 | __selector 237 | __argument 238 | */ 239 | wi_status wi_send_plist(wi_t self, plist_t rpc_dict) { 240 | wi_private_t my = self->private_state; 241 | char *rpc_bin = NULL; 242 | uint32_t rpc_len = 0; 243 | plist_to_bin(rpc_dict, &rpc_bin, &rpc_len); 244 | // if our message is <8k, we'll send a single final_msg, 245 | // otherwise we'll send <8k partial_msg "chunks" then a final_msg "chunk" 246 | wi_status ret = WI_ERROR; 247 | uint32_t i; 248 | for (i = 0; ; i += MAX_RPC_LEN) { 249 | bool is_partial = false; 250 | char *data = NULL; 251 | uint32_t data_len = 0; 252 | if (!my->partials_supported) { 253 | data = rpc_bin; 254 | data_len = rpc_len; 255 | rpc_bin = NULL; 256 | } else { 257 | is_partial = (rpc_len - i > MAX_RPC_LEN); 258 | plist_t wi_dict = plist_new_dict(); 259 | plist_t wi_rpc = plist_new_data(rpc_bin + i, 260 | (is_partial ? MAX_RPC_LEN : rpc_len - i)); 261 | plist_dict_set_item(wi_dict, 262 | (is_partial ? "WIRPartialMessageKey" : "WIRFinalMessageKey"), wi_rpc); 263 | plist_to_bin(wi_dict, &data, &data_len); 264 | plist_free(wi_dict); 265 | wi_dict = NULL; 266 | wi_rpc = NULL; // freed by wi_dict 267 | if (!data) { 268 | break; 269 | } 270 | } 271 | 272 | size_t length = data_len + 4; 273 | char *out_head = (char*)malloc(length * sizeof(char)); 274 | if (!out_head) { 275 | if (my->partials_supported) { 276 | free(data); 277 | } 278 | break; 279 | } 280 | char *out_tail = out_head; 281 | 282 | // write big-endian int 283 | *out_tail++ = ((data_len >> 24) & 0xFF); 284 | *out_tail++ = ((data_len >> 16) & 0xFF); 285 | *out_tail++ = ((data_len >> 8) & 0xFF); 286 | *out_tail++ = (data_len & 0xFF); 287 | 288 | // write data 289 | memcpy(out_tail, data, data_len); 290 | free(data); 291 | 292 | wi_on_debug(self, "wi.send_packet", out_head, length); 293 | wi_status not_sent = self->send_packet(self, out_head, length); 294 | free(out_head); 295 | if (not_sent) { 296 | break; 297 | } 298 | 299 | if (!is_partial) { 300 | ret = WI_SUCCESS; 301 | break; 302 | } 303 | } 304 | free(rpc_bin); 305 | return ret; 306 | } 307 | 308 | // 309 | // RECV 310 | // 311 | 312 | wi_status wi_parse_length(wi_t self, const char *buf, size_t *to_length) { 313 | if (!buf || !to_length) { 314 | return WI_ERROR; 315 | } 316 | *to_length = ( 317 | ((((unsigned char) buf[0]) & 0xFF) << 24) | 318 | ((((unsigned char) buf[1]) & 0xFF) << 16) | 319 | ((((unsigned char) buf[2]) & 0xFF) << 8) | 320 | (((unsigned char) buf[3]) & 0xFF)); 321 | if (MAX_BODY_LENGTH > 0 && *to_length > MAX_BODY_LENGTH) { 322 | #define TO_CHAR(c) ((c) >= ' ' && (c) < '~' ? (c) : '.') 323 | return self->on_error(self, "Invalid packet header " 324 | "0x%x%x%x%x == %c%c%c%c == %zd", 325 | buf[0], buf[1], buf[2], buf[3], 326 | TO_CHAR(buf[0]), TO_CHAR(buf[1]), 327 | TO_CHAR(buf[2]), TO_CHAR(buf[3]), 328 | *to_length); 329 | } 330 | return WI_SUCCESS; 331 | } 332 | 333 | wi_status wi_parse_plist(wi_t self, const char *from_buf, size_t length, 334 | plist_t *to_rpc_dict, bool *to_is_partial) { 335 | wi_private_t my = self->private_state; 336 | *to_is_partial = false; 337 | *to_rpc_dict = NULL; 338 | 339 | if (!my->partials_supported) { 340 | plist_from_bin(from_buf, length, to_rpc_dict); 341 | } else { 342 | plist_t wi_dict = NULL; 343 | plist_from_bin(from_buf, length, &wi_dict); 344 | if (!wi_dict) { 345 | return WI_ERROR; 346 | } 347 | plist_t wi_rpc = plist_dict_get_item(wi_dict, "WIRFinalMessageKey"); 348 | if (!wi_rpc) { 349 | wi_rpc = plist_dict_get_item(wi_dict, "WIRPartialMessageKey"); 350 | if (!wi_rpc) { 351 | return WI_ERROR; 352 | } 353 | *to_is_partial = true; 354 | } 355 | 356 | uint64_t rpc_len = 0; 357 | char *rpc_bin = NULL; 358 | plist_get_data_val(wi_rpc, &rpc_bin, &rpc_len); 359 | plist_free(wi_dict); // also frees wi_rpc 360 | if (!rpc_bin) { 361 | return WI_ERROR; 362 | } 363 | // assert rpc_len < MAX_RPC_LEN? 364 | 365 | size_t p_length = my->partial->tail - my->partial->head; 366 | if (*to_is_partial || p_length) { 367 | if (cb_ensure_capacity(my->partial, rpc_len)) { 368 | return self->on_error(self, "Out of memory"); 369 | } 370 | memcpy(my->partial->tail, rpc_bin, rpc_len); 371 | my->partial->tail += rpc_len; 372 | p_length += rpc_len; 373 | free(rpc_bin); 374 | if (*to_is_partial) { 375 | return WI_SUCCESS; 376 | } 377 | } 378 | 379 | if (p_length) { 380 | plist_from_bin(my->partial->head, (uint32_t)p_length, to_rpc_dict); 381 | cb_clear(my->partial); 382 | } else { 383 | plist_from_bin(rpc_bin, (uint32_t)rpc_len, to_rpc_dict); 384 | free(rpc_bin); 385 | } 386 | } 387 | 388 | return (*to_rpc_dict ? WI_SUCCESS : WI_ERROR); 389 | } 390 | 391 | wi_status wi_recv_packet(wi_t self, const char *packet, ssize_t length) { 392 | wi_on_debug(self, "wi.recv_packet", packet, length); 393 | 394 | size_t body_length = 0; 395 | plist_t rpc_dict = NULL; 396 | bool is_partial = false; 397 | if (!packet || length < 4 || wi_parse_length(self, packet, &body_length) || 398 | //TODO (body_length != length - 4) || 399 | wi_parse_plist(self, packet + 4, body_length, &rpc_dict, &is_partial)) { 400 | // invalid packet 401 | char *text = NULL; 402 | if (body_length != length - 4) { 403 | if (asprintf(&text, "size %zd != %zd - 4", body_length, length) < 0) { 404 | return self->on_error(self, "asprintf failed"); 405 | } 406 | } else { 407 | cb_asprint(&text, packet, length, 80, 50); 408 | } 409 | wi_status ret = self->on_error(self, "Invalid packet:\n%s\n", text); 410 | free(text); 411 | return ret; 412 | } 413 | 414 | if (is_partial) { 415 | return WI_SUCCESS; 416 | } 417 | wi_status ret = self->recv_plist(self, rpc_dict); 418 | plist_free(rpc_dict); 419 | return ret; 420 | } 421 | 422 | wi_status wi_recv_loop(wi_t self) { 423 | wi_private_t my = self->private_state; 424 | wi_status ret; 425 | const char *in_head = my->in->in_head; 426 | const char *in_tail = my->in->in_tail; 427 | while (1) { 428 | size_t in_length = in_tail - in_head; 429 | if (!my->has_length && in_length >= 4) { 430 | // can read body_length now 431 | size_t len; 432 | ret = wi_parse_length(self, in_head, &len); 433 | if (ret) { 434 | in_head += 4; 435 | break; 436 | } 437 | my->body_length = len; 438 | my->has_length = true; 439 | // don't advance in_head yet 440 | } else if (my->has_length && in_length >= my->body_length + 4) { 441 | // can read body now 442 | ret = self->recv_packet(self, in_head, my->body_length + 4); 443 | in_head += my->body_length + 4; 444 | my->has_length = false; 445 | my->body_length = 0; 446 | if (ret) { 447 | break; 448 | } 449 | } else { 450 | // need more input 451 | ret = WI_SUCCESS; 452 | break; 453 | } 454 | } 455 | my->in->in_head = in_head; 456 | return ret; 457 | } 458 | 459 | wi_status wi_on_recv(wi_t self, const char *buf, ssize_t length) { 460 | wi_private_t my = self->private_state; 461 | if (length < 0) { 462 | return WI_ERROR; 463 | } else if (length == 0) { 464 | return WI_SUCCESS; 465 | } 466 | wi_on_debug(self, "wi.recv", buf, length); 467 | if (cb_begin_input(my->in, buf, length)) { 468 | return self->on_error(self, "begin_input buffer error"); 469 | } 470 | wi_status ret = wi_recv_loop(self); 471 | if (cb_end_input(my->in)) { 472 | return self->on_error(self, "end_input buffer error"); 473 | } 474 | return ret; 475 | } 476 | 477 | // 478 | // STRUCTS 479 | // 480 | 481 | void wi_private_free(wi_private_t my) { 482 | if (my) { 483 | cb_free(my->in); 484 | cb_free(my->partial); 485 | memset(my, 0, sizeof(struct wi_private)); 486 | free(my); 487 | } 488 | } 489 | wi_private_t wi_private_new() { 490 | wi_private_t my = (wi_private_t)malloc(sizeof( 491 | struct wi_private)); 492 | if (my) { 493 | memset(my, 0, sizeof(struct wi_private)); 494 | my->in = cb_new(); 495 | my->partial = cb_new(); 496 | if (!my->in || !my->partial) { 497 | wi_private_free(my); 498 | return NULL; 499 | } 500 | } 501 | return my; 502 | } 503 | 504 | 505 | void wi_free(wi_t self) { 506 | if (self) { 507 | wi_private_free(self->private_state); 508 | memset(self, 0, sizeof(struct wi_struct)); 509 | free(self); 510 | } 511 | } 512 | wi_t wi_new(bool partials_supported) { 513 | wi_t self = (wi_t)malloc(sizeof(struct wi_struct)); 514 | if (!self) { 515 | return NULL; 516 | } 517 | memset(self, 0, sizeof(struct wi_struct)); 518 | self->on_recv = wi_on_recv; 519 | self->send_plist = wi_send_plist; 520 | self->recv_packet = wi_recv_packet; 521 | self->on_error = wi_on_error; 522 | self->private_state = wi_private_new(); 523 | if (!self->private_state) { 524 | wi_free(self); 525 | return NULL; 526 | } 527 | self->private_state->partials_supported = partials_supported; 528 | return self; 529 | } 530 | -------------------------------------------------------------------------------- /src/sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * FIPS-180-1 compliant SHA-1 implementation 3 | * 4 | * Copyright (C) 2006-2010, Brainspark B.V. 5 | * 6 | * This file is part of PolarSSL (http://www.polarssl.org) 7 | * Lead Maintainer: Paul Bakker 8 | * 9 | * All rights reserved. 10 | * 11 | * This program is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU General Public License as published by 13 | * the Free Software Foundation; either version 2 of the License, or 14 | * (at your option) any later version. 15 | * 16 | * This program is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU General Public License along 22 | * with this program; if not, write to the Free Software Foundation, Inc., 23 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 24 | */ 25 | /* 26 | * The SHA-1 standard was published by NIST in 1993. 27 | * 28 | * http://www.itl.nist.gov/fipspubs/fip180-1.htm 29 | */ 30 | 31 | // BEGIN CHANGES off of revision=1398 11/01/2012 32 | //#include "polarssl/config.h" 33 | #define POLARSSL_SHA1_C 34 | // END CHANGES 35 | 36 | #if defined(POLARSSL_SHA1_C) 37 | 38 | #ifdef HAVE_CONFIG_H 39 | #include 40 | #endif 41 | 42 | //#include "polarssl/sha1.h" 43 | #include "sha1.h" 44 | // END CHANGES 45 | 46 | #if defined(POLARSSL_FS_IO) || defined(POLARSSL_SELF_TEST) 47 | #include 48 | #endif 49 | 50 | /* 51 | * 32-bit integer manipulation macros (big endian) 52 | */ 53 | #ifndef GET_UINT32_BE 54 | #define GET_UINT32_BE(n,b,i) \ 55 | { \ 56 | (n) = ( (uint32_t) (b)[(i) ] << 24 ) \ 57 | | ( (uint32_t) (b)[(i) + 1] << 16 ) \ 58 | | ( (uint32_t) (b)[(i) + 2] << 8 ) \ 59 | | ( (uint32_t) (b)[(i) + 3] ); \ 60 | } 61 | #endif 62 | 63 | #ifndef PUT_UINT32_BE 64 | #define PUT_UINT32_BE(n,b,i) \ 65 | { \ 66 | (b)[(i) ] = (unsigned char) ( (n) >> 24 ); \ 67 | (b)[(i) + 1] = (unsigned char) ( (n) >> 16 ); \ 68 | (b)[(i) + 2] = (unsigned char) ( (n) >> 8 ); \ 69 | (b)[(i) + 3] = (unsigned char) ( (n) ); \ 70 | } 71 | #endif 72 | 73 | /* 74 | * SHA-1 context setup 75 | */ 76 | void sha1_starts( sha1_context *ctx ) 77 | { 78 | ctx->total[0] = 0; 79 | ctx->total[1] = 0; 80 | 81 | ctx->state[0] = 0x67452301; 82 | ctx->state[1] = 0xEFCDAB89; 83 | ctx->state[2] = 0x98BADCFE; 84 | ctx->state[3] = 0x10325476; 85 | ctx->state[4] = 0xC3D2E1F0; 86 | } 87 | 88 | static void sha1_process( sha1_context *ctx, const unsigned char data[64] ) 89 | { 90 | uint32_t temp, W[16], A, B, C, D, E; 91 | 92 | GET_UINT32_BE( W[ 0], data, 0 ); 93 | GET_UINT32_BE( W[ 1], data, 4 ); 94 | GET_UINT32_BE( W[ 2], data, 8 ); 95 | GET_UINT32_BE( W[ 3], data, 12 ); 96 | GET_UINT32_BE( W[ 4], data, 16 ); 97 | GET_UINT32_BE( W[ 5], data, 20 ); 98 | GET_UINT32_BE( W[ 6], data, 24 ); 99 | GET_UINT32_BE( W[ 7], data, 28 ); 100 | GET_UINT32_BE( W[ 8], data, 32 ); 101 | GET_UINT32_BE( W[ 9], data, 36 ); 102 | GET_UINT32_BE( W[10], data, 40 ); 103 | GET_UINT32_BE( W[11], data, 44 ); 104 | GET_UINT32_BE( W[12], data, 48 ); 105 | GET_UINT32_BE( W[13], data, 52 ); 106 | GET_UINT32_BE( W[14], data, 56 ); 107 | GET_UINT32_BE( W[15], data, 60 ); 108 | 109 | #define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n))) 110 | 111 | #define R(t) \ 112 | ( \ 113 | temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \ 114 | W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \ 115 | ( W[t & 0x0F] = S(temp,1) ) \ 116 | ) 117 | 118 | #define P(a,b,c,d,e,x) \ 119 | { \ 120 | e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \ 121 | } 122 | 123 | A = ctx->state[0]; 124 | B = ctx->state[1]; 125 | C = ctx->state[2]; 126 | D = ctx->state[3]; 127 | E = ctx->state[4]; 128 | 129 | #define F(x,y,z) (z ^ (x & (y ^ z))) 130 | #define K 0x5A827999 131 | 132 | P( A, B, C, D, E, W[0] ); 133 | P( E, A, B, C, D, W[1] ); 134 | P( D, E, A, B, C, W[2] ); 135 | P( C, D, E, A, B, W[3] ); 136 | P( B, C, D, E, A, W[4] ); 137 | P( A, B, C, D, E, W[5] ); 138 | P( E, A, B, C, D, W[6] ); 139 | P( D, E, A, B, C, W[7] ); 140 | P( C, D, E, A, B, W[8] ); 141 | P( B, C, D, E, A, W[9] ); 142 | P( A, B, C, D, E, W[10] ); 143 | P( E, A, B, C, D, W[11] ); 144 | P( D, E, A, B, C, W[12] ); 145 | P( C, D, E, A, B, W[13] ); 146 | P( B, C, D, E, A, W[14] ); 147 | P( A, B, C, D, E, W[15] ); 148 | P( E, A, B, C, D, R(16) ); 149 | P( D, E, A, B, C, R(17) ); 150 | P( C, D, E, A, B, R(18) ); 151 | P( B, C, D, E, A, R(19) ); 152 | 153 | #undef K 154 | #undef F 155 | 156 | #define F(x,y,z) (x ^ y ^ z) 157 | #define K 0x6ED9EBA1 158 | 159 | P( A, B, C, D, E, R(20) ); 160 | P( E, A, B, C, D, R(21) ); 161 | P( D, E, A, B, C, R(22) ); 162 | P( C, D, E, A, B, R(23) ); 163 | P( B, C, D, E, A, R(24) ); 164 | P( A, B, C, D, E, R(25) ); 165 | P( E, A, B, C, D, R(26) ); 166 | P( D, E, A, B, C, R(27) ); 167 | P( C, D, E, A, B, R(28) ); 168 | P( B, C, D, E, A, R(29) ); 169 | P( A, B, C, D, E, R(30) ); 170 | P( E, A, B, C, D, R(31) ); 171 | P( D, E, A, B, C, R(32) ); 172 | P( C, D, E, A, B, R(33) ); 173 | P( B, C, D, E, A, R(34) ); 174 | P( A, B, C, D, E, R(35) ); 175 | P( E, A, B, C, D, R(36) ); 176 | P( D, E, A, B, C, R(37) ); 177 | P( C, D, E, A, B, R(38) ); 178 | P( B, C, D, E, A, R(39) ); 179 | 180 | #undef K 181 | #undef F 182 | 183 | #define F(x,y,z) ((x & y) | (z & (x | y))) 184 | #define K 0x8F1BBCDC 185 | 186 | P( A, B, C, D, E, R(40) ); 187 | P( E, A, B, C, D, R(41) ); 188 | P( D, E, A, B, C, R(42) ); 189 | P( C, D, E, A, B, R(43) ); 190 | P( B, C, D, E, A, R(44) ); 191 | P( A, B, C, D, E, R(45) ); 192 | P( E, A, B, C, D, R(46) ); 193 | P( D, E, A, B, C, R(47) ); 194 | P( C, D, E, A, B, R(48) ); 195 | P( B, C, D, E, A, R(49) ); 196 | P( A, B, C, D, E, R(50) ); 197 | P( E, A, B, C, D, R(51) ); 198 | P( D, E, A, B, C, R(52) ); 199 | P( C, D, E, A, B, R(53) ); 200 | P( B, C, D, E, A, R(54) ); 201 | P( A, B, C, D, E, R(55) ); 202 | P( E, A, B, C, D, R(56) ); 203 | P( D, E, A, B, C, R(57) ); 204 | P( C, D, E, A, B, R(58) ); 205 | P( B, C, D, E, A, R(59) ); 206 | 207 | #undef K 208 | #undef F 209 | 210 | #define F(x,y,z) (x ^ y ^ z) 211 | #define K 0xCA62C1D6 212 | 213 | P( A, B, C, D, E, R(60) ); 214 | P( E, A, B, C, D, R(61) ); 215 | P( D, E, A, B, C, R(62) ); 216 | P( C, D, E, A, B, R(63) ); 217 | P( B, C, D, E, A, R(64) ); 218 | P( A, B, C, D, E, R(65) ); 219 | P( E, A, B, C, D, R(66) ); 220 | P( D, E, A, B, C, R(67) ); 221 | P( C, D, E, A, B, R(68) ); 222 | P( B, C, D, E, A, R(69) ); 223 | P( A, B, C, D, E, R(70) ); 224 | P( E, A, B, C, D, R(71) ); 225 | P( D, E, A, B, C, R(72) ); 226 | P( C, D, E, A, B, R(73) ); 227 | P( B, C, D, E, A, R(74) ); 228 | P( A, B, C, D, E, R(75) ); 229 | P( E, A, B, C, D, R(76) ); 230 | P( D, E, A, B, C, R(77) ); 231 | P( C, D, E, A, B, R(78) ); 232 | P( B, C, D, E, A, R(79) ); 233 | 234 | #undef K 235 | #undef F 236 | 237 | ctx->state[0] += A; 238 | ctx->state[1] += B; 239 | ctx->state[2] += C; 240 | ctx->state[3] += D; 241 | ctx->state[4] += E; 242 | } 243 | 244 | /* 245 | * SHA-1 process buffer 246 | */ 247 | void sha1_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) 248 | { 249 | size_t fill; 250 | uint32_t left; 251 | 252 | if( ilen <= 0 ) 253 | return; 254 | 255 | left = ctx->total[0] & 0x3F; 256 | fill = 64 - left; 257 | 258 | ctx->total[0] += (uint32_t) ilen; 259 | ctx->total[0] &= 0xFFFFFFFF; 260 | 261 | if( ctx->total[0] < (uint32_t) ilen ) 262 | ctx->total[1]++; 263 | 264 | if( left && ilen >= fill ) 265 | { 266 | memcpy( (void *) (ctx->buffer + left), 267 | (void *) input, fill ); 268 | sha1_process( ctx, ctx->buffer ); 269 | input += fill; 270 | ilen -= fill; 271 | left = 0; 272 | } 273 | 274 | while( ilen >= 64 ) 275 | { 276 | sha1_process( ctx, input ); 277 | input += 64; 278 | ilen -= 64; 279 | } 280 | 281 | if( ilen > 0 ) 282 | { 283 | memcpy( (void *) (ctx->buffer + left), 284 | (void *) input, ilen ); 285 | } 286 | } 287 | 288 | static const unsigned char sha1_padding[64] = 289 | { 290 | 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 291 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 292 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 293 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 294 | }; 295 | 296 | /* 297 | * SHA-1 final digest 298 | */ 299 | void sha1_finish( sha1_context *ctx, unsigned char output[20] ) 300 | { 301 | uint32_t last, padn; 302 | uint32_t high, low; 303 | unsigned char msglen[8]; 304 | 305 | high = ( ctx->total[0] >> 29 ) 306 | | ( ctx->total[1] << 3 ); 307 | low = ( ctx->total[0] << 3 ); 308 | 309 | PUT_UINT32_BE( high, msglen, 0 ); 310 | PUT_UINT32_BE( low, msglen, 4 ); 311 | 312 | last = ctx->total[0] & 0x3F; 313 | padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last ); 314 | 315 | sha1_update( ctx, (unsigned char *) sha1_padding, padn ); 316 | sha1_update( ctx, msglen, 8 ); 317 | 318 | PUT_UINT32_BE( ctx->state[0], output, 0 ); 319 | PUT_UINT32_BE( ctx->state[1], output, 4 ); 320 | PUT_UINT32_BE( ctx->state[2], output, 8 ); 321 | PUT_UINT32_BE( ctx->state[3], output, 12 ); 322 | PUT_UINT32_BE( ctx->state[4], output, 16 ); 323 | } 324 | 325 | /* 326 | * output = SHA-1( input buffer ) 327 | */ 328 | void sha1( const unsigned char *input, size_t ilen, unsigned char output[20] ) 329 | { 330 | sha1_context ctx; 331 | 332 | sha1_starts( &ctx ); 333 | sha1_update( &ctx, input, ilen ); 334 | sha1_finish( &ctx, output ); 335 | 336 | memset( &ctx, 0, sizeof( sha1_context ) ); 337 | } 338 | 339 | #if defined(POLARSSL_FS_IO) 340 | /* 341 | * output = SHA-1( file contents ) 342 | */ 343 | int sha1_file( const char *path, unsigned char output[20] ) 344 | { 345 | FILE *f; 346 | size_t n; 347 | sha1_context ctx; 348 | unsigned char buf[1024]; 349 | 350 | if( ( f = fopen( path, "rb" ) ) == NULL ) 351 | return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); 352 | 353 | sha1_starts( &ctx ); 354 | 355 | while( ( n = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) 356 | sha1_update( &ctx, buf, n ); 357 | 358 | sha1_finish( &ctx, output ); 359 | 360 | memset( &ctx, 0, sizeof( sha1_context ) ); 361 | 362 | if( ferror( f ) != 0 ) 363 | { 364 | fclose( f ); 365 | return( POLARSSL_ERR_SHA1_FILE_IO_ERROR ); 366 | } 367 | 368 | fclose( f ); 369 | return( 0 ); 370 | } 371 | #endif /* POLARSSL_FS_IO */ 372 | 373 | /* 374 | * SHA-1 HMAC context setup 375 | */ 376 | void sha1_hmac_starts( sha1_context *ctx, const unsigned char *key, size_t keylen ) 377 | { 378 | size_t i; 379 | unsigned char sum[20]; 380 | 381 | if( keylen > 64 ) 382 | { 383 | sha1( key, keylen, sum ); 384 | keylen = 20; 385 | key = sum; 386 | } 387 | 388 | memset( ctx->ipad, 0x36, 64 ); 389 | memset( ctx->opad, 0x5C, 64 ); 390 | 391 | for( i = 0; i < keylen; i++ ) 392 | { 393 | ctx->ipad[i] = (unsigned char)( ctx->ipad[i] ^ key[i] ); 394 | ctx->opad[i] = (unsigned char)( ctx->opad[i] ^ key[i] ); 395 | } 396 | 397 | sha1_starts( ctx ); 398 | sha1_update( ctx, ctx->ipad, 64 ); 399 | 400 | memset( sum, 0, sizeof( sum ) ); 401 | } 402 | 403 | /* 404 | * SHA-1 HMAC process buffer 405 | */ 406 | void sha1_hmac_update( sha1_context *ctx, const unsigned char *input, size_t ilen ) 407 | { 408 | sha1_update( ctx, input, ilen ); 409 | } 410 | 411 | /* 412 | * SHA-1 HMAC final digest 413 | */ 414 | void sha1_hmac_finish( sha1_context *ctx, unsigned char output[20] ) 415 | { 416 | unsigned char tmpbuf[20]; 417 | 418 | sha1_finish( ctx, tmpbuf ); 419 | sha1_starts( ctx ); 420 | sha1_update( ctx, ctx->opad, 64 ); 421 | sha1_update( ctx, tmpbuf, 20 ); 422 | sha1_finish( ctx, output ); 423 | 424 | memset( tmpbuf, 0, sizeof( tmpbuf ) ); 425 | } 426 | 427 | /* 428 | * SHA1 HMAC context reset 429 | */ 430 | void sha1_hmac_reset( sha1_context *ctx ) 431 | { 432 | sha1_starts( ctx ); 433 | sha1_update( ctx, ctx->ipad, 64 ); 434 | } 435 | 436 | /* 437 | * output = HMAC-SHA-1( hmac key, input buffer ) 438 | */ 439 | void sha1_hmac( const unsigned char *key, size_t keylen, 440 | const unsigned char *input, size_t ilen, 441 | unsigned char output[20] ) 442 | { 443 | sha1_context ctx; 444 | 445 | sha1_hmac_starts( &ctx, key, keylen ); 446 | sha1_hmac_update( &ctx, input, ilen ); 447 | sha1_hmac_finish( &ctx, output ); 448 | 449 | memset( &ctx, 0, sizeof( sha1_context ) ); 450 | } 451 | 452 | #if defined(POLARSSL_SELF_TEST) 453 | /* 454 | * FIPS-180-1 test vectors 455 | */ 456 | static unsigned char sha1_test_buf[3][57] = 457 | { 458 | { "abc" }, 459 | { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" }, 460 | { "" } 461 | }; 462 | 463 | static const int sha1_test_buflen[3] = 464 | { 465 | 3, 56, 1000 466 | }; 467 | 468 | static const unsigned char sha1_test_sum[3][20] = 469 | { 470 | { 0xA9, 0x99, 0x3E, 0x36, 0x47, 0x06, 0x81, 0x6A, 0xBA, 0x3E, 471 | 0x25, 0x71, 0x78, 0x50, 0xC2, 0x6C, 0x9C, 0xD0, 0xD8, 0x9D }, 472 | { 0x84, 0x98, 0x3E, 0x44, 0x1C, 0x3B, 0xD2, 0x6E, 0xBA, 0xAE, 473 | 0x4A, 0xA1, 0xF9, 0x51, 0x29, 0xE5, 0xE5, 0x46, 0x70, 0xF1 }, 474 | { 0x34, 0xAA, 0x97, 0x3C, 0xD4, 0xC4, 0xDA, 0xA4, 0xF6, 0x1E, 475 | 0xEB, 0x2B, 0xDB, 0xAD, 0x27, 0x31, 0x65, 0x34, 0x01, 0x6F } 476 | }; 477 | 478 | /* 479 | * RFC 2202 test vectors 480 | */ 481 | static unsigned char sha1_hmac_test_key[7][26] = 482 | { 483 | { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" 484 | "\x0B\x0B\x0B\x0B" }, 485 | { "Jefe" }, 486 | { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" 487 | "\xAA\xAA\xAA\xAA" }, 488 | { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" 489 | "\x11\x12\x13\x14\x15\x16\x17\x18\x19" }, 490 | { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" 491 | "\x0C\x0C\x0C\x0C" }, 492 | { "" }, /* 0xAA 80 times */ 493 | { "" } 494 | }; 495 | 496 | static const int sha1_hmac_test_keylen[7] = 497 | { 498 | 20, 4, 20, 25, 20, 80, 80 499 | }; 500 | 501 | static unsigned char sha1_hmac_test_buf[7][74] = 502 | { 503 | { "Hi There" }, 504 | { "what do ya want for nothing?" }, 505 | { "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" 506 | "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" 507 | "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" 508 | "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" 509 | "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" }, 510 | { "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 511 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 512 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 513 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" 514 | "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" }, 515 | { "Test With Truncation" }, 516 | { "Test Using Larger Than Block-Size Key - Hash Key First" }, 517 | { "Test Using Larger Than Block-Size Key and Larger" 518 | " Than One Block-Size Data" } 519 | }; 520 | 521 | static const int sha1_hmac_test_buflen[7] = 522 | { 523 | 8, 28, 50, 50, 20, 54, 73 524 | }; 525 | 526 | static const unsigned char sha1_hmac_test_sum[7][20] = 527 | { 528 | { 0xB6, 0x17, 0x31, 0x86, 0x55, 0x05, 0x72, 0x64, 0xE2, 0x8B, 529 | 0xC0, 0xB6, 0xFB, 0x37, 0x8C, 0x8E, 0xF1, 0x46, 0xBE, 0x00 }, 530 | { 0xEF, 0xFC, 0xDF, 0x6A, 0xE5, 0xEB, 0x2F, 0xA2, 0xD2, 0x74, 531 | 0x16, 0xD5, 0xF1, 0x84, 0xDF, 0x9C, 0x25, 0x9A, 0x7C, 0x79 }, 532 | { 0x12, 0x5D, 0x73, 0x42, 0xB9, 0xAC, 0x11, 0xCD, 0x91, 0xA3, 533 | 0x9A, 0xF4, 0x8A, 0xA1, 0x7B, 0x4F, 0x63, 0xF1, 0x75, 0xD3 }, 534 | { 0x4C, 0x90, 0x07, 0xF4, 0x02, 0x62, 0x50, 0xC6, 0xBC, 0x84, 535 | 0x14, 0xF9, 0xBF, 0x50, 0xC8, 0x6C, 0x2D, 0x72, 0x35, 0xDA }, 536 | { 0x4C, 0x1A, 0x03, 0x42, 0x4B, 0x55, 0xE0, 0x7F, 0xE7, 0xF2, 537 | 0x7B, 0xE1 }, 538 | { 0xAA, 0x4A, 0xE5, 0xE1, 0x52, 0x72, 0xD0, 0x0E, 0x95, 0x70, 539 | 0x56, 0x37, 0xCE, 0x8A, 0x3B, 0x55, 0xED, 0x40, 0x21, 0x12 }, 540 | { 0xE8, 0xE9, 0x9D, 0x0F, 0x45, 0x23, 0x7D, 0x78, 0x6D, 0x6B, 541 | 0xBA, 0xA7, 0x96, 0x5C, 0x78, 0x08, 0xBB, 0xFF, 0x1A, 0x91 } 542 | }; 543 | 544 | /* 545 | * Checkup routine 546 | */ 547 | int sha1_self_test( int verbose ) 548 | { 549 | int i, j, buflen; 550 | unsigned char buf[1024]; 551 | unsigned char sha1sum[20]; 552 | sha1_context ctx; 553 | 554 | /* 555 | * SHA-1 556 | */ 557 | for( i = 0; i < 3; i++ ) 558 | { 559 | if( verbose != 0 ) 560 | printf( " SHA-1 test #%d: ", i + 1 ); 561 | 562 | sha1_starts( &ctx ); 563 | 564 | if( i == 2 ) 565 | { 566 | memset( buf, 'a', buflen = 1000 ); 567 | 568 | for( j = 0; j < 1000; j++ ) 569 | sha1_update( &ctx, buf, buflen ); 570 | } 571 | else 572 | sha1_update( &ctx, sha1_test_buf[i], 573 | sha1_test_buflen[i] ); 574 | 575 | sha1_finish( &ctx, sha1sum ); 576 | 577 | if( memcmp( sha1sum, sha1_test_sum[i], 20 ) != 0 ) 578 | { 579 | if( verbose != 0 ) 580 | printf( "failed\n" ); 581 | 582 | return( 1 ); 583 | } 584 | 585 | if( verbose != 0 ) 586 | printf( "passed\n" ); 587 | } 588 | 589 | if( verbose != 0 ) 590 | printf( "\n" ); 591 | 592 | for( i = 0; i < 7; i++ ) 593 | { 594 | if( verbose != 0 ) 595 | printf( " HMAC-SHA-1 test #%d: ", i + 1 ); 596 | 597 | if( i == 5 || i == 6 ) 598 | { 599 | memset( buf, '\xAA', buflen = 80 ); 600 | sha1_hmac_starts( &ctx, buf, buflen ); 601 | } 602 | else 603 | sha1_hmac_starts( &ctx, sha1_hmac_test_key[i], 604 | sha1_hmac_test_keylen[i] ); 605 | 606 | sha1_hmac_update( &ctx, sha1_hmac_test_buf[i], 607 | sha1_hmac_test_buflen[i] ); 608 | 609 | sha1_hmac_finish( &ctx, sha1sum ); 610 | 611 | buflen = ( i == 4 ) ? 12 : 20; 612 | 613 | if( memcmp( sha1sum, sha1_hmac_test_sum[i], buflen ) != 0 ) 614 | { 615 | if( verbose != 0 ) 616 | printf( "failed\n" ); 617 | 618 | return( 1 ); 619 | } 620 | 621 | if( verbose != 0 ) 622 | printf( "passed\n" ); 623 | } 624 | 625 | if( verbose != 0 ) 626 | printf( "\n" ); 627 | 628 | return( 0 ); 629 | } 630 | 631 | #endif 632 | 633 | #endif 634 | -------------------------------------------------------------------------------- /src/socket_manager.c: -------------------------------------------------------------------------------- 1 | // Google BSD license https://developers.google.com/google-bsd-license 2 | // Copyright 2012 Google Inc. wrightt@google.com 3 | 4 | #ifdef HAVE_CONFIG_H 5 | #include 6 | #endif 7 | 8 | #define _GNU_SOURCE 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #ifdef WIN32 19 | #include 20 | #include 21 | #else 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #endif 30 | 31 | #include 32 | 33 | #include "char_buffer.h" 34 | #include "socket_manager.h" 35 | #include "hash_table.h" 36 | #include "strndup.h" 37 | 38 | #if defined(__MACH__) || defined(WIN32) 39 | #define SIZEOF_FD_SET sizeof(struct fd_set) 40 | #define RECV_FLAGS 0 41 | #else 42 | #define SIZEOF_FD_SET sizeof(fd_set) 43 | #define RECV_FLAGS MSG_DONTWAIT 44 | #endif 45 | 46 | struct sm_private { 47 | struct timeval timeout; 48 | // fds: 49 | fd_set *all_fds; 50 | int max_fd; // max fd in all_fds 51 | // subsets of all_fds: 52 | fd_set *server_fds; // can on_accept, i.e. "is_server" 53 | fd_set *send_fds; // blocked sends, same as fd_to_sendq.keys 54 | fd_set *recv_fds; // can recv, same as all_fds - sendq.recv_fd's 55 | // fd to ssl_session 56 | ht_t fd_to_ssl; 57 | // fd to on_* callback 58 | ht_t fd_to_value; 59 | // fd to blocked sm_sendq_t, often empty 60 | ht_t fd_to_sendq; 61 | // temp recv buffer, for use in sm_select: 62 | char *tmp_buf; 63 | size_t tmp_buf_length; 64 | // temp fd sets, for use in sm_select: 65 | fd_set *tmp_send_fds; 66 | fd_set *tmp_recv_fds; 67 | fd_set *tmp_fail_fds; 68 | // current sm_select on_recv fd, only set when in sm_select loop 69 | int curr_recv_fd; 70 | }; 71 | 72 | struct sm_sendq; 73 | typedef struct sm_sendq *sm_sendq_t; 74 | struct sm_sendq { 75 | void *value; // for on_sent 76 | int recv_fd; // the my->recv_fd that caused this blocked send 77 | char *begin; // sm_send data 78 | char *head; 79 | char *tail; // begin + sm_send length 80 | sm_sendq_t next; 81 | }; 82 | sm_sendq_t sm_sendq_new(int recv_fd, void *value, const char *data, 83 | size_t length); 84 | void sm_sendq_free(sm_sendq_t sendq); 85 | 86 | 87 | int sm_listen(int port) { 88 | int fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 89 | #ifdef WIN32 90 | if (fd == INVALID_SOCKET) { 91 | fprintf(stderr, "socket_manager: socket function failed with\ 92 | error %d\n", WSAGetLastError()); 93 | return -1; 94 | } 95 | struct sockaddr_in local; 96 | local.sin_family = AF_INET; 97 | local.sin_addr.s_addr = INADDR_ANY; 98 | local.sin_port = htons(port); 99 | int ra = 1; 100 | u_long nb = 1; 101 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&ra, 102 | sizeof(ra)) == SOCKET_ERROR || 103 | ioctlsocket(fd, FIONBIO, &nb) || 104 | bind(fd, (SOCKADDR *)&local, sizeof(local)) == SOCKET_ERROR || 105 | listen(fd, 5)) { 106 | fprintf(stderr, "socket_manager: bind failed with\ 107 | error %d\n", WSAGetLastError()); 108 | closesocket(fd); 109 | return -1; 110 | } 111 | #else 112 | if (fd < 0) { 113 | return -1; 114 | } 115 | int opts = fcntl(fd, F_GETFL); 116 | struct sockaddr_in local; 117 | local.sin_family = AF_INET; 118 | local.sin_addr.s_addr = INADDR_ANY; 119 | local.sin_port = htons(port); 120 | int ra = 1; 121 | int nb = 1; 122 | if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&ra,sizeof(ra)) < 0 || 123 | opts < 0 || 124 | ioctl(fd, FIONBIO, (char *)&nb) < 0 || 125 | bind(fd, (struct sockaddr*)&local, sizeof(local)) < 0 || 126 | listen(fd, 5)) { 127 | close(fd); 128 | return -1; 129 | } 130 | #endif 131 | return fd; 132 | } 133 | 134 | #ifndef WIN32 135 | int sm_connect_unix(const char *filename) { 136 | struct sockaddr_un name; 137 | int sfd = -1; 138 | struct stat fst; 139 | 140 | if (stat(filename, &fst) != 0 || !S_ISSOCK(fst.st_mode)) { 141 | fprintf(stderr, "File '%s' is not a socket: %s\n", filename, 142 | strerror(errno)); 143 | return -1; 144 | } 145 | 146 | if ((sfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { 147 | perror("create socket failed"); 148 | return -1; 149 | } 150 | 151 | int opts = fcntl(sfd, F_GETFL); 152 | if (fcntl(sfd, F_SETFL, (opts | O_NONBLOCK)) < 0) { 153 | perror("failed to set socket to non-blocking"); 154 | return -1; 155 | } 156 | 157 | name.sun_family = AF_UNIX; 158 | strncpy(name.sun_path, filename, sizeof(name.sun_path) - 1); 159 | 160 | if (connect(sfd, (struct sockaddr*)&name, sizeof(name)) < 0) { 161 | close(sfd); 162 | perror("connect failed"); 163 | return -1; 164 | } 165 | 166 | return sfd; 167 | } 168 | #endif 169 | 170 | int sm_connect_tcp(const char *hostname, int port) { 171 | struct addrinfo hints; 172 | memset(&hints, 0, sizeof(hints)); 173 | hints.ai_family = PF_UNSPEC; 174 | hints.ai_socktype = SOCK_STREAM; 175 | hints.ai_protocol = IPPROTO_TCP; 176 | struct addrinfo *res0; 177 | char *port_str = NULL; 178 | if (asprintf(&port_str, "%d", port) < 0) { 179 | return -1; // asprintf failed 180 | } 181 | int ret = getaddrinfo(hostname, port_str, &hints, &res0); 182 | free(port_str); 183 | if (ret) { 184 | perror("Unknown host"); 185 | return (ret < 0 ? ret : -1); 186 | } 187 | ret = -1; 188 | int fd = 0; 189 | struct addrinfo *res; 190 | for (res = res0; res; res = res->ai_next) { 191 | #ifdef WIN32 192 | if (fd != INVALID_SOCKET) { 193 | closesocket(fd); 194 | } 195 | fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 196 | if (fd == INVALID_SOCKET) { 197 | continue; 198 | } 199 | u_long nb = 1; 200 | if (ioctlsocket(fd, FIONBIO, &nb) || 201 | (connect(fd, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR && 202 | WSAGetLastError() != WSAEWOULDBLOCK && 203 | WSAGetLastError() != WSAEINPROGRESS)) { 204 | continue; 205 | } 206 | 207 | struct timeval to; 208 | to.tv_sec = 0; 209 | to.tv_usec= 500*1000; 210 | fd_set write_fds; 211 | FD_ZERO(&write_fds); 212 | FD_SET(fd, &write_fds); 213 | 214 | if (select(1, NULL, &write_fds, NULL, &to) < 1) { 215 | continue; 216 | } 217 | #else 218 | if (fd > 0) { 219 | close(fd); 220 | } 221 | fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); 222 | if (fd < 0) { 223 | continue; 224 | } 225 | // try non-blocking connect, usually succeeds even if unreachable 226 | int opts = fcntl(fd, F_GETFL); 227 | if (opts < 0 || 228 | fcntl(fd, F_SETFL, (opts | O_NONBLOCK)) < 0 || 229 | ((connect(fd, res->ai_addr, res->ai_addrlen) < 0) == 230 | (errno != EINPROGRESS))) { 231 | continue; 232 | } 233 | // try blocking select to verify its reachable 234 | struct timeval to; 235 | to.tv_sec = 0; 236 | to.tv_usec= 500*1000; // arbitrary 237 | fd_set error_fds; 238 | FD_ZERO(&error_fds); 239 | FD_SET(fd, &error_fds); 240 | if (fcntl(fd, F_SETFL, opts) < 0) { 241 | continue; 242 | } 243 | int is_error = select(fd + 1, &error_fds, NULL, NULL, &to); 244 | if (is_error) { 245 | continue; 246 | } 247 | // success! set back to non-blocking and return 248 | if (fcntl(fd, F_SETFL, (opts | O_NONBLOCK)) < 0) { 249 | continue; 250 | } 251 | #endif 252 | ret = fd; 253 | break; 254 | } 255 | #ifdef WIN32 256 | if (fd != INVALID_SOCKET && ret <= 0) { 257 | closesocket(fd); 258 | } 259 | #else 260 | if (fd > 0 && ret <= 0) { 261 | close(fd); 262 | } 263 | #endif 264 | freeaddrinfo(res0); 265 | return ret; 266 | } 267 | 268 | int sm_connect(const char *socket_addr) { 269 | if (strncmp(socket_addr, "unix:", 5) == 0) { 270 | #ifdef WIN32 271 | return -1; 272 | #else 273 | return sm_connect_unix(socket_addr + 5); 274 | #endif 275 | } else { 276 | const char *s_port = strrchr(socket_addr, ':'); 277 | int port = 0; 278 | 279 | if (s_port) { 280 | port = strtol(s_port + 1, NULL, 0); 281 | } 282 | 283 | if (port <= 0) { 284 | return -1; 285 | } 286 | 287 | size_t host_len = s_port - socket_addr; 288 | char *host = strndup(socket_addr, host_len); 289 | 290 | int ret = sm_connect_tcp(host, port); 291 | free(host); 292 | return ret; 293 | } 294 | } 295 | 296 | 297 | sm_status sm_on_debug(sm_t self, const char *format, ...) { 298 | if (self->is_debug && *self->is_debug) { 299 | va_list args; 300 | va_start(args, format); 301 | vfprintf(stdout, format, args); 302 | fprintf(stdout, "\n"); 303 | va_end(args); 304 | } 305 | return SM_SUCCESS; 306 | } 307 | 308 | sm_status sm_add_fd(sm_t self, int fd, void *ssl_session, void *value, 309 | bool is_server) { 310 | sm_private_t my = self->private_state; 311 | if (FD_ISSET(fd, my->all_fds)) { 312 | return SM_ERROR; 313 | } 314 | if (ht_put(my->fd_to_value, HT_KEY(fd), value)) { 315 | // The above FD_ISSET(..master..) should prevent this 316 | return SM_ERROR; 317 | } 318 | if (ssl_session != NULL && ht_put(my->fd_to_ssl, HT_KEY(fd), ssl_session)) { 319 | return SM_ERROR; 320 | } 321 | // is_server == getsockopt(..., SO_ACCEPTCONN, ...)? 322 | sm_on_debug(self, "ss.add%s_fd(%d)", (is_server ? "_server" : ""), fd); 323 | FD_SET(fd, my->all_fds); 324 | FD_CLR(fd, my->send_fds); // only set if blocked 325 | FD_SET(fd, my->recv_fds); 326 | FD_CLR(fd, my->tmp_send_fds); 327 | FD_CLR(fd, my->tmp_recv_fds); 328 | FD_CLR(fd, my->tmp_fail_fds); 329 | if (is_server) { 330 | FD_SET(fd, my->server_fds); 331 | } 332 | if (fd > my->max_fd) { 333 | my->max_fd = fd; 334 | } 335 | return SM_SUCCESS; 336 | } 337 | 338 | sm_status sm_remove_fd(sm_t self, int fd) { 339 | sm_private_t my = self->private_state; 340 | if (!FD_ISSET(fd, my->all_fds)) { 341 | return SM_ERROR; 342 | } 343 | SSL *ssl_session = (SSL *)ht_put(my->fd_to_ssl, HT_KEY(fd), NULL); 344 | if (ssl_session) { 345 | SSL_shutdown(ssl_session); 346 | SSL_free(ssl_session); 347 | } 348 | void *value = ht_put(my->fd_to_value, HT_KEY(fd), NULL); 349 | bool is_server = FD_ISSET(fd, my->server_fds); 350 | sm_on_debug(self, "ss.remove%s_fd(%d)", (is_server ? "_server" : ""), fd); 351 | sm_status ret = self->on_close(self, fd, value, is_server); 352 | #ifdef WIN32 353 | closesocket(fd); 354 | #else 355 | close(fd); 356 | #endif 357 | FD_CLR(fd, my->all_fds); 358 | if (is_server) { 359 | FD_CLR(fd, my->server_fds); 360 | } 361 | FD_CLR(fd, my->send_fds); 362 | FD_CLR(fd, my->recv_fds); 363 | FD_CLR(fd, my->tmp_send_fds); 364 | FD_CLR(fd, my->tmp_recv_fds); 365 | FD_CLR(fd, my->tmp_fail_fds); 366 | if (fd == my->max_fd) { 367 | while (my->max_fd >= 0 && !FD_ISSET(my->max_fd, my->all_fds)) { 368 | my->max_fd--; 369 | } 370 | } 371 | if (ht_size(my->fd_to_sendq)) { 372 | sm_sendq_t *qs = (sm_sendq_t *)ht_values(my->fd_to_sendq); 373 | sm_sendq_t *q; 374 | for (q = qs; *q; q++) { 375 | sm_sendq_t sendq = *q; 376 | while (sendq) { 377 | if (sendq->recv_fd == fd) { 378 | sendq->recv_fd = 0; 379 | // don't abort this blocked send, even though the "cause" has ended 380 | } 381 | sendq = sendq->next; 382 | } 383 | } 384 | free(qs); 385 | } 386 | return ret; 387 | } 388 | 389 | sm_status sm_send(sm_t self, int fd, const char *data, size_t length, 390 | void* value) { 391 | sm_private_t my = self->private_state; 392 | sm_sendq_t sendq = (sm_sendq_t)ht_get_value(my->fd_to_sendq, HT_KEY(fd)); 393 | const char *head = data; 394 | const char *tail = data + length; 395 | if (!sendq) { 396 | void *ssl_session = ht_get_value(my->fd_to_ssl, HT_KEY(fd)); 397 | // send as much as we can without blocking 398 | while (1) { 399 | ssize_t sent_bytes; 400 | if (ssl_session == NULL) { 401 | sent_bytes = send(fd, (void*)head, (tail - head), 0); 402 | if (sent_bytes <= 0) { 403 | #ifdef WIN32 404 | if (sent_bytes && WSAGetLastError() != WSAEWOULDBLOCK) { 405 | #else 406 | if (sent_bytes && errno != EWOULDBLOCK) { 407 | #endif 408 | sm_on_debug(self, "ss.failed fd=%d", fd); 409 | perror("send failed"); 410 | return SM_ERROR; 411 | } 412 | break; 413 | } 414 | } else { 415 | sent_bytes = SSL_write((SSL *)ssl_session, (void*)head, tail - head); 416 | if (sent_bytes <= 0) { 417 | if (SSL_get_error(ssl_session, sent_bytes) != SSL_ERROR_WANT_READ && 418 | SSL_get_error(ssl_session, sent_bytes) != SSL_ERROR_WANT_WRITE) { 419 | sm_on_debug(self, "ss.failed fd=%d", fd); 420 | perror("ssl send failed"); 421 | return SM_ERROR; 422 | } 423 | break; 424 | } 425 | } 426 | head += sent_bytes; 427 | if (head >= tail) { 428 | self->on_sent(self, fd, value, data, length); 429 | return SM_SUCCESS; // this is the typical case 430 | } 431 | } 432 | } 433 | // we can't send this now, so queue it 434 | int curr_recv_fd = my->curr_recv_fd; 435 | sm_sendq_t newq = sm_sendq_new(curr_recv_fd, value, head, tail - head); 436 | if (sendq) { 437 | while (sendq->next) { 438 | sendq = sendq->next; 439 | } 440 | sendq->next = newq; 441 | } else { 442 | ht_put(my->fd_to_sendq, HT_KEY(fd), newq); 443 | FD_SET(fd, my->send_fds); 444 | } 445 | sm_on_debug(self, "ss.sendq<%p> new fd=%d recv_fd=%d length=%zd" 446 | ", prev=<%p>", newq, fd, curr_recv_fd, tail - head, sendq); 447 | if (curr_recv_fd && FD_ISSET(curr_recv_fd, my->recv_fds)) { 448 | // block the current recv_fd, to prevent our sendq from growing too large. 449 | // At worst our recv_fds are all trying to send to the same fd, in which 450 | // case we'll eventually block all of them until the first blocked send 451 | // succeeds. 452 | sm_on_debug(self, "ss.sendq<%p> disable recv_fd=%d", newq, curr_recv_fd); 453 | FD_CLR(curr_recv_fd, my->recv_fds); 454 | FD_CLR(curr_recv_fd, my->tmp_recv_fds); 455 | } 456 | return SM_SUCCESS; 457 | } 458 | 459 | void sm_accept(sm_t self, int fd) { 460 | sm_private_t my = self->private_state; 461 | while (1) { 462 | int new_fd = accept(fd, NULL, NULL); 463 | if (new_fd < 0) { 464 | #ifdef WIN32 465 | if (WSAGetLastError() != WSAEWOULDBLOCK) { 466 | #else 467 | if (errno != EWOULDBLOCK) { 468 | #endif 469 | perror("accept failed"); 470 | self->remove_fd(self, fd); 471 | return; 472 | } 473 | break; 474 | } 475 | sm_on_debug(self, "ss.accept server=%d new_client=%d", 476 | fd, new_fd); 477 | void *value = ht_get_value(my->fd_to_value, HT_KEY(fd)); 478 | void *new_value = NULL; 479 | if (self->on_accept(self, fd, value, new_fd, &new_value)) { 480 | #ifdef WIN32 481 | closesocket(new_fd); 482 | #else 483 | close(new_fd); 484 | #endif 485 | } else if (self->add_fd(self, new_fd, NULL, new_value, false)) { 486 | self->on_close(self, new_fd, new_value, false); 487 | #ifdef WIN32 488 | closesocket(new_fd); 489 | #else 490 | close(new_fd); 491 | #endif 492 | } 493 | } 494 | } 495 | 496 | void sm_resend(sm_t self, int fd) { 497 | sm_private_t my = self->private_state; 498 | sm_sendq_t sendq = ht_get_value(my->fd_to_sendq, HT_KEY(fd)); 499 | void *ssl_session = ht_get_value(my->fd_to_ssl, HT_KEY(fd)); 500 | while (sendq) { 501 | char *head = sendq->head; 502 | char *tail = sendq->tail; 503 | // send as much as we can without blocking 504 | sm_on_debug(self, "ss.sendq<%p> resume send to fd=%d len=%zd", sendq, fd, 505 | (tail - head)); 506 | while (head < tail) { 507 | ssize_t sent_bytes; 508 | if (ssl_session == NULL) { 509 | sent_bytes = send(fd, (void*)head, (tail - head), 0); 510 | if (sent_bytes <= 0) { 511 | #ifdef WIN32 512 | if (sent_bytes && WSAGetLastError() != WSAEWOULDBLOCK) { 513 | fprintf(stderr, "sendq retry failed with error: %d\n", 514 | WSAGetLastError()); 515 | #else 516 | if (sent_bytes && errno != EWOULDBLOCK) { 517 | perror("sendq retry failed"); 518 | #endif 519 | self->remove_fd(self, fd); 520 | return; 521 | } 522 | break; 523 | } 524 | } else { 525 | sent_bytes = SSL_write((SSL *)ssl_session, (void*)head, tail - head); 526 | if (sent_bytes <= 0) { 527 | if (SSL_get_error(ssl_session, sent_bytes) != SSL_ERROR_WANT_READ && 528 | SSL_get_error(ssl_session, sent_bytes) != SSL_ERROR_WANT_WRITE) { 529 | perror("ssl sendq retry failed"); 530 | self->remove_fd(self, fd); 531 | return; 532 | } 533 | break; 534 | } 535 | } 536 | head += sent_bytes; 537 | } 538 | sendq->head = head; 539 | if (head < tail) { 540 | // still have stuff to send 541 | sm_on_debug(self, "ss.sendq<%p> defer len=%zd", sendq, (tail - head)); 542 | break; 543 | } 544 | self->on_sent(self, fd, sendq->value, sendq->begin, tail - sendq->begin); 545 | sm_sendq_t nextq = sendq->next; 546 | ht_put(my->fd_to_sendq, HT_KEY(fd), nextq); 547 | if (!nextq) { 548 | FD_CLR(fd, my->send_fds); 549 | } 550 | int recv_fd = sendq->recv_fd; 551 | if (recv_fd && FD_ISSET(recv_fd, my->all_fds)) { 552 | // if no other sendq's match this blocked recv_fd, re-enable it 553 | bool found = false; 554 | if (ht_size(my->fd_to_sendq)) { 555 | sm_sendq_t *qs = (sm_sendq_t *)ht_values(my->fd_to_sendq); 556 | sm_sendq_t *q; 557 | for (q = qs; *q && !found; q++) { 558 | sm_sendq_t sq; 559 | for (sq = *q; sq && !found; sq = sq->next) { 560 | found |= (sq->recv_fd == recv_fd); 561 | } 562 | } 563 | free(qs); 564 | } 565 | if (!found) { 566 | sm_on_debug(self, "ss.sendq<%p> re-enable recv_fd=%d", sendq, recv_fd); 567 | FD_SET(recv_fd, my->recv_fds); 568 | // don't FD_SET(tmp_recv_fds), since maybe there was no input 569 | // instead, let the next select loop pick it up 570 | } 571 | } 572 | sm_on_debug(self, "ss.sendq<%p> free, next=<%p>", sendq, nextq); 573 | sm_sendq_free(sendq); 574 | sendq = nextq; 575 | } 576 | } 577 | 578 | void sm_recv(sm_t self, int fd) { 579 | sm_private_t my = self->private_state; 580 | my->curr_recv_fd = fd; 581 | void *ssl_session = ht_get_value(my->fd_to_ssl, HT_KEY(fd)); 582 | while (1) { 583 | ssize_t read_bytes; 584 | if (ssl_session == NULL) { 585 | read_bytes = recv(fd, my->tmp_buf, my->tmp_buf_length, RECV_FLAGS); 586 | if (read_bytes < 0) { 587 | #ifdef WIN32 588 | if (WSAGetLastError() != WSAEWOULDBLOCK) { 589 | fprintf(stderr, "recv failed with error %d\n", WSAGetLastError()); 590 | #else 591 | if (errno != EWOULDBLOCK) { 592 | perror("recv failed"); 593 | #endif 594 | self->remove_fd(self, fd); 595 | } 596 | break; 597 | } 598 | } else { 599 | read_bytes = SSL_read((SSL *)ssl_session, my->tmp_buf, my->tmp_buf_length); 600 | if (read_bytes <= 0) { 601 | if (SSL_get_error(ssl_session, read_bytes) != SSL_ERROR_WANT_READ && 602 | SSL_get_error(ssl_session, read_bytes) != SSL_ERROR_WANT_WRITE) { 603 | perror("ssl recv failed"); 604 | self->remove_fd(self, fd); 605 | } 606 | break; 607 | } 608 | } 609 | sm_on_debug(self, "ss.recv fd=%d len=%zd", fd, read_bytes); 610 | void *value = ht_get_value(my->fd_to_value, HT_KEY(fd)); 611 | if (read_bytes == 0 || 612 | self->on_recv(self, fd, value, my->tmp_buf, read_bytes)) { 613 | self->remove_fd(self, fd); 614 | break; 615 | } 616 | } 617 | my->curr_recv_fd = 0; 618 | } 619 | 620 | int sm_select(sm_t self, int timeout_secs) { 621 | sm_private_t my = self->private_state; 622 | 623 | if (my->max_fd <= 0) { 624 | return -1; 625 | } 626 | 627 | my->timeout.tv_sec = timeout_secs; 628 | 629 | // copy into tmp 630 | memcpy(my->tmp_send_fds, my->send_fds, SIZEOF_FD_SET); 631 | memcpy(my->tmp_recv_fds, my->recv_fds, SIZEOF_FD_SET); 632 | memcpy(my->tmp_fail_fds, my->all_fds, SIZEOF_FD_SET); 633 | int num_ready = select(my->max_fd + 1, my->tmp_recv_fds, 634 | my->tmp_send_fds, my->tmp_fail_fds, &my->timeout); 635 | 636 | // see if any sockets are readable 637 | if (num_ready == 0) { 638 | return 0; // timeout, select again 639 | } 640 | if (num_ready < 0) { 641 | #ifdef WIN32 642 | int socket_err = WSAGetLastError(); 643 | if (socket_err != WSAEINTR && socket_err != WSAEWOULDBLOCK) { 644 | fprintf(stderr, "socket_manager: select failed with\ 645 | error %d\n", WSAGetLastError()); 646 | return -socket_err; 647 | } 648 | #else 649 | if (errno != EINTR && errno != EAGAIN) { 650 | // might want to sleep here? 651 | perror("select failed"); 652 | return -errno; 653 | } 654 | #endif 655 | return 0; 656 | } 657 | 658 | int num_left = num_ready; 659 | int fd; 660 | for (fd = 0; fd <= my->max_fd && num_left > 0; fd++) { 661 | bool can_send = FD_ISSET(fd, my->tmp_send_fds); 662 | bool can_recv = FD_ISSET(fd, my->tmp_recv_fds); 663 | bool is_fail = FD_ISSET(fd, my->tmp_fail_fds); 664 | if (!can_send && !can_recv && !is_fail) { 665 | continue; 666 | } 667 | num_left--; 668 | if (is_fail) { 669 | self->remove_fd(self, fd); 670 | } else if (FD_ISSET(fd, my->server_fds)) { 671 | sm_accept(self, fd); 672 | } else { 673 | if (can_send) { 674 | sm_resend(self, fd); 675 | } 676 | if (can_recv) { 677 | sm_recv(self, fd); 678 | } 679 | } 680 | } 681 | return num_ready; 682 | } 683 | 684 | sm_status sm_cleanup(sm_t self) { 685 | sm_private_t my = self->private_state; 686 | int fd; 687 | for (fd = 0; fd <= my->max_fd; fd++) { 688 | if (FD_ISSET(fd, my->all_fds)) { 689 | self->remove_fd(self, fd); 690 | } 691 | } 692 | return SM_SUCCESS; 693 | } 694 | 695 | // 696 | // STRUCTS 697 | // 698 | 699 | void sm_private_free(sm_private_t my) { 700 | if (my) { 701 | free(my->all_fds); 702 | free(my->server_fds); 703 | free(my->send_fds); 704 | free(my->recv_fds); 705 | free(my->tmp_send_fds); 706 | free(my->tmp_recv_fds); 707 | free(my->tmp_fail_fds); 708 | ht_free(my->fd_to_ssl); 709 | ht_free(my->fd_to_value); 710 | ht_free(my->fd_to_sendq); 711 | free(my->tmp_buf); 712 | memset(my, 0, sizeof(struct sm_private)); 713 | free(my); 714 | } 715 | } 716 | 717 | sm_private_t sm_private_new(size_t buf_length) { 718 | sm_private_t my = (sm_private_t)malloc(sizeof(struct sm_private)); 719 | if (!my) { 720 | return NULL; 721 | } 722 | memset(my, 0, sizeof(struct sm_private)); 723 | my->all_fds = (fd_set *)malloc(SIZEOF_FD_SET); 724 | my->server_fds = (fd_set *)malloc(SIZEOF_FD_SET); 725 | my->send_fds = (fd_set *)malloc(SIZEOF_FD_SET); 726 | my->recv_fds = (fd_set *)malloc(SIZEOF_FD_SET); 727 | my->tmp_send_fds = (fd_set *)malloc(SIZEOF_FD_SET); 728 | my->tmp_recv_fds = (fd_set *)malloc(SIZEOF_FD_SET); 729 | my->tmp_fail_fds = (fd_set *)malloc(SIZEOF_FD_SET); 730 | my->fd_to_ssl = ht_new(HT_INT_KEYS); 731 | my->fd_to_value = ht_new(HT_INT_KEYS); 732 | my->fd_to_sendq = ht_new(HT_INT_KEYS); 733 | my->tmp_buf = (char *)calloc(buf_length, sizeof(char *)); 734 | if (!my->tmp_buf || !my->all_fds || !my->server_fds || 735 | !my->send_fds || !my->recv_fds || 736 | !my->tmp_send_fds || !my->tmp_recv_fds || !my->tmp_fail_fds || 737 | !my->fd_to_ssl || !my->fd_to_value || !my->fd_to_sendq) { 738 | sm_private_free(my); 739 | return NULL; 740 | } 741 | FD_ZERO(my->all_fds); 742 | FD_ZERO(my->server_fds); 743 | FD_ZERO(my->send_fds); 744 | FD_ZERO(my->recv_fds); 745 | FD_ZERO(my->tmp_send_fds); 746 | FD_ZERO(my->tmp_recv_fds); 747 | FD_ZERO(my->tmp_fail_fds); 748 | my->max_fd = -1; 749 | my->timeout.tv_sec = 5; 750 | my->timeout.tv_usec = 0; 751 | my->tmp_buf_length = buf_length; 752 | return my; 753 | } 754 | 755 | sm_sendq_t sm_sendq_new(int recv_fd, void *value, const char *data, 756 | size_t length) { 757 | sm_sendq_t ret = (sm_sendq_t)malloc(sizeof(struct sm_sendq)); 758 | memset(ret, 0, sizeof(struct sm_sendq)); 759 | ret->recv_fd = recv_fd; 760 | ret->value = value; 761 | ret->begin = (char *)malloc(length); 762 | memcpy(ret->begin, data, length); 763 | ret->head = ret->begin; 764 | ret->tail = ret->begin + length; 765 | return ret; 766 | } 767 | 768 | void sm_sendq_free(sm_sendq_t sendq) { 769 | if (sendq) { 770 | free(sendq->begin); 771 | memset(sendq, 0, sizeof(struct sm_sendq)); 772 | free(sendq); 773 | } 774 | } 775 | 776 | sm_t sm_new(size_t buf_length) { 777 | sm_private_t my = sm_private_new(buf_length); 778 | if (!my) { 779 | return NULL; 780 | } 781 | sm_t self = (sm_t)malloc(sizeof(struct sm_struct)); 782 | if (!self) { 783 | sm_private_free(my); 784 | return NULL; 785 | } 786 | memset(self, 0, sizeof(struct sm_struct)); 787 | self->add_fd = sm_add_fd; 788 | self->remove_fd = sm_remove_fd; 789 | self->send = sm_send; 790 | self->select = sm_select; 791 | self->cleanup = sm_cleanup; 792 | self->private_state = my; 793 | return self; 794 | } 795 | 796 | void sm_free(sm_t self) { 797 | if (self) { 798 | sm_private_free(self->private_state); 799 | memset(self, 0, sizeof(struct sm_struct)); 800 | free(self); 801 | } 802 | } 803 | --------------------------------------------------------------------------------
98 |
99 |
    100 | 101 |
    102 |
      103 |