├── .gitignore ├── Makefile ├── README.md ├── base64.c ├── md5.c ├── md5.h ├── sha1.c ├── sha1.h ├── websocket.c ├── websocket.h └── websockify.c /.gitignore: -------------------------------------------------------------------------------- 1 | websockify 2 | *.o 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGETS=wsproxy 2 | CFLAGS += -fPIC 3 | 4 | all: $(TARGETS) 5 | 6 | wsproxy: websockify.o websocket.o base64.o md5.o sha1.o 7 | $(CC) $(LDFLAGS) $^ -o $@ 8 | 9 | websocket.o: websocket.c websocket.h 10 | websockify.o: websockify.c websocket.h 11 | md5.o: md5.c md5.h 12 | sha1.o: sha1.c sha1.h 13 | base64.o: base64.c 14 | 15 | clean: 16 | rm -f wsproxy *.o 17 | 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wsproxy 2 | 3 | websocket tcp/udp proxy (based on websockify-c) 4 | 5 | ```Usage: wsproxy [options] [source_addr:]source_port 6 | 7 | --verbose|-v verbose messages and per frame traffic 8 | --daemon|-d become a daemon (background process) 9 | --whitelist-hosts|-W LIST new-line separated target host whitelist file 10 | --whitelist-ports|-P LIST new-line separated target port whitelist file 11 | --pid|-p desired path of pid file. Default: '/var/run/websockify.pid' 12 | 13 | ``` 14 | 15 | Patch for emscripten: 16 | 17 | https://github.com/FWGS/emscripten/commit/efcb8ecd0807c5590637812a29b4d1c7cd582719 -------------------------------------------------------------------------------- /base64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996-1999 by Internet Software Consortium. 3 | * 4 | * Permission to use, copy, modify, and distribute this software for any 5 | * purpose with or without fee is hereby granted, provided that the above 6 | * copyright notice and this permission notice appear in all copies. 7 | * 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS 9 | * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES 10 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE 11 | * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 12 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 13 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 14 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS 15 | * SOFTWARE. 16 | */ 17 | 18 | /* 19 | * Portions Copyright (c) 1995 by International Business Machines, Inc. 20 | * 21 | * International Business Machines, Inc. (hereinafter called IBM) grants 22 | * permission under its copyrights to use, copy, modify, and distribute this 23 | * Software with or without fee, provided that the above copyright notice and 24 | * all paragraphs of this notice appear in all copies, and that the name of IBM 25 | * not be used in connection with the marketing of any product incorporating 26 | * the Software or modifications thereof, without specific, written prior 27 | * permission. 28 | * 29 | * To the extent it has a right to do so, IBM grants an immunity from suit 30 | * under its patents, if any, for the use, sale or manufacture of products to 31 | * the extent that such products are used for performing Domain Name System 32 | * dynamic updates in TCP/IP networks by means of the Software. No immunity is 33 | * granted for any product per se or for any other function of any product. 34 | * 35 | * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, 36 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 37 | * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, 38 | * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING 39 | * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN 40 | * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. 41 | */ 42 | 43 | #if !defined(LINT) && !defined(CODECENTER) 44 | static const char rcsid[] = "$BINDId: base64.c,v 8.7 1999/10/13 16:39:33 vixie Exp $"; 45 | #endif /* not lint */ 46 | 47 | #include 48 | #include 49 | #include 50 | 51 | #include 52 | #include 53 | #include 54 | 55 | #include 56 | #include 57 | #include 58 | #include 59 | #include 60 | 61 | #define Assert(Cond) if (!(Cond)) abort() 62 | 63 | static const char Base64[] = 64 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 65 | static const char Pad64 = '='; 66 | 67 | /* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) 68 | The following encoding technique is taken from RFC 1521 by Borenstein 69 | and Freed. It is reproduced here in a slightly edited form for 70 | convenience. 71 | 72 | A 65-character subset of US-ASCII is used, enabling 6 bits to be 73 | represented per printable character. (The extra 65th character, "=", 74 | is used to signify a special processing function.) 75 | 76 | The encoding process represents 24-bit groups of input bits as output 77 | strings of 4 encoded characters. Proceeding from left to right, a 78 | 24-bit input group is formed by concatenating 3 8-bit input groups. 79 | These 24 bits are then treated as 4 concatenated 6-bit groups, each 80 | of which is translated into a single digit in the base64 alphabet. 81 | 82 | Each 6-bit group is used as an index into an array of 64 printable 83 | characters. The character referenced by the index is placed in the 84 | output string. 85 | 86 | Table 1: The Base64 Alphabet 87 | 88 | Value Encoding Value Encoding Value Encoding Value Encoding 89 | 0 A 17 R 34 i 51 z 90 | 1 B 18 S 35 j 52 0 91 | 2 C 19 T 36 k 53 1 92 | 3 D 20 U 37 l 54 2 93 | 4 E 21 V 38 m 55 3 94 | 5 F 22 W 39 n 56 4 95 | 6 G 23 X 40 o 57 5 96 | 7 H 24 Y 41 p 58 6 97 | 8 I 25 Z 42 q 59 7 98 | 9 J 26 a 43 r 60 8 99 | 10 K 27 b 44 s 61 9 100 | 11 L 28 c 45 t 62 + 101 | 12 M 29 d 46 u 63 / 102 | 13 N 30 e 47 v 103 | 14 O 31 f 48 w (pad) = 104 | 15 P 32 g 49 x 105 | 16 Q 33 h 50 y 106 | 107 | Special processing is performed if fewer than 24 bits are available 108 | at the end of the data being encoded. A full encoding quantum is 109 | always completed at the end of a quantity. When fewer than 24 input 110 | bits are available in an input group, zero bits are added (on the 111 | right) to form an integral number of 6-bit groups. Padding at the 112 | end of the data is performed using the '=' character. 113 | 114 | Since all base64 input is an integral number of octets, only the 115 | ------------------------------------------------- 116 | following cases can arise: 117 | 118 | (1) the final quantum of encoding input is an integral 119 | multiple of 24 bits; here, the final unit of encoded 120 | output will be an integral multiple of 4 characters 121 | with no "=" padding, 122 | (2) the final quantum of encoding input is exactly 8 bits; 123 | here, the final unit of encoded output will be two 124 | characters followed by two "=" padding characters, or 125 | (3) the final quantum of encoding input is exactly 16 bits; 126 | here, the final unit of encoded output will be three 127 | characters followed by one "=" padding character. 128 | */ 129 | 130 | int 131 | ws_b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) { 132 | size_t datalength = 0; 133 | u_char input[3]; 134 | u_char output[4]; 135 | size_t i; 136 | 137 | while (2 < srclength) { 138 | input[0] = *src++; 139 | input[1] = *src++; 140 | input[2] = *src++; 141 | srclength -= 3; 142 | 143 | output[0] = input[0] >> 2; 144 | output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 145 | output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 146 | output[3] = input[2] & 0x3f; 147 | Assert(output[0] < 64); 148 | Assert(output[1] < 64); 149 | Assert(output[2] < 64); 150 | Assert(output[3] < 64); 151 | 152 | if (datalength + 4 > targsize) 153 | return (-1); 154 | target[datalength++] = Base64[output[0]]; 155 | target[datalength++] = Base64[output[1]]; 156 | target[datalength++] = Base64[output[2]]; 157 | target[datalength++] = Base64[output[3]]; 158 | } 159 | 160 | /* Now we worry about padding. */ 161 | if (0 != srclength) { 162 | /* Get what's left. */ 163 | input[0] = input[1] = input[2] = '\0'; 164 | for (i = 0; i < srclength; i++) 165 | input[i] = *src++; 166 | 167 | output[0] = input[0] >> 2; 168 | output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); 169 | output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); 170 | Assert(output[0] < 64); 171 | Assert(output[1] < 64); 172 | Assert(output[2] < 64); 173 | 174 | if (datalength + 4 > targsize) 175 | return (-1); 176 | target[datalength++] = Base64[output[0]]; 177 | target[datalength++] = Base64[output[1]]; 178 | if (srclength == 1) 179 | target[datalength++] = Pad64; 180 | else 181 | target[datalength++] = Base64[output[2]]; 182 | target[datalength++] = Pad64; 183 | } 184 | if (datalength >= targsize) 185 | return (-1); 186 | target[datalength] = '\0'; /* Returned value doesn't count \0. */ 187 | return (datalength); 188 | } 189 | //libresolv_hidden_def (b64_ntop) 190 | 191 | /* skips all whitespace anywhere. 192 | converts characters, four at a time, starting at (or after) 193 | src from base - 64 numbers into three 8 bit bytes in the target area. 194 | it returns the number of data bytes stored at the target, or -1 on error. 195 | */ 196 | 197 | int 198 | ws_b64_pton(char const *src, u_char *target, size_t targsize) { 199 | int tarindex, state, ch; 200 | char *pos; 201 | 202 | state = 0; 203 | tarindex = 0; 204 | 205 | while ((ch = *src++) != '\0') { 206 | if (isspace(ch)) /* Skip whitespace anywhere. */ 207 | continue; 208 | 209 | if (ch == Pad64) 210 | break; 211 | 212 | pos = strchr(Base64, ch); 213 | if (pos == 0) /* A non-base64 character. */ 214 | return (-1); 215 | 216 | switch (state) { 217 | case 0: 218 | if (target) { 219 | if ((size_t)tarindex >= targsize) 220 | return (-1); 221 | target[tarindex] = (pos - Base64) << 2; 222 | } 223 | state = 1; 224 | break; 225 | case 1: 226 | if (target) { 227 | if ((size_t)tarindex + 1 >= targsize) 228 | return (-1); 229 | target[tarindex] |= (pos - Base64) >> 4; 230 | target[tarindex+1] = ((pos - Base64) & 0x0f) 231 | << 4 ; 232 | } 233 | tarindex++; 234 | state = 2; 235 | break; 236 | case 2: 237 | if (target) { 238 | if ((size_t)tarindex + 1 >= targsize) 239 | return (-1); 240 | target[tarindex] |= (pos - Base64) >> 2; 241 | target[tarindex+1] = ((pos - Base64) & 0x03) 242 | << 6; 243 | } 244 | tarindex++; 245 | state = 3; 246 | break; 247 | case 3: 248 | if (target) { 249 | if ((size_t)tarindex >= targsize) 250 | return (-1); 251 | target[tarindex] |= (pos - Base64); 252 | } 253 | tarindex++; 254 | state = 0; 255 | break; 256 | default: 257 | abort(); 258 | } 259 | } 260 | 261 | /* 262 | * We are done decoding Base-64 chars. Let's see if we ended 263 | * on a byte boundary, and/or with erroneous trailing characters. 264 | */ 265 | 266 | if (ch == Pad64) { /* We got a pad char. */ 267 | ch = *src++; /* Skip it, get next. */ 268 | switch (state) { 269 | case 0: /* Invalid = in first position */ 270 | case 1: /* Invalid = in second position */ 271 | return (-1); 272 | 273 | case 2: /* Valid, means one byte of info */ 274 | /* Skip any number of spaces. */ 275 | for ((void)NULL; ch != '\0'; ch = *src++) 276 | if (!isspace(ch)) 277 | break; 278 | /* Make sure there is another trailing = sign. */ 279 | if (ch != Pad64) 280 | return (-1); 281 | ch = *src++; /* Skip the = */ 282 | /* Fall through to "single trailing =" case. */ 283 | /* FALLTHROUGH */ 284 | 285 | case 3: /* Valid, means two bytes of info */ 286 | /* 287 | * We know this char is an =. Is there anything but 288 | * whitespace after it? 289 | */ 290 | for ((void)NULL; ch != '\0'; ch = *src++) 291 | if (!isspace(ch)) 292 | return (-1); 293 | 294 | /* 295 | * Now make sure for cases 2 and 3 that the "extra" 296 | * bits that slopped past the last full byte were 297 | * zeros. If we don't check them, they become a 298 | * subliminal channel. 299 | */ 300 | if (target && target[tarindex] != 0) 301 | return (-1); 302 | } 303 | } else { 304 | /* 305 | * We ended by seeing the end of the string. Make sure we 306 | * have no partial bytes lying around. 307 | */ 308 | if (state != 0) 309 | return (-1); 310 | } 311 | 312 | return (tarindex); 313 | } 314 | -------------------------------------------------------------------------------- /md5.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: 9 | * Alexander Peslyak, better known as Solar Designer 10 | * 11 | * This software was written by Alexander Peslyak in 2001. No copyright is 12 | * claimed, and the software is hereby placed in the public domain. 13 | * In case this attempt to disclaim copyright and place the software in the 14 | * public domain is deemed null and void, then the software is 15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 16 | * general public under the following terms: 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted. 20 | * 21 | * There's ABSOLUTELY NO WARRANTY, express or implied. 22 | * 23 | * (This is a heavily cut-down "BSD license".) 24 | * 25 | * This differs from Colin Plumb's older public domain implementation in that 26 | * no exactly 32-bit integer data type is required (any 32-bit or wider 27 | * unsigned integer data type will do), there's no compile-time endianness 28 | * configuration, and the function prototypes match OpenSSL's. No code from 29 | * Colin Plumb's implementation has been reused; this comment merely compares 30 | * the properties of the two independent implementations. 31 | * 32 | * The primary goals of this implementation are portability and ease of use. 33 | * It is meant to be fast, but not as fast as possible. Some known 34 | * optimizations are not included to reduce source code size and avoid 35 | * compile-time configuration. 36 | */ 37 | 38 | #ifndef HAVE_OPENSSL 39 | 40 | #include 41 | 42 | #include "md5.h" 43 | 44 | /* 45 | * The basic MD5 functions. 46 | * 47 | * F and G are optimized compared to their RFC 1321 definitions for 48 | * architectures that lack an AND-NOT instruction, just like in Colin Plumb's 49 | * implementation. 50 | */ 51 | #define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) 52 | #define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) 53 | #define H(x, y, z) (((x) ^ (y)) ^ (z)) 54 | #define H2(x, y, z) ((x) ^ ((y) ^ (z))) 55 | #define I(x, y, z) ((y) ^ ((x) | ~(z))) 56 | 57 | /* 58 | * The MD5 transformation for all four rounds. 59 | */ 60 | #define STEP(f, a, b, c, d, x, t, s) \ 61 | (a) += f((b), (c), (d)) + (x) + (t); \ 62 | (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \ 63 | (a) += (b); 64 | 65 | /* 66 | * SET reads 4 input bytes in little-endian byte order and stores them in a 67 | * properly aligned word in host byte order. 68 | * 69 | * The check for little-endian architectures that tolerate unaligned memory 70 | * accesses is just an optimization. Nothing will break if it fails to detect 71 | * a suitable architecture. 72 | * 73 | * Unfortunately, this optimization may be a C strict aliasing rules violation 74 | * if the caller's data buffer has effective type that cannot be aliased by 75 | * MD5_u32plus. In practice, this problem may occur if these MD5 routines are 76 | * inlined into a calling function, or with future and dangerously advanced 77 | * link-time optimizations. For the time being, keeping these MD5 routines in 78 | * their own translation unit avoids the problem. 79 | */ 80 | #if defined(__i386__) || defined(__x86_64__) || defined(__vax__) 81 | #define SET(n) \ 82 | (*(MD5_u32plus *)&ptr[(n) * 4]) 83 | #define GET(n) \ 84 | SET(n) 85 | #else 86 | #define SET(n) \ 87 | (ctx->block[(n)] = \ 88 | (MD5_u32plus)ptr[(n) * 4] | \ 89 | ((MD5_u32plus)ptr[(n) * 4 + 1] << 8) | \ 90 | ((MD5_u32plus)ptr[(n) * 4 + 2] << 16) | \ 91 | ((MD5_u32plus)ptr[(n) * 4 + 3] << 24)) 92 | #define GET(n) \ 93 | (ctx->block[(n)]) 94 | #endif 95 | 96 | /* 97 | * This processes one or more 64-byte data blocks, but does NOT update the bit 98 | * counters. There are no alignment requirements. 99 | */ 100 | static const void *body(MD5_CTX *ctx, const void *data, unsigned long size) 101 | { 102 | const unsigned char *ptr; 103 | MD5_u32plus a, b, c, d; 104 | MD5_u32plus saved_a, saved_b, saved_c, saved_d; 105 | 106 | ptr = (const unsigned char *)data; 107 | 108 | a = ctx->a; 109 | b = ctx->b; 110 | c = ctx->c; 111 | d = ctx->d; 112 | 113 | do { 114 | saved_a = a; 115 | saved_b = b; 116 | saved_c = c; 117 | saved_d = d; 118 | 119 | /* Round 1 */ 120 | STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7) 121 | STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12) 122 | STEP(F, c, d, a, b, SET(2), 0x242070db, 17) 123 | STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22) 124 | STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7) 125 | STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12) 126 | STEP(F, c, d, a, b, SET(6), 0xa8304613, 17) 127 | STEP(F, b, c, d, a, SET(7), 0xfd469501, 22) 128 | STEP(F, a, b, c, d, SET(8), 0x698098d8, 7) 129 | STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12) 130 | STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17) 131 | STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22) 132 | STEP(F, a, b, c, d, SET(12), 0x6b901122, 7) 133 | STEP(F, d, a, b, c, SET(13), 0xfd987193, 12) 134 | STEP(F, c, d, a, b, SET(14), 0xa679438e, 17) 135 | STEP(F, b, c, d, a, SET(15), 0x49b40821, 22) 136 | 137 | /* Round 2 */ 138 | STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5) 139 | STEP(G, d, a, b, c, GET(6), 0xc040b340, 9) 140 | STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14) 141 | STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20) 142 | STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5) 143 | STEP(G, d, a, b, c, GET(10), 0x02441453, 9) 144 | STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14) 145 | STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20) 146 | STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5) 147 | STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9) 148 | STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14) 149 | STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20) 150 | STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5) 151 | STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9) 152 | STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14) 153 | STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20) 154 | 155 | /* Round 3 */ 156 | STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4) 157 | STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11) 158 | STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16) 159 | STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23) 160 | STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4) 161 | STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11) 162 | STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16) 163 | STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23) 164 | STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4) 165 | STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11) 166 | STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16) 167 | STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23) 168 | STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4) 169 | STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11) 170 | STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16) 171 | STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23) 172 | 173 | /* Round 4 */ 174 | STEP(I, a, b, c, d, GET(0), 0xf4292244, 6) 175 | STEP(I, d, a, b, c, GET(7), 0x432aff97, 10) 176 | STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15) 177 | STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21) 178 | STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6) 179 | STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10) 180 | STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15) 181 | STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21) 182 | STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6) 183 | STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10) 184 | STEP(I, c, d, a, b, GET(6), 0xa3014314, 15) 185 | STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21) 186 | STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6) 187 | STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10) 188 | STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15) 189 | STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21) 190 | 191 | a += saved_a; 192 | b += saved_b; 193 | c += saved_c; 194 | d += saved_d; 195 | 196 | ptr += 64; 197 | } while (size -= 64); 198 | 199 | ctx->a = a; 200 | ctx->b = b; 201 | ctx->c = c; 202 | ctx->d = d; 203 | 204 | return ptr; 205 | } 206 | 207 | void MD5_Init(MD5_CTX *ctx) 208 | { 209 | ctx->a = 0x67452301; 210 | ctx->b = 0xefcdab89; 211 | ctx->c = 0x98badcfe; 212 | ctx->d = 0x10325476; 213 | 214 | ctx->lo = 0; 215 | ctx->hi = 0; 216 | } 217 | 218 | void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size) 219 | { 220 | MD5_u32plus saved_lo; 221 | unsigned long used, available; 222 | 223 | saved_lo = ctx->lo; 224 | if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo) 225 | ctx->hi++; 226 | ctx->hi += size >> 29; 227 | 228 | used = saved_lo & 0x3f; 229 | 230 | if (used) { 231 | available = 64 - used; 232 | 233 | if (size < available) { 234 | memcpy(&ctx->buffer[used], data, size); 235 | return; 236 | } 237 | 238 | memcpy(&ctx->buffer[used], data, available); 239 | data = (const unsigned char *)data + available; 240 | size -= available; 241 | body(ctx, ctx->buffer, 64); 242 | } 243 | 244 | if (size >= 64) { 245 | data = body(ctx, data, size & ~(unsigned long)0x3f); 246 | size &= 0x3f; 247 | } 248 | 249 | memcpy(ctx->buffer, data, size); 250 | } 251 | 252 | #define OUT(dst, src) \ 253 | (dst)[0] = (unsigned char)(src); \ 254 | (dst)[1] = (unsigned char)((src) >> 8); \ 255 | (dst)[2] = (unsigned char)((src) >> 16); \ 256 | (dst)[3] = (unsigned char)((src) >> 24); 257 | 258 | void MD5_Final(unsigned char *result, MD5_CTX *ctx) 259 | { 260 | unsigned long used, available; 261 | 262 | used = ctx->lo & 0x3f; 263 | 264 | ctx->buffer[used++] = 0x80; 265 | 266 | available = 64 - used; 267 | 268 | if (available < 8) { 269 | memset(&ctx->buffer[used], 0, available); 270 | body(ctx, ctx->buffer, 64); 271 | used = 0; 272 | available = 64; 273 | } 274 | 275 | memset(&ctx->buffer[used], 0, available - 8); 276 | 277 | ctx->lo <<= 3; 278 | OUT(&ctx->buffer[56], ctx->lo) 279 | OUT(&ctx->buffer[60], ctx->hi) 280 | 281 | body(ctx, ctx->buffer, 64); 282 | 283 | OUT(&result[0], ctx->a) 284 | OUT(&result[4], ctx->b) 285 | OUT(&result[8], ctx->c) 286 | OUT(&result[12], ctx->d) 287 | 288 | memset(ctx, 0, sizeof(*ctx)); 289 | } 290 | 291 | #endif 292 | -------------------------------------------------------------------------------- /md5.h: -------------------------------------------------------------------------------- 1 | /* 2 | * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc. 3 | * MD5 Message-Digest Algorithm (RFC 1321). 4 | * 5 | * Homepage: 6 | * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5 7 | * 8 | * Author: 9 | * Alexander Peslyak, better known as Solar Designer 10 | * 11 | * This software was written by Alexander Peslyak in 2001. No copyright is 12 | * claimed, and the software is hereby placed in the public domain. 13 | * In case this attempt to disclaim copyright and place the software in the 14 | * public domain is deemed null and void, then the software is 15 | * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the 16 | * general public under the following terms: 17 | * 18 | * Redistribution and use in source and binary forms, with or without 19 | * modification, are permitted. 20 | * 21 | * There's ABSOLUTELY NO WARRANTY, express or implied. 22 | * 23 | * See md5.c for more information. 24 | */ 25 | 26 | #ifdef HAVE_OPENSSL 27 | #include 28 | #elif !defined(_MD5_H) 29 | #define _MD5_H 30 | 31 | /* Any 32-bit or wider unsigned integer data type will do */ 32 | typedef unsigned int MD5_u32plus; 33 | 34 | typedef struct { 35 | MD5_u32plus lo, hi; 36 | MD5_u32plus a, b, c, d; 37 | unsigned char buffer[64]; 38 | MD5_u32plus block[16]; 39 | } MD5_CTX; 40 | 41 | extern void MD5_Init(MD5_CTX *ctx); 42 | extern void MD5_Update(MD5_CTX *ctx, const void *data, unsigned long size); 43 | extern void MD5_Final(unsigned char *result, MD5_CTX *ctx); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /sha1.c: -------------------------------------------------------------------------------- 1 | /* 2 | SHA-1 in C 3 | By Steve Reid 4 | 100% Public Domain 5 | 6 | Test Vectors (from FIPS PUB 180-1) 7 | "abc" 8 | A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D 9 | "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 10 | 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 11 | A million repetitions of "a" 12 | 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F 13 | */ 14 | 15 | /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ 16 | /* #define SHA1HANDSOFF * Copies data before messing with it. */ 17 | 18 | #define SHA1HANDSOFF 19 | 20 | #include 21 | #include 22 | 23 | /* for uint32_t */ 24 | #include 25 | 26 | #include "sha1.h" 27 | 28 | 29 | #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) 30 | 31 | /* blk0() and blk() perform the initial expand. */ 32 | /* I got the idea of expanding during the round function from SSLeay */ 33 | #if BYTE_ORDER == LITTLE_ENDIAN 34 | #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ 35 | |(rol(block->l[i],8)&0x00FF00FF)) 36 | #elif BYTE_ORDER == BIG_ENDIAN 37 | #define blk0(i) block->l[i] 38 | #else 39 | #error "Endianness not defined!" 40 | #endif 41 | #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ 42 | ^block->l[(i+2)&15]^block->l[i&15],1)) 43 | 44 | /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ 45 | #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); 46 | #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); 47 | #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); 48 | #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); 49 | #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); 50 | 51 | 52 | /* Hash a single 512-bit block. This is the core of the algorithm. */ 53 | 54 | void SHA1Transform( 55 | uint32_t state[5], 56 | const unsigned char buffer[64] 57 | ) 58 | { 59 | uint32_t a, b, c, d, e; 60 | 61 | typedef union 62 | { 63 | unsigned char c[64]; 64 | uint32_t l[16]; 65 | } CHAR64LONG16; 66 | 67 | #ifdef SHA1HANDSOFF 68 | CHAR64LONG16 block[1]; /* use array to appear as a pointer */ 69 | 70 | memcpy(block, buffer, 64); 71 | #else 72 | /* The following had better never be used because it causes the 73 | * pointer-to-const buffer to be cast into a pointer to non-const. 74 | * And the result is written through. I threw a "const" in, hoping 75 | * this will cause a diagnostic. 76 | */ 77 | CHAR64LONG16 *block = (const CHAR64LONG16 *) buffer; 78 | #endif 79 | /* Copy context->state[] to working vars */ 80 | a = state[0]; 81 | b = state[1]; 82 | c = state[2]; 83 | d = state[3]; 84 | e = state[4]; 85 | /* 4 rounds of 20 operations each. Loop unrolled. */ 86 | R0(a, b, c, d, e, 0); 87 | R0(e, a, b, c, d, 1); 88 | R0(d, e, a, b, c, 2); 89 | R0(c, d, e, a, b, 3); 90 | R0(b, c, d, e, a, 4); 91 | R0(a, b, c, d, e, 5); 92 | R0(e, a, b, c, d, 6); 93 | R0(d, e, a, b, c, 7); 94 | R0(c, d, e, a, b, 8); 95 | R0(b, c, d, e, a, 9); 96 | R0(a, b, c, d, e, 10); 97 | R0(e, a, b, c, d, 11); 98 | R0(d, e, a, b, c, 12); 99 | R0(c, d, e, a, b, 13); 100 | R0(b, c, d, e, a, 14); 101 | R0(a, b, c, d, e, 15); 102 | R1(e, a, b, c, d, 16); 103 | R1(d, e, a, b, c, 17); 104 | R1(c, d, e, a, b, 18); 105 | R1(b, c, d, e, a, 19); 106 | R2(a, b, c, d, e, 20); 107 | R2(e, a, b, c, d, 21); 108 | R2(d, e, a, b, c, 22); 109 | R2(c, d, e, a, b, 23); 110 | R2(b, c, d, e, a, 24); 111 | R2(a, b, c, d, e, 25); 112 | R2(e, a, b, c, d, 26); 113 | R2(d, e, a, b, c, 27); 114 | R2(c, d, e, a, b, 28); 115 | R2(b, c, d, e, a, 29); 116 | R2(a, b, c, d, e, 30); 117 | R2(e, a, b, c, d, 31); 118 | R2(d, e, a, b, c, 32); 119 | R2(c, d, e, a, b, 33); 120 | R2(b, c, d, e, a, 34); 121 | R2(a, b, c, d, e, 35); 122 | R2(e, a, b, c, d, 36); 123 | R2(d, e, a, b, c, 37); 124 | R2(c, d, e, a, b, 38); 125 | R2(b, c, d, e, a, 39); 126 | R3(a, b, c, d, e, 40); 127 | R3(e, a, b, c, d, 41); 128 | R3(d, e, a, b, c, 42); 129 | R3(c, d, e, a, b, 43); 130 | R3(b, c, d, e, a, 44); 131 | R3(a, b, c, d, e, 45); 132 | R3(e, a, b, c, d, 46); 133 | R3(d, e, a, b, c, 47); 134 | R3(c, d, e, a, b, 48); 135 | R3(b, c, d, e, a, 49); 136 | R3(a, b, c, d, e, 50); 137 | R3(e, a, b, c, d, 51); 138 | R3(d, e, a, b, c, 52); 139 | R3(c, d, e, a, b, 53); 140 | R3(b, c, d, e, a, 54); 141 | R3(a, b, c, d, e, 55); 142 | R3(e, a, b, c, d, 56); 143 | R3(d, e, a, b, c, 57); 144 | R3(c, d, e, a, b, 58); 145 | R3(b, c, d, e, a, 59); 146 | R4(a, b, c, d, e, 60); 147 | R4(e, a, b, c, d, 61); 148 | R4(d, e, a, b, c, 62); 149 | R4(c, d, e, a, b, 63); 150 | R4(b, c, d, e, a, 64); 151 | R4(a, b, c, d, e, 65); 152 | R4(e, a, b, c, d, 66); 153 | R4(d, e, a, b, c, 67); 154 | R4(c, d, e, a, b, 68); 155 | R4(b, c, d, e, a, 69); 156 | R4(a, b, c, d, e, 70); 157 | R4(e, a, b, c, d, 71); 158 | R4(d, e, a, b, c, 72); 159 | R4(c, d, e, a, b, 73); 160 | R4(b, c, d, e, a, 74); 161 | R4(a, b, c, d, e, 75); 162 | R4(e, a, b, c, d, 76); 163 | R4(d, e, a, b, c, 77); 164 | R4(c, d, e, a, b, 78); 165 | R4(b, c, d, e, a, 79); 166 | /* Add the working vars back into context.state[] */ 167 | state[0] += a; 168 | state[1] += b; 169 | state[2] += c; 170 | state[3] += d; 171 | state[4] += e; 172 | /* Wipe variables */ 173 | a = b = c = d = e = 0; 174 | #ifdef SHA1HANDSOFF 175 | memset(block, '\0', sizeof(block)); 176 | #endif 177 | } 178 | 179 | 180 | /* SHA1Init - Initialize new context */ 181 | 182 | void SHA1Init( 183 | SHA1_CTX * context 184 | ) 185 | { 186 | /* SHA1 initialization constants */ 187 | context->state[0] = 0x67452301; 188 | context->state[1] = 0xEFCDAB89; 189 | context->state[2] = 0x98BADCFE; 190 | context->state[3] = 0x10325476; 191 | context->state[4] = 0xC3D2E1F0; 192 | context->count[0] = context->count[1] = 0; 193 | } 194 | 195 | 196 | /* Run your data through this. */ 197 | 198 | void SHA1Update( 199 | SHA1_CTX * context, 200 | const unsigned char *data, 201 | uint32_t len 202 | ) 203 | { 204 | uint32_t i; 205 | 206 | uint32_t j; 207 | 208 | j = context->count[0]; 209 | if ((context->count[0] += len << 3) < j) 210 | context->count[1]++; 211 | context->count[1] += (len >> 29); 212 | j = (j >> 3) & 63; 213 | if ((j + len) > 63) 214 | { 215 | memcpy(&context->buffer[j], data, (i = 64 - j)); 216 | SHA1Transform(context->state, context->buffer); 217 | for (; i + 63 < len; i += 64) 218 | { 219 | SHA1Transform(context->state, &data[i]); 220 | } 221 | j = 0; 222 | } 223 | else 224 | i = 0; 225 | memcpy(&context->buffer[j], &data[i], len - i); 226 | } 227 | 228 | 229 | /* Add padding and return the message digest. */ 230 | 231 | void SHA1Final( 232 | unsigned char digest[20], 233 | SHA1_CTX * context 234 | ) 235 | { 236 | unsigned i; 237 | 238 | unsigned char finalcount[8]; 239 | 240 | unsigned char c; 241 | 242 | #if 0 /* untested "improvement" by DHR */ 243 | /* Convert context->count to a sequence of bytes 244 | * in finalcount. Second element first, but 245 | * big-endian order within element. 246 | * But we do it all backwards. 247 | */ 248 | unsigned char *fcp = &finalcount[8]; 249 | 250 | for (i = 0; i < 2; i++) 251 | { 252 | uint32_t t = context->count[i]; 253 | 254 | int j; 255 | 256 | for (j = 0; j < 4; t >>= 8, j++) 257 | *--fcp = (unsigned char) t} 258 | #else 259 | for (i = 0; i < 8; i++) 260 | { 261 | finalcount[i] = (unsigned char) ((context->count[(i >= 4 ? 0 : 1)] >> ((3 - (i & 3)) * 8)) & 255); /* Endian independent */ 262 | } 263 | #endif 264 | c = 0200; 265 | SHA1Update(context, &c, 1); 266 | while ((context->count[0] & 504) != 448) 267 | { 268 | c = 0000; 269 | SHA1Update(context, &c, 1); 270 | } 271 | SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ 272 | for (i = 0; i < 20; i++) 273 | { 274 | digest[i] = (unsigned char) 275 | ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & 255); 276 | } 277 | /* Wipe variables */ 278 | memset(context, '\0', sizeof(*context)); 279 | memset(&finalcount, '\0', sizeof(finalcount)); 280 | } 281 | 282 | void SHA1( 283 | char *hash_out, 284 | const char *str, 285 | int len) 286 | { 287 | SHA1_CTX ctx; 288 | unsigned int ii; 289 | 290 | SHA1Init(&ctx); 291 | for (ii=0; ii 7 | 100% Public Domain 8 | */ 9 | 10 | #include "stdint.h" 11 | 12 | typedef struct 13 | { 14 | uint32_t state[5]; 15 | uint32_t count[2]; 16 | unsigned char buffer[64]; 17 | } SHA1_CTX; 18 | 19 | void SHA1Transform( 20 | uint32_t state[5], 21 | const unsigned char buffer[64] 22 | ); 23 | 24 | void SHA1Init( 25 | SHA1_CTX * context 26 | ); 27 | 28 | void SHA1Update( 29 | SHA1_CTX * context, 30 | const unsigned char *data, 31 | uint32_t len 32 | ); 33 | 34 | void SHA1Final( 35 | unsigned char digest[20], 36 | SHA1_CTX * context 37 | ); 38 | 39 | void SHA1( 40 | char *hash_out, 41 | const char *str, 42 | int len); 43 | 44 | #endif /* SHA1_H */ 45 | -------------------------------------------------------------------------------- /websocket.c: -------------------------------------------------------------------------------- 1 | /* 2 | * WebSocket lib with support for "wss://" encryption. 3 | * Copyright 2010 Joel Martin 4 | * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) 5 | * 6 | * You can make a cert/key with openssl using: 7 | * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem 8 | * as taken from http://docs.python.org/dev/library/ssl.html#certificates 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include // daemonizing 21 | #include // daemonizing 22 | #include "websocket.h" 23 | #include "sha1.h" 24 | #include "md5.h" 25 | 26 | int ws_b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize); 27 | int ws_b64_pton(char const *src, u_char *target, size_t targsize); 28 | 29 | /* 30 | * Global state 31 | * 32 | * Warning: not thread safe 33 | */ 34 | int ssl_initialized = 0; 35 | int pipe_error = 0; 36 | settings_t settings; 37 | 38 | 39 | void traffic(char * token) { 40 | if ((settings.verbose) && (! settings.daemon)) { 41 | fprintf(stdout, "%s", token); 42 | fflush(stdout); 43 | } 44 | } 45 | 46 | void error(char *msg) 47 | { 48 | perror(msg); 49 | } 50 | 51 | void fatal(char *msg) 52 | { 53 | perror(msg); 54 | exit(1); 55 | } 56 | 57 | /* resolve host with also IP address parsing */ 58 | int resolve_host(struct in_addr *sin_addr, const char *hostname) 59 | { 60 | if (!inet_aton(hostname, sin_addr)) { 61 | struct addrinfo *ai, *cur; 62 | struct addrinfo hints; 63 | memset(&hints, 0, sizeof(hints)); 64 | hints.ai_family = AF_INET; 65 | if (getaddrinfo(hostname, NULL, &hints, &ai)) 66 | return -1; 67 | for (cur = ai; cur; cur = cur->ai_next) { 68 | if (cur->ai_family == AF_INET) { 69 | *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr; 70 | freeaddrinfo(ai); 71 | return 0; 72 | } 73 | } 74 | freeaddrinfo(ai); 75 | return -1; 76 | } 77 | return 0; 78 | } 79 | 80 | ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len) { 81 | return recv(ctx->sockfd, buf, len, 0); 82 | 83 | } 84 | 85 | ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) { 86 | return send(ctx->sockfd, buf, len, 0); 87 | } 88 | 89 | ws_ctx_t *alloc_ws_ctx() { 90 | ws_ctx_t *ctx; 91 | if (! (ctx = malloc(sizeof(ws_ctx_t))) ) 92 | { fatal("malloc()"); } 93 | 94 | if (! (ctx->cin_buf = malloc(BUFSIZE)) ) 95 | { fatal("malloc of cin_buf"); } 96 | if (! (ctx->cout_buf = malloc(BUFSIZE)) ) 97 | { fatal("malloc of cout_buf"); } 98 | if (! (ctx->tin_buf = malloc(BUFSIZE)) ) 99 | { fatal("malloc of tin_buf"); } 100 | if (! (ctx->tout_buf = malloc(BUFSIZE)) ) 101 | { fatal("malloc of tout_buf"); } 102 | 103 | ctx->headers = malloc(sizeof(headers_t)); 104 | return ctx; 105 | } 106 | 107 | int free_ws_ctx(ws_ctx_t *ctx) { 108 | free(ctx->cin_buf); 109 | free(ctx->cout_buf); 110 | free(ctx->tin_buf); 111 | free(ctx->tout_buf); 112 | free(ctx); 113 | } 114 | 115 | ws_ctx_t *ws_socket(ws_ctx_t *ctx, int socket) { 116 | ctx->sockfd = socket; 117 | } 118 | 119 | int ws_socket_free(ws_ctx_t *ctx) { 120 | if (ctx->sockfd) { 121 | shutdown(ctx->sockfd, SHUT_RDWR); 122 | close(ctx->sockfd); 123 | ctx->sockfd = 0; 124 | } 125 | } 126 | 127 | /* ------------------------------------------------------- */ 128 | 129 | 130 | int encode_hixie(u_char const *src, size_t srclength, 131 | char *target, size_t targsize) { 132 | int sz = 0, len = 0; 133 | target[sz++] = '\x00'; 134 | len = ws_b64_ntop(src, srclength, target+sz, targsize-sz); 135 | if (len < 0) { 136 | return len; 137 | } 138 | sz += len; 139 | target[sz++] = '\xff'; 140 | return sz; 141 | } 142 | 143 | int decode_hixie(char *src, size_t srclength, 144 | u_char *target, size_t targsize, 145 | unsigned int *opcode, unsigned int *left) { 146 | char *start, *end, cntstr[4]; 147 | int i, len, framecount = 0, retlen = 0; 148 | unsigned char chr; 149 | if ((src[0] != '\x00') || (src[srclength-1] != '\xff')) { 150 | handler_emsg("WebSocket framing error\n"); 151 | return -1; 152 | } 153 | *left = srclength; 154 | 155 | if (srclength == 2 && 156 | (src[0] == '\xff') && 157 | (src[1] == '\x00')) { 158 | // client sent orderly close frame 159 | *opcode = 0x8; // Close frame 160 | return 0; 161 | } 162 | *opcode = 0x1; // Text frame 163 | 164 | start = src+1; // Skip '\x00' start 165 | do { 166 | /* We may have more than one frame */ 167 | end = (char *)memchr(start, '\xff', srclength); 168 | *end = '\x00'; 169 | len = ws_b64_pton(start, target+retlen, targsize-retlen); 170 | if (len < 0) { 171 | return len; 172 | } 173 | retlen += len; 174 | start = end + 2; // Skip '\xff' end and '\x00' start 175 | framecount++; 176 | } while (end < (src+srclength-1)); 177 | if (framecount > 1) { 178 | snprintf(cntstr, 3, "%d", framecount); 179 | traffic(cntstr); 180 | } 181 | *left = 0; 182 | return retlen; 183 | } 184 | 185 | int encode_hybi(u_char const *src, size_t srclength, 186 | char *target, size_t targsize, unsigned int opcode) 187 | { 188 | unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0; 189 | 190 | if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) { 191 | handler_emsg("Invalid opcode. Opcode must be 0x01 for text mode, or 0x02 for binary mode.\n"); 192 | return -1; 193 | } 194 | 195 | target[0] = (char)(opcode & 0x0F | 0x80); 196 | 197 | if ((int)srclength <= 0) { 198 | return 0; 199 | } 200 | 201 | if (opcode & OPCODE_TEXT) { 202 | len = ((srclength - 1) / 3) * 4 + 4; 203 | } else { 204 | len = srclength; 205 | } 206 | 207 | if (len <= 125) { 208 | target[1] = (char) len; 209 | payload_offset = 2; 210 | } else if ((len > 125) && (len < 65536)) { 211 | target[1] = (char) 126; 212 | *(u_short*)&(target[2]) = htons(len); 213 | payload_offset = 4; 214 | } else { 215 | handler_emsg("Sending frames larger than 65535 bytes not supported\n"); 216 | return -1; 217 | //target[1] = (char) 127; 218 | // *(u_long*)&(target[2]) = htonl(b64_sz); 219 | //payload_offset = 10; 220 | } 221 | 222 | if (opcode & OPCODE_TEXT) { 223 | len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset); 224 | } else { 225 | memcpy(target+payload_offset, src, srclength); 226 | len = srclength; 227 | } 228 | 229 | if (len < 0) { 230 | return len; 231 | } 232 | 233 | return len + payload_offset; 234 | } 235 | 236 | int decode_hybi(unsigned char *src, size_t srclength, 237 | u_char *target, size_t targsize, 238 | unsigned int *opcode, unsigned int *left) 239 | { 240 | unsigned char *frame, *mask, *payload, save_char, cntstr[4];; 241 | int masked = 0; 242 | int i = 0, len, framecount = 0; 243 | size_t remaining; 244 | unsigned int target_offset = 0, hdr_length = 0, payload_length = 0; 245 | 246 | *left = srclength; 247 | frame = src; 248 | 249 | //printf("Deocde new frame\n"); 250 | while (1) { 251 | // Need at least two bytes of the header 252 | // Find beginning of next frame. First time hdr_length, masked and 253 | // payload_length are zero 254 | frame += hdr_length + 4*masked + payload_length; 255 | //printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (tot: %d)\n", 256 | // (unsigned char) frame[0], 257 | // (unsigned char) frame[1], 258 | // (unsigned char) frame[2], 259 | // (unsigned char) frame[3], srclength); 260 | 261 | if (frame > src + srclength) { 262 | //printf("Truncated frame from client, need %d more bytes\n", frame - (src + srclength) ); 263 | break; 264 | } 265 | remaining = (src + srclength) - frame; 266 | if (remaining < 2) { 267 | //printf("Truncated frame header from client\n"); 268 | break; 269 | } 270 | framecount ++; 271 | 272 | *opcode = frame[0] & 0x0f; 273 | masked = (frame[1] & 0x80) >> 7; 274 | 275 | if (*opcode == 0x8) { 276 | // client sent orderly close frame 277 | break; 278 | } 279 | 280 | payload_length = frame[1] & 0x7f; 281 | if (payload_length < 126) { 282 | hdr_length = 2; 283 | //frame += 2 * sizeof(char); 284 | } else if (payload_length == 126) { 285 | payload_length = (frame[2] << 8) + frame[3]; 286 | hdr_length = 4; 287 | } else { 288 | handler_emsg("Receiving frames larger than 65535 bytes not supported\n"); 289 | return -1; 290 | } 291 | if ((hdr_length + 4*masked + payload_length) > remaining) { 292 | continue; 293 | } 294 | //printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining); 295 | payload = frame + hdr_length + 4*masked; 296 | 297 | if (*opcode != OPCODE_TEXT && *opcode != OPCODE_BINARY) { 298 | handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode); 299 | continue; 300 | } 301 | 302 | if (payload_length == 0) { 303 | handler_msg("Ignoring empty frame\n"); 304 | continue; 305 | } 306 | 307 | if ((payload_length > 0) && (!masked)) { 308 | handler_emsg("Received unmasked payload from client\n"); 309 | return -1; 310 | } 311 | 312 | // Terminate with a null for base64 decode 313 | save_char = payload[payload_length]; 314 | payload[payload_length] = '\0'; 315 | 316 | // unmask the data 317 | mask = payload - 4; 318 | for (i = 0; i < payload_length; i++) { 319 | payload[i] ^= mask[i%4]; 320 | } 321 | 322 | if (*opcode & OPCODE_TEXT) { 323 | // base64 decode the data 324 | len = ws_b64_pton((const char*)payload, target+target_offset, targsize); 325 | } else { 326 | memcpy(target+target_offset, payload, payload_length); 327 | len = payload_length; 328 | } 329 | 330 | // Restore the first character of the next frame 331 | payload[payload_length] = save_char; 332 | 333 | if (len < 0) { 334 | handler_emsg("Base64 decode error code %d\n%s\n", len, payload); 335 | return len; 336 | } 337 | 338 | // Restore the first character of the next frame 339 | payload[payload_length] = save_char; 340 | target_offset += len; 341 | 342 | //printf(" len %d, raw %s\n", len, frame); 343 | } 344 | 345 | if (framecount > 1) { 346 | snprintf(cntstr, 3, "%d", framecount); 347 | traffic(cntstr); 348 | } 349 | 350 | *left = remaining; 351 | return target_offset; 352 | } 353 | 354 | 355 | 356 | int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) { 357 | char *start, *end; 358 | headers_t *headers = ws_ctx->headers; 359 | 360 | headers->key1[0] = '\0'; 361 | headers->key2[0] = '\0'; 362 | headers->key3[0] = '\0'; 363 | 364 | if ((strlen(handshake) < 92) || (memcmp(handshake, "GET ", 4) != 0)) { 365 | return 0; 366 | } 367 | start = handshake+4; 368 | end = strstr(start, " HTTP/1.1"); 369 | if (!end) { return 0; } 370 | strncpy(headers->path, start, end-start); 371 | headers->path[end-start] = '\0'; 372 | 373 | start = strstr(handshake, "\r\nHost: "); 374 | if (!start) { return 0; } 375 | start += 8; 376 | end = strstr(start, "\r\n"); 377 | strncpy(headers->host, start, end-start); 378 | headers->host[end-start] = '\0'; 379 | 380 | headers->origin[0] = '\0'; 381 | start = strstr(handshake, "\r\nOrigin: "); 382 | if (start) { 383 | start += 10; 384 | } else { 385 | start = strstr(handshake, "\r\nSec-WebSocket-Origin: "); 386 | if (!start) { return 0; } 387 | start += 24; 388 | } 389 | end = strstr(start, "\r\n"); 390 | strncpy(headers->origin, start, end-start); 391 | headers->origin[end-start] = '\0'; 392 | 393 | start = strstr(handshake, "\r\nSec-WebSocket-Version: "); 394 | if (start) { 395 | // HyBi/RFC 6455 396 | start += 25; 397 | end = strstr(start, "\r\n"); 398 | strncpy(headers->version, start, end-start); 399 | headers->version[end-start] = '\0'; 400 | ws_ctx->hixie = 0; 401 | ws_ctx->hybi = strtol(headers->version, NULL, 10); 402 | 403 | start = strstr(handshake, "\r\nSec-WebSocket-Key: "); 404 | if (!start) { return 0; } 405 | start += 21; 406 | end = strstr(start, "\r\n"); 407 | strncpy(headers->key1, start, end-start); 408 | headers->key1[end-start] = '\0'; 409 | 410 | start = strstr(handshake, "\r\nConnection: "); 411 | if (!start) { return 0; } 412 | start += 14; 413 | end = strstr(start, "\r\n"); 414 | strncpy(headers->connection, start, end-start); 415 | headers->connection[end-start] = '\0'; 416 | 417 | start = strstr(handshake, "\r\nSec-WebSocket-Protocol: "); 418 | if (!start) { return 0; } 419 | start += 26; 420 | end = strstr(start, "\r\n"); 421 | strncpy(headers->protocols, start, end-start); 422 | headers->protocols[end-start] = '\0'; 423 | } else { 424 | // Hixie 75 or 76 425 | ws_ctx->hybi = 0; 426 | 427 | start = strstr(handshake, "\r\n\r\n"); 428 | if (!start) { return 0; } 429 | start += 4; 430 | if (strlen(start) == 8) { 431 | ws_ctx->hixie = 76; 432 | strncpy(headers->key3, start, 8); 433 | headers->key3[8] = '\0'; 434 | 435 | start = strstr(handshake, "\r\nSec-WebSocket-Key1: "); 436 | if (!start) { return 0; } 437 | start += 22; 438 | end = strstr(start, "\r\n"); 439 | strncpy(headers->key1, start, end-start); 440 | headers->key1[end-start] = '\0'; 441 | 442 | start = strstr(handshake, "\r\nSec-WebSocket-Key2: "); 443 | if (!start) { return 0; } 444 | start += 22; 445 | end = strstr(start, "\r\n"); 446 | strncpy(headers->key2, start, end-start); 447 | headers->key2[end-start] = '\0'; 448 | } else { 449 | ws_ctx->hixie = 75; 450 | } 451 | 452 | } 453 | 454 | return 1; 455 | } 456 | 457 | int parse_hixie76_key(char * key) { 458 | unsigned long i, spaces = 0, num = 0; 459 | for (i=0; i < strlen(key); i++) { 460 | if (key[i] == ' ') { 461 | spaces += 1; 462 | } 463 | if ((key[i] >= 48) && (key[i] <= 57)) { 464 | num = num * 10 + (key[i] - 48); 465 | } 466 | } 467 | return num / spaces; 468 | } 469 | 470 | int gen_md5(headers_t *headers, char *target) { 471 | unsigned long key1 = parse_hixie76_key(headers->key1); 472 | unsigned long key2 = parse_hixie76_key(headers->key2); 473 | char *key3 = headers->key3; 474 | 475 | MD5_CTX c; 476 | char in[HIXIE_MD5_DIGEST_LENGTH] = { 477 | key1 >> 24, key1 >> 16, key1 >> 8, key1, 478 | key2 >> 24, key2 >> 16, key2 >> 8, key2, 479 | key3[0], key3[1], key3[2], key3[3], 480 | key3[4], key3[5], key3[6], key3[7] 481 | }; 482 | 483 | MD5_Init(&c); 484 | MD5_Update(&c, (void *)in, sizeof in); 485 | MD5_Final((void *)target, &c); 486 | 487 | target[HIXIE_MD5_DIGEST_LENGTH] = '\0'; 488 | 489 | return 1; 490 | } 491 | 492 | static void gen_sha1(headers_t *headers, char *target) { 493 | SHA1_CTX c={0}; 494 | unsigned char hash[20]; 495 | int r; 496 | 497 | SHA1Init(&c); 498 | SHA1Update(&c, headers->key1, strlen(headers->key1)); 499 | SHA1Update(&c, HYBI_GUID, 36); 500 | SHA1Final(hash, &c); 501 | 502 | r = ws_b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN); 503 | //assert(r == HYBI10_ACCEPTHDRLEN - 1); 504 | } 505 | 506 | 507 | ws_ctx_t *do_handshake(int sock) { 508 | char handshake[4096], response[4096], sha1[29], trailer[17]; 509 | char *scheme, *pre; 510 | headers_t *headers; 511 | int len, ret, i, offset; 512 | ws_ctx_t * ws_ctx; 513 | char *response_protocol; 514 | 515 | // Peek, but don't read the data 516 | len = recv(sock, handshake, 1024, MSG_PEEK); 517 | handshake[len] = 0; 518 | if (len == 0) { 519 | handler_msg("ignoring empty handshake\n"); 520 | return NULL; 521 | } else { 522 | ws_ctx = alloc_ws_ctx(); 523 | ws_socket(ws_ctx, sock); 524 | if (! ws_ctx) { return NULL; } 525 | scheme = "ws"; 526 | handler_msg("using plain (not SSL) socket\n"); 527 | } 528 | offset = 0; 529 | for (i = 0; i < 10; i++) { 530 | /* (offset + 1): reserve one byte for the trailing '\0' */ 531 | if (0 > (len = ws_recv(ws_ctx, handshake + offset, sizeof(handshake) - (offset + 1)))) { 532 | handler_emsg("Read error during handshake: %m\n"); 533 | free_ws_ctx(ws_ctx); 534 | return NULL; 535 | } else if (0 == len) { 536 | handler_emsg("Client closed during handshake\n"); 537 | free_ws_ctx(ws_ctx); 538 | return NULL; 539 | } 540 | offset += len; 541 | handshake[offset] = 0; 542 | if (strstr(handshake, "\r\n\r\n")) { 543 | break; 544 | } else if (sizeof(handshake) <= (size_t)(offset + 1)) { 545 | handler_emsg("Oversized handshake\n"); 546 | free_ws_ctx(ws_ctx); 547 | return NULL; 548 | } else if (9 == i) { 549 | handler_emsg("Incomplete handshake\n"); 550 | free_ws_ctx(ws_ctx); 551 | return NULL; 552 | } 553 | usleep(10); 554 | } 555 | 556 | //handler_msg("handshake: %s\n", handshake); 557 | if (!parse_handshake(ws_ctx, handshake)) { 558 | handler_emsg("Invalid WS request\n"); 559 | free_ws_ctx(ws_ctx); 560 | return NULL; 561 | } 562 | 563 | headers = ws_ctx->headers; 564 | 565 | if (strstr(headers->protocols, "binary")) { 566 | ws_ctx->opcode = OPCODE_BINARY; 567 | response_protocol = "binary"; 568 | } else if (strstr(headers->protocols, "base64")) { 569 | ws_ctx->opcode = OPCODE_TEXT; 570 | response_protocol = "base64"; 571 | } else { 572 | handler_emsg("Invalid protocol '%s', expecting 'binary' or 'base64'\n", 573 | headers->protocols); 574 | return NULL; 575 | } 576 | 577 | if (ws_ctx->hybi > 0) { 578 | handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi); 579 | gen_sha1(headers, sha1); 580 | sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, response_protocol); 581 | } else { 582 | if (ws_ctx->hixie == 76) { 583 | handler_msg("using protocol Hixie 76\n"); 584 | gen_md5(headers, trailer); 585 | pre = "Sec-"; 586 | } else { 587 | handler_msg("using protocol Hixie 75\n"); 588 | trailer[0] = '\0'; 589 | pre = ""; 590 | } 591 | sprintf(response, SERVER_HANDSHAKE_HIXIE, pre, headers->origin, pre, scheme, 592 | headers->host, headers->path, pre, "base64", trailer); 593 | } 594 | 595 | //handler_msg("response: %s\n", response); 596 | ws_send(ws_ctx, response, strlen(response)); 597 | 598 | return ws_ctx; 599 | } 600 | 601 | void signal_handler(sig) { 602 | switch (sig) { 603 | case SIGHUP: 604 | if (settings.whitelist_host != NULL ) 605 | load_whitelist_host(); 606 | 607 | if (settings.whitelist_port != NULL ) 608 | load_whitelist_port(); 609 | break; 610 | case SIGPIPE: pipe_error = 1; break; // handle inline 611 | case SIGTERM: 612 | remove(settings.pid); 613 | exit(0); 614 | break; 615 | } 616 | } 617 | 618 | void daemonize(int keepfd) { 619 | int pid, i; 620 | 621 | umask(0); 622 | chdir("/"); 623 | setgid(getgid()); 624 | setuid(getuid()); 625 | 626 | /* Double fork to daemonize */ 627 | pid = fork(); 628 | if (pid<0) { fatal("fork error"); } 629 | if (pid>0) { exit(0); } // parent exits 630 | setsid(); // Obtain new process group 631 | pid = fork(); 632 | if (pid<0) { fatal("fork error"); } 633 | if (pid>0) { 634 | // parent exits 635 | FILE *pidf = fopen(settings.pid, "w"); 636 | if (pidf) { 637 | fprintf(pidf, "%d", pid); 638 | fclose(pidf); 639 | } else { 640 | fprintf(stderr, "Could not write daemon PID file '%s': %s\n", settings.pid, strerror(errno)); 641 | } 642 | exit(0); 643 | } 644 | 645 | /* Signal handling */ 646 | signal(SIGHUP, signal_handler); // catch HUP 647 | signal(SIGTERM, signal_handler); // catch kill 648 | 649 | /* Close open files */ 650 | for (i=getdtablesize(); i>=0; --i) { 651 | if (i != keepfd) { 652 | close(i); 653 | } else if (settings.verbose) { 654 | printf("keeping fd %d\n", keepfd); 655 | } 656 | } 657 | i=open("/dev/null", O_RDWR); // Redirect stdin 658 | dup(i); // Redirect stdout 659 | dup(i); // Redirect stderr 660 | } 661 | 662 | 663 | void start_server() { 664 | int lsock, csock, pid, clilen, sopt = 1, i; 665 | struct sockaddr_in serv_addr, cli_addr; 666 | ws_ctx_t *ws_ctx; 667 | 668 | 669 | /* Initialize buffers */ 670 | lsock = socket(AF_INET, SOCK_STREAM, 0); 671 | if (lsock < 0) { error("ERROR creating listener socket"); } 672 | bzero((char *) &serv_addr, sizeof(serv_addr)); 673 | serv_addr.sin_family = AF_INET; 674 | serv_addr.sin_port = htons(settings.listen_port); 675 | 676 | /* Resolve listen address */ 677 | if (settings.listen_host && (settings.listen_host[0] != '\0')) { 678 | if (resolve_host(&serv_addr.sin_addr, settings.listen_host) < -1) { 679 | fatal("Could not resolve listen address"); 680 | } 681 | } else { 682 | serv_addr.sin_addr.s_addr = INADDR_ANY; 683 | } 684 | 685 | setsockopt(lsock, SOL_SOCKET, SO_REUSEADDR, (char *)&sopt, sizeof(sopt)); 686 | if (bind(lsock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { 687 | fatal("ERROR on binding listener socket"); 688 | } 689 | listen(lsock,100); 690 | 691 | signal(SIGPIPE, signal_handler); // catch pipe 692 | 693 | if (settings.daemon) { 694 | daemonize(lsock); 695 | } 696 | 697 | 698 | // Reep zombies 699 | signal(SIGCHLD, SIG_IGN); 700 | 701 | printf("Waiting for connections on %s:%d\n", 702 | settings.listen_host, settings.listen_port); 703 | 704 | while (1) { 705 | clilen = sizeof(cli_addr); 706 | pipe_error = 0; 707 | pid = 0; 708 | csock = accept(lsock, 709 | (struct sockaddr *) &cli_addr, 710 | &clilen); 711 | if (csock < 0) { 712 | error("ERROR on accept"); 713 | continue; 714 | } 715 | handler_msg("got client connection from %s\n", 716 | inet_ntoa(cli_addr.sin_addr)); 717 | 718 | if (!settings.run_once) { 719 | handler_msg("forking handler process\n"); 720 | pid = fork(); 721 | } 722 | 723 | if (pid == 0) { // handler process 724 | ws_ctx = do_handshake(csock); 725 | if (settings.run_once) { 726 | if (ws_ctx == NULL) { 727 | // Not a real WebSocket connection 728 | continue; 729 | } else { 730 | // Successful connection, stop listening for new 731 | // connections 732 | close(lsock); 733 | } 734 | } 735 | if (ws_ctx == NULL) { 736 | handler_msg("No connection after handshake\n"); 737 | break; // Child process exits 738 | } 739 | 740 | settings.handler(ws_ctx); 741 | if (pipe_error) { 742 | handler_emsg("Closing due to SIGPIPE\n"); 743 | } 744 | break; // Child process exits 745 | } else { // parent process 746 | settings.handler_id += 1; 747 | } 748 | } 749 | if (pid == 0) { 750 | if (ws_ctx) { 751 | ws_socket_free(ws_ctx); 752 | free_ws_ctx(ws_ctx); 753 | } else { 754 | shutdown(csock, SHUT_RDWR); 755 | close(csock); 756 | } 757 | handler_msg("handler exit\n"); 758 | } else { 759 | handler_msg("websockify exit\n"); 760 | } 761 | 762 | } 763 | 764 | -------------------------------------------------------------------------------- /websocket.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #define BUFSIZE 65536 4 | #define DBUFSIZE (BUFSIZE * 3) / 4 - 20 5 | 6 | #define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ 7 | Upgrade: WebSocket\r\n\ 8 | Connection: Upgrade\r\n\ 9 | %sWebSocket-Origin: %s\r\n\ 10 | %sWebSocket-Location: %s://%s%s\r\n\ 11 | %sWebSocket-Protocol: %s\r\n\ 12 | \r\n%s" 13 | 14 | #define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\ 15 | Upgrade: websocket\r\n\ 16 | Connection: Upgrade\r\n\ 17 | Sec-WebSocket-Accept: %s\r\n\ 18 | Sec-WebSocket-Protocol: %s\r\n\ 19 | \r\n" 20 | 21 | #define HYBI_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 22 | 23 | #define HYBI10_ACCEPTHDRLEN 29 24 | 25 | #define HIXIE_MD5_DIGEST_LENGTH 16 26 | 27 | #define POLICY_RESPONSE "\n" 28 | 29 | #define OPCODE_TEXT 0x01 30 | #define OPCODE_BINARY 0x02 31 | 32 | typedef struct { 33 | char path[1024+1]; 34 | char host[1024+1]; 35 | char origin[1024+1]; 36 | char version[1024+1]; 37 | char connection[1024+1]; 38 | char protocols[1024+1]; 39 | char key1[1024+1]; 40 | char key2[1024+1]; 41 | char key3[8+1]; 42 | } headers_t; 43 | 44 | typedef struct { 45 | int sockfd; 46 | int hixie; 47 | int hybi; 48 | int opcode; 49 | headers_t *headers; 50 | char *cin_buf; 51 | char *cout_buf; 52 | char *tin_buf; 53 | char *tout_buf; 54 | struct sockaddr_in udpaddr; 55 | int udp; 56 | } ws_ctx_t; 57 | 58 | typedef struct { 59 | int verbose; 60 | char listen_host[256]; 61 | int listen_port; 62 | void (*handler)(ws_ctx_t*); 63 | int handler_id; 64 | int daemon; 65 | int run_once; 66 | char *whitelist_port; 67 | char *whitelist_host; 68 | 69 | char *pattern; 70 | char *pid; 71 | } settings_t; 72 | 73 | 74 | ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len); 75 | 76 | ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len); 77 | 78 | /* base64.c declarations */ 79 | //int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize); 80 | //int b64_pton(char const *src, u_char *target, size_t targsize); 81 | 82 | #define gen_handler_msg(stream, ...) \ 83 | if (! settings.daemon) { \ 84 | fprintf(stream, " %d: ", settings.handler_id); \ 85 | fprintf(stream, __VA_ARGS__); \ 86 | } 87 | 88 | #define handler_msg(...) gen_handler_msg(stdout, __VA_ARGS__); 89 | #define handler_emsg(...) gen_handler_msg(stderr, __VA_ARGS__); 90 | 91 | -------------------------------------------------------------------------------- /websockify.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A WebSocket to TCP socket proxy with support for "wss://" encryption. 3 | * Copyright 2010 Joel Martin 4 | * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) 5 | * 6 | * You can make a cert/key with openssl using: 7 | * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem 8 | * as taken from http://docs.python.org/dev/library/ssl.html#certificates 9 | */ 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include "websocket.h" 22 | 23 | char traffic_legend[] = "\n\ 24 | Traffic Legend:\n\ 25 | } - Client receive\n\ 26 | }. - Client receive partial\n\ 27 | { - Target receive\n\ 28 | \n\ 29 | > - Target send\n\ 30 | >. - Target send partial\n\ 31 | < - Client send\n\ 32 | <. - Client send partial\n\ 33 | "; 34 | 35 | char USAGE[] = "Usage: wsproxy [options] " \ 36 | "[source_addr:]source_port\n\n" \ 37 | " --verbose|-v verbose messages and per frame traffic\n" \ 38 | " --daemon|-d become a daemon (background process)\n" \ 39 | " --whitelist-hosts|-W LIST new-line separated target host whitelist file\n" \ 40 | " --whitelist-ports|-P LIST new-line separated target port whitelist file\n" \ 41 | 42 | 43 | " --pid|-p desired path of pid file. Default: '/var/run/websockify.pid'"; 44 | 45 | #define usage(fmt, args...) \ 46 | do { \ 47 | fprintf(stderr, "%s\n\n", USAGE); \ 48 | fprintf(stderr, fmt , ## args); \ 49 | exit(1); \ 50 | } while(0) 51 | 52 | char target_host[256]; 53 | int target_port; 54 | int *target_ports; 55 | int *target_hosts; 56 | extern pipe_error; 57 | extern settings_t settings; 58 | 59 | void do_proxy(ws_ctx_t *ws_ctx, int target) { 60 | fd_set rlist, wlist, elist; 61 | struct timeval tv; 62 | int i, maxfd, client = ws_ctx->sockfd; 63 | unsigned int opcode, left, ret; 64 | unsigned int tout_start, tout_end, cout_start, cout_end; 65 | unsigned int tin_start, tin_end; 66 | ssize_t len, bytes; 67 | 68 | tout_start = tout_end = cout_start = cout_end; 69 | tin_start = tin_end = 0; 70 | maxfd = client > target ? client+1 : target+1; 71 | 72 | while (1) { 73 | tv.tv_sec = 1; 74 | tv.tv_usec = 0; 75 | 76 | FD_ZERO(&rlist); 77 | FD_ZERO(&wlist); 78 | FD_ZERO(&elist); 79 | 80 | FD_SET(client, &elist); 81 | FD_SET(target, &elist); 82 | 83 | if (tout_end == tout_start) { 84 | // Nothing queued for target, so read from client 85 | FD_SET(client, &rlist); 86 | } else { 87 | // Data queued for target, so write to it 88 | FD_SET(target, &wlist); 89 | } 90 | if (cout_end == cout_start) { 91 | // Nothing queued for client, so read from target 92 | FD_SET(target, &rlist); 93 | } else { 94 | // Data queued for client, so write to it 95 | FD_SET(client, &wlist); 96 | } 97 | 98 | ret = select(maxfd, &rlist, &wlist, &elist, &tv); 99 | if (pipe_error) { break; } 100 | 101 | if (FD_ISSET(target, &elist)) { 102 | handler_emsg("target exception\n"); 103 | break; 104 | } 105 | if (FD_ISSET(client, &elist)) { 106 | handler_emsg("client exception\n"); 107 | break; 108 | } 109 | 110 | if (ret == -1) { 111 | handler_emsg("select(): %s\n", strerror(errno)); 112 | break; 113 | } else if (ret == 0) { 114 | //handler_emsg("select timeout\n"); 115 | continue; 116 | } 117 | 118 | if (FD_ISSET(target, &wlist)) { 119 | len = tout_end-tout_start; 120 | bytes = sendto(target, ws_ctx->tout_buf + tout_start, len, 0, &ws_ctx->udpaddr, sizeof(ws_ctx->udpaddr)); 121 | if (pipe_error) { break; } 122 | if (bytes < 0) { 123 | handler_emsg("target connection error: %s\n", 124 | strerror(errno)); 125 | break; 126 | } 127 | tout_start += bytes; 128 | if (tout_start >= tout_end) { 129 | tout_start = tout_end = 0; 130 | traffic(">"); 131 | } else { 132 | traffic(">."); 133 | } 134 | } 135 | 136 | if (FD_ISSET(client, &wlist)) { 137 | len = cout_end-cout_start; 138 | bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len); 139 | if (pipe_error) { break; } 140 | if (len < 3) { 141 | handler_emsg("len: %d, bytes: %d: %d\n", 142 | (int) len, (int) bytes, 143 | (int) *(ws_ctx->cout_buf + cout_start)); 144 | } 145 | cout_start += bytes; 146 | if (cout_start >= cout_end) { 147 | cout_start = cout_end = 0; 148 | traffic("<"); 149 | } else { 150 | traffic("<."); 151 | } 152 | } 153 | 154 | if (FD_ISSET(target, &rlist)) { 155 | bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0); 156 | if (pipe_error) { break; } 157 | if (bytes <= 0) { 158 | handler_emsg("target closed connection\n"); 159 | break; 160 | } 161 | cout_start = 0; 162 | if (ws_ctx->hybi) { 163 | cout_end = encode_hybi(ws_ctx->cin_buf, bytes, 164 | ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode); 165 | } else { 166 | cout_end = encode_hixie(ws_ctx->cin_buf, bytes, 167 | ws_ctx->cout_buf, BUFSIZE); 168 | } 169 | /* 170 | printf("encoded: "); 171 | for (i=0; i< cout_end; i++) { 172 | printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i)); 173 | } 174 | printf("\n"); 175 | */ 176 | if (cout_end < 0) { 177 | handler_emsg("encoding error\n"); 178 | break; 179 | } 180 | traffic("{"); 181 | } 182 | 183 | if (FD_ISSET(client, &rlist)) { 184 | bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1); 185 | if (pipe_error) { break; } 186 | if (bytes <= 0) { 187 | handler_emsg("client closed connection\n"); 188 | break; 189 | } 190 | tin_end += bytes; 191 | /* 192 | printf("before decode: "); 193 | for (i=0; i< bytes; i++) { 194 | printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i)); 195 | } 196 | printf("\n"); 197 | */ 198 | if (ws_ctx->hybi) { 199 | len = decode_hybi(ws_ctx->tin_buf + tin_start, 200 | tin_end-tin_start, 201 | ws_ctx->tout_buf, BUFSIZE-1, 202 | &opcode, &left); 203 | } else { 204 | len = decode_hixie(ws_ctx->tin_buf + tin_start, 205 | tin_end-tin_start, 206 | ws_ctx->tout_buf, BUFSIZE-1, 207 | &opcode, &left); 208 | } 209 | 210 | if (opcode == 8) { 211 | handler_msg("client sent orderly close frame\n"); 212 | break; 213 | } 214 | 215 | /* 216 | printf("decoded: "); 217 | for (i=0; i< len; i++) { 218 | printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i)); 219 | } 220 | printf("\n"); 221 | */ 222 | 223 | if (len < 0) { 224 | handler_emsg("decoding error\n"); 225 | break; 226 | } 227 | if (left) { 228 | tin_start = tin_end - left; 229 | //printf("partial frame from client"); 230 | } else { 231 | tin_start = 0; 232 | tin_end = 0; 233 | } 234 | 235 | traffic("}"); 236 | tout_start = 0; 237 | tout_end = len; 238 | } 239 | } 240 | } 241 | 242 | void proxy_handler(ws_ctx_t *ws_ctx) { 243 | int tsock = 0; 244 | struct sockaddr_in taddr = {0}; 245 | struct sockaddr_in addr = {0}; 246 | char protocol = 't'; 247 | char dummy; 248 | 249 | sscanf(ws_ctx->headers->path+1, "%c%c%[^:]%c%d" , &protocol, &dummy, &target_host, &dummy,&target_port); 250 | 251 | if (target_ports != NULL) { 252 | int *p; 253 | int found = 0; 254 | for (p = target_ports; *p; p++) { 255 | if (*p == target_port) { 256 | found = 1; 257 | break; 258 | } 259 | } 260 | if (!found) { 261 | handler_emsg("Rejecting connection to non-whitelisted port: '%d'\n", 262 | target_port); 263 | return; 264 | } 265 | } 266 | 267 | /* Resolve target address */ 268 | if (resolve_host(&taddr.sin_addr, target_host) < -1) { 269 | handler_emsg("Could not resolve target address: %s\n", 270 | strerror(errno)); 271 | } 272 | 273 | if (target_hosts != NULL) { 274 | int *p; 275 | int found = 0; 276 | for (p = target_hosts; *p; p++) { 277 | if (*p == *((int*)&taddr.sin_addr)) { 278 | found = 1; 279 | break; 280 | } 281 | } 282 | if (!found) { 283 | handler_emsg("Rejecting connection to non-whitelisted host: '%s'\n", 284 | target_host); 285 | return; 286 | } 287 | } 288 | 289 | handler_msg("connecting to: %s:%d via %c\n", target_host, target_port, protocol); 290 | 291 | if( protocol == 'u' ) 292 | tsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 293 | else 294 | tsock = socket(AF_INET, SOCK_STREAM, 0); 295 | 296 | if (tsock < 0) { 297 | handler_emsg("Could not create target socket: %s\n", 298 | strerror(errno)); 299 | return; 300 | } 301 | //bzero((char *) &taddr, sizeof(taddr)); 302 | taddr.sin_family = AF_INET; 303 | taddr.sin_port = htons(target_port); 304 | 305 | if ( protocol == 't' ) { 306 | if (connect(tsock, (struct sockaddr *) &taddr, sizeof(taddr)) < 0) { 307 | handler_emsg("Could not connect to target: %s\n", 308 | strerror(errno)); 309 | close(tsock); 310 | return; 311 | } 312 | } 313 | else { 314 | addr.sin_addr.s_addr = INADDR_ANY; 315 | addr.sin_port = 0; 316 | addr.sin_family = AF_INET; 317 | if( bind( tsock, (void *)&addr, sizeof( addr ) ) < 0 ) 318 | { 319 | handler_emsg("Could not bind udp socket: %s\n", 320 | strerror(errno)); 321 | close(tsock); 322 | return; 323 | } 324 | ws_ctx->udpaddr = taddr; 325 | ws_ctx->udp = 1; 326 | } 327 | 328 | if ((settings.verbose) && (! settings.daemon)) { 329 | printf("%s", traffic_legend); 330 | } 331 | 332 | do_proxy(ws_ctx, tsock); 333 | 334 | shutdown(tsock, SHUT_RDWR); 335 | close(tsock); 336 | } 337 | 338 | int load_whitelist_port() { 339 | printf("loading port whitelist '%s'\n", settings.whitelist_port); 340 | FILE *whitelist = fopen(settings.whitelist_port, "r"); 341 | if (whitelist == NULL) { 342 | fprintf(stderr, "Error opening whitelist file '%s':\n\t%s\n", 343 | settings.whitelist_port, strerror(errno)); 344 | return -1; 345 | } 346 | 347 | const int tplen_grow = 512; 348 | int tplen = tplen_grow, tpcount = 0; 349 | target_ports = (int*)malloc(tplen*sizeof(int)); 350 | if (target_ports == NULL) { 351 | fprintf(stderr, "Whitelist port malloc error"); 352 | return -2; 353 | } 354 | 355 | char *line = NULL; 356 | ssize_t n = 0, nread = 0; 357 | while ((nread = getline(&line, &n, whitelist)) > 0) { 358 | if (line[0] == '\n') continue; 359 | line[nread-1] = '\x00'; 360 | long int port = strtol(line, NULL, 10); 361 | if (port < 1 || port > 65535) { 362 | fprintf(stderr, 363 | "Whitelist port '%s' is not between valid range 1 and 65535", line); 364 | return -3; 365 | } 366 | tpcount++; 367 | if (tpcount >= tplen) { 368 | tplen += tplen_grow; 369 | target_ports = (int*)realloc(target_ports, tplen*sizeof(int)); 370 | if (target_ports == NULL) { 371 | fprintf(stderr, "Whitelist port realloc error"); 372 | return -2; 373 | } 374 | } 375 | target_ports[tpcount-1] = port; 376 | } 377 | if (line != NULL) free(line); 378 | 379 | if (tpcount == 0) { 380 | fprintf(stderr, "0 ports read from whitelist file '%s'\n", 381 | settings.whitelist_port); 382 | return -4; 383 | } 384 | 385 | target_ports = (int*)realloc(target_ports, (tpcount + 1)*sizeof(int)); 386 | if (target_ports == NULL) { 387 | fprintf(stderr, "Whitelist port realloc error"); 388 | return -2; 389 | } 390 | target_ports[tpcount] = 0; 391 | return 0; 392 | } 393 | 394 | int load_whitelist_host() { 395 | printf("loading host whitelist '%s'\n", settings.whitelist_host); 396 | FILE *whitelist = fopen(settings.whitelist_host, "r"); 397 | if (whitelist == NULL) { 398 | fprintf(stderr, "Error opening whitelist file '%s':\n\t%s\n", 399 | settings.whitelist_host, strerror(errno)); 400 | return -1; 401 | } 402 | 403 | const int tplen_grow = 512; 404 | int tplen = tplen_grow, tpcount = 0; 405 | target_hosts = (int*)malloc(tplen*sizeof(int)); 406 | if (target_hosts == NULL) { 407 | fprintf(stderr, "Whitelist port malloc error"); 408 | return -2; 409 | } 410 | 411 | char *line = NULL; 412 | ssize_t n = 0, nread = 0; 413 | while ((nread = getline(&line, &n, whitelist)) > 0) { 414 | if (line[0] == '\n') continue; 415 | line[nread-1] = '\x00'; 416 | int host; 417 | 418 | if (resolve_host(&host, line) < -1 ) { 419 | fprintf(stderr, 420 | "Whitelist host '%s': failed to resolve\n", line); 421 | //return -3; 422 | continue; 423 | } 424 | tpcount++; 425 | if (tpcount >= tplen) { 426 | tplen += tplen_grow; 427 | target_hosts = (int*)realloc(target_hosts, tplen*sizeof(int)); 428 | if (target_hosts == NULL) { 429 | fprintf(stderr, "Whitelist port realloc error\n"); 430 | return -2; 431 | } 432 | } 433 | target_hosts[tpcount-1] = host; 434 | } 435 | if (line != NULL) free(line); 436 | 437 | if (tpcount == 0) { 438 | fprintf(stderr, "0 ports read from whitelist file '%s'\n", 439 | settings.whitelist_port); 440 | return -4; 441 | } 442 | 443 | target_hosts = (int*)realloc(target_hosts, (tpcount + 1)*sizeof(int)); 444 | if (target_hosts == NULL) { 445 | fprintf(stderr, "Whitelist port realloc error\n"); 446 | return -2; 447 | } 448 | target_hosts[tpcount] = 0; 449 | return 0; 450 | } 451 | 452 | int main(int argc, char *argv[]) 453 | { 454 | int fd, c, option_index = 0; 455 | char *found; 456 | static struct option long_options[] = { 457 | {"verbose", no_argument, 0, 'v'}, 458 | {"daemon", no_argument, 0, 'd'}, 459 | /* ---- */ 460 | {"whitelist-ports", required_argument, 0, 'P'}, 461 | {"whitelist-hosts", required_argument, 0, 'W'}, 462 | {"pid", required_argument, 0, 'p'}, 463 | {0, 0, 0, 0} 464 | }; 465 | 466 | settings.pattern = "/%d"; 467 | settings.pid = "/var/run/websockify.pid"; 468 | 469 | while (1) { 470 | c = getopt_long (argc, argv, "vdW:p:P:", 471 | long_options, &option_index); 472 | 473 | /* Detect the end */ 474 | if (c == -1) break; 475 | 476 | switch (c) { 477 | case 0: 478 | break; // ignore 479 | case 1: 480 | break; // ignore 481 | case 'v': 482 | settings.verbose = 1; 483 | break; 484 | case 'd': 485 | settings.daemon = 1; 486 | break; 487 | case 'W': 488 | settings.whitelist_host = realpath(optarg, NULL); 489 | if (! settings.whitelist_host) { 490 | usage("No whitelist file at %s\n", optarg); 491 | } 492 | break; 493 | case 'P': 494 | settings.whitelist_port = realpath(optarg, NULL); 495 | if (! settings.whitelist_port) { 496 | usage("No whitelist file at %s\n", optarg); 497 | } 498 | break; 499 | case 'p': 500 | settings.pid = optarg; 501 | break; 502 | default: 503 | usage(" "); 504 | } 505 | } 506 | 507 | if ((argc-optind) != 1) { 508 | usage("Invalid number of arguments\n"); 509 | } 510 | 511 | found = strstr(argv[optind], ":"); 512 | if (found) { 513 | memcpy(settings.listen_host, argv[optind], found-argv[optind]); 514 | settings.listen_port = strtol(found+1, NULL, 10); 515 | } else { 516 | settings.listen_host[0] = '\0'; 517 | settings.listen_port = strtol(argv[optind], NULL, 10); 518 | } 519 | optind++; 520 | if (settings.listen_port == 0) { 521 | usage("Could not parse listen_port\n"); 522 | } 523 | 524 | if (!found && settings.whitelist_host != NULL) { 525 | if (load_whitelist_host()) { 526 | usage("Whitelist hosts error."); 527 | } 528 | 529 | } 530 | 531 | if (!found && settings.whitelist_port != NULL) { 532 | if (load_whitelist_port()) { 533 | usage("Whitelist ports error."); 534 | } 535 | 536 | } 537 | 538 | //printf(" verbose: %d\n", settings.verbose); 539 | //printf(" daemon: %d\n", settings.daemon); 540 | //printf(" run_once: %d\n", settings.run_once); 541 | 542 | settings.handler = proxy_handler; 543 | start_server(); 544 | 545 | } 546 | --------------------------------------------------------------------------------