Merge 0ec8b7008c into 414d1d8b44
This commit is contained in:
commit
4266dfa88c
|
|
@ -0,0 +1 @@
|
||||||
|
websockify
|
||||||
|
|
@ -3,11 +3,12 @@ CFLAGS += -fPIC
|
||||||
|
|
||||||
all: $(TARGETS)
|
all: $(TARGETS)
|
||||||
|
|
||||||
websockify: websockify.o websocket.o
|
websockify: websockify.o websocket.o base64.o
|
||||||
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@
|
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -o $@
|
||||||
|
|
||||||
websocket.o: websocket.c websocket.h
|
websocket.o: websocket.c websocket.h
|
||||||
websockify.o: websockify.c websocket.h
|
websockify.o: websockify.c websocket.h
|
||||||
|
base64.o: base64.c
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -f websockify *.o
|
rm -f websockify *.o
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,313 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1996-1999 by Internet Software Consortium.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
||||||
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
||||||
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||||
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Portions Copyright (c) 1995 by International Business Machines, Inc.
|
||||||
|
*
|
||||||
|
* International Business Machines, Inc. (hereinafter called IBM) grants
|
||||||
|
* permission under its copyrights to use, copy, modify, and distribute this
|
||||||
|
* Software with or without fee, provided that the above copyright notice and
|
||||||
|
* all paragraphs of this notice appear in all copies, and that the name of IBM
|
||||||
|
* not be used in connection with the marketing of any product incorporating
|
||||||
|
* the Software or modifications thereof, without specific, written prior
|
||||||
|
* permission.
|
||||||
|
*
|
||||||
|
* To the extent it has a right to do so, IBM grants an immunity from suit
|
||||||
|
* under its patents, if any, for the use, sale or manufacture of products to
|
||||||
|
* the extent that such products are used for performing Domain Name System
|
||||||
|
* dynamic updates in TCP/IP networks by means of the Software. No immunity is
|
||||||
|
* granted for any product per se or for any other function of any product.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES,
|
||||||
|
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL,
|
||||||
|
* DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN
|
||||||
|
* IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(LINT) && !defined(CODECENTER)
|
||||||
|
static const char rcsid[] = "$BINDId: base64.c,v 8.7 1999/10/13 16:39:33 vixie Exp $";
|
||||||
|
#endif /* not lint */
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <arpa/nameser.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <resolv.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define Assert(Cond) if (!(Cond)) abort()
|
||||||
|
|
||||||
|
static const char Base64[] =
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
static const char Pad64 = '=';
|
||||||
|
|
||||||
|
/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt)
|
||||||
|
The following encoding technique is taken from RFC 1521 by Borenstein
|
||||||
|
and Freed. It is reproduced here in a slightly edited form for
|
||||||
|
convenience.
|
||||||
|
|
||||||
|
A 65-character subset of US-ASCII is used, enabling 6 bits to be
|
||||||
|
represented per printable character. (The extra 65th character, "=",
|
||||||
|
is used to signify a special processing function.)
|
||||||
|
|
||||||
|
The encoding process represents 24-bit groups of input bits as output
|
||||||
|
strings of 4 encoded characters. Proceeding from left to right, a
|
||||||
|
24-bit input group is formed by concatenating 3 8-bit input groups.
|
||||||
|
These 24 bits are then treated as 4 concatenated 6-bit groups, each
|
||||||
|
of which is translated into a single digit in the base64 alphabet.
|
||||||
|
|
||||||
|
Each 6-bit group is used as an index into an array of 64 printable
|
||||||
|
characters. The character referenced by the index is placed in the
|
||||||
|
output string.
|
||||||
|
|
||||||
|
Table 1: The Base64 Alphabet
|
||||||
|
|
||||||
|
Value Encoding Value Encoding Value Encoding Value Encoding
|
||||||
|
0 A 17 R 34 i 51 z
|
||||||
|
1 B 18 S 35 j 52 0
|
||||||
|
2 C 19 T 36 k 53 1
|
||||||
|
3 D 20 U 37 l 54 2
|
||||||
|
4 E 21 V 38 m 55 3
|
||||||
|
5 F 22 W 39 n 56 4
|
||||||
|
6 G 23 X 40 o 57 5
|
||||||
|
7 H 24 Y 41 p 58 6
|
||||||
|
8 I 25 Z 42 q 59 7
|
||||||
|
9 J 26 a 43 r 60 8
|
||||||
|
10 K 27 b 44 s 61 9
|
||||||
|
11 L 28 c 45 t 62 +
|
||||||
|
12 M 29 d 46 u 63 /
|
||||||
|
13 N 30 e 47 v
|
||||||
|
14 O 31 f 48 w (pad) =
|
||||||
|
15 P 32 g 49 x
|
||||||
|
16 Q 33 h 50 y
|
||||||
|
|
||||||
|
Special processing is performed if fewer than 24 bits are available
|
||||||
|
at the end of the data being encoded. A full encoding quantum is
|
||||||
|
always completed at the end of a quantity. When fewer than 24 input
|
||||||
|
bits are available in an input group, zero bits are added (on the
|
||||||
|
right) to form an integral number of 6-bit groups. Padding at the
|
||||||
|
end of the data is performed using the '=' character.
|
||||||
|
|
||||||
|
Since all base64 input is an integral number of octets, only the
|
||||||
|
-------------------------------------------------
|
||||||
|
following cases can arise:
|
||||||
|
|
||||||
|
(1) the final quantum of encoding input is an integral
|
||||||
|
multiple of 24 bits; here, the final unit of encoded
|
||||||
|
output will be an integral multiple of 4 characters
|
||||||
|
with no "=" padding,
|
||||||
|
(2) the final quantum of encoding input is exactly 8 bits;
|
||||||
|
here, the final unit of encoded output will be two
|
||||||
|
characters followed by two "=" padding characters, or
|
||||||
|
(3) the final quantum of encoding input is exactly 16 bits;
|
||||||
|
here, the final unit of encoded output will be three
|
||||||
|
characters followed by one "=" padding character.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
ws_b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize) {
|
||||||
|
size_t datalength = 0;
|
||||||
|
u_char input[3];
|
||||||
|
u_char output[4];
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
while (2 < srclength) {
|
||||||
|
input[0] = *src++;
|
||||||
|
input[1] = *src++;
|
||||||
|
input[2] = *src++;
|
||||||
|
srclength -= 3;
|
||||||
|
|
||||||
|
output[0] = input[0] >> 2;
|
||||||
|
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
|
||||||
|
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
|
||||||
|
output[3] = input[2] & 0x3f;
|
||||||
|
Assert(output[0] < 64);
|
||||||
|
Assert(output[1] < 64);
|
||||||
|
Assert(output[2] < 64);
|
||||||
|
Assert(output[3] < 64);
|
||||||
|
|
||||||
|
if (datalength + 4 > targsize)
|
||||||
|
return (-1);
|
||||||
|
target[datalength++] = Base64[output[0]];
|
||||||
|
target[datalength++] = Base64[output[1]];
|
||||||
|
target[datalength++] = Base64[output[2]];
|
||||||
|
target[datalength++] = Base64[output[3]];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we worry about padding. */
|
||||||
|
if (0 != srclength) {
|
||||||
|
/* Get what's left. */
|
||||||
|
input[0] = input[1] = input[2] = '\0';
|
||||||
|
for (i = 0; i < srclength; i++)
|
||||||
|
input[i] = *src++;
|
||||||
|
|
||||||
|
output[0] = input[0] >> 2;
|
||||||
|
output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4);
|
||||||
|
output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6);
|
||||||
|
Assert(output[0] < 64);
|
||||||
|
Assert(output[1] < 64);
|
||||||
|
Assert(output[2] < 64);
|
||||||
|
|
||||||
|
if (datalength + 4 > targsize)
|
||||||
|
return (-1);
|
||||||
|
target[datalength++] = Base64[output[0]];
|
||||||
|
target[datalength++] = Base64[output[1]];
|
||||||
|
if (srclength == 1)
|
||||||
|
target[datalength++] = Pad64;
|
||||||
|
else
|
||||||
|
target[datalength++] = Base64[output[2]];
|
||||||
|
target[datalength++] = Pad64;
|
||||||
|
}
|
||||||
|
if (datalength >= targsize)
|
||||||
|
return (-1);
|
||||||
|
target[datalength] = '\0'; /* Returned value doesn't count \0. */
|
||||||
|
return (datalength);
|
||||||
|
}
|
||||||
|
//libresolv_hidden_def (b64_ntop)
|
||||||
|
|
||||||
|
/* skips all whitespace anywhere.
|
||||||
|
converts characters, four at a time, starting at (or after)
|
||||||
|
src from base - 64 numbers into three 8 bit bytes in the target area.
|
||||||
|
it returns the number of data bytes stored at the target, or -1 on error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int
|
||||||
|
ws_b64_pton(char const *src, u_char *target, size_t targsize) {
|
||||||
|
int tarindex, state, ch;
|
||||||
|
char *pos;
|
||||||
|
|
||||||
|
state = 0;
|
||||||
|
tarindex = 0;
|
||||||
|
|
||||||
|
while ((ch = *src++) != '\0') {
|
||||||
|
if (isspace(ch)) /* Skip whitespace anywhere. */
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ch == Pad64)
|
||||||
|
break;
|
||||||
|
|
||||||
|
pos = strchr(Base64, ch);
|
||||||
|
if (pos == 0) /* A non-base64 character. */
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
switch (state) {
|
||||||
|
case 0:
|
||||||
|
if (target) {
|
||||||
|
if ((size_t)tarindex >= targsize)
|
||||||
|
return (-1);
|
||||||
|
target[tarindex] = (pos - Base64) << 2;
|
||||||
|
}
|
||||||
|
state = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (target) {
|
||||||
|
if ((size_t)tarindex + 1 >= targsize)
|
||||||
|
return (-1);
|
||||||
|
target[tarindex] |= (pos - Base64) >> 4;
|
||||||
|
target[tarindex+1] = ((pos - Base64) & 0x0f)
|
||||||
|
<< 4 ;
|
||||||
|
}
|
||||||
|
tarindex++;
|
||||||
|
state = 2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (target) {
|
||||||
|
if ((size_t)tarindex + 1 >= targsize)
|
||||||
|
return (-1);
|
||||||
|
target[tarindex] |= (pos - Base64) >> 2;
|
||||||
|
target[tarindex+1] = ((pos - Base64) & 0x03)
|
||||||
|
<< 6;
|
||||||
|
}
|
||||||
|
tarindex++;
|
||||||
|
state = 3;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (target) {
|
||||||
|
if ((size_t)tarindex >= targsize)
|
||||||
|
return (-1);
|
||||||
|
target[tarindex] |= (pos - Base64);
|
||||||
|
}
|
||||||
|
tarindex++;
|
||||||
|
state = 0;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are done decoding Base-64 chars. Let's see if we ended
|
||||||
|
* on a byte boundary, and/or with erroneous trailing characters.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ch == Pad64) { /* We got a pad char. */
|
||||||
|
ch = *src++; /* Skip it, get next. */
|
||||||
|
switch (state) {
|
||||||
|
case 0: /* Invalid = in first position */
|
||||||
|
case 1: /* Invalid = in second position */
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
case 2: /* Valid, means one byte of info */
|
||||||
|
/* Skip any number of spaces. */
|
||||||
|
for ((void)NULL; ch != '\0'; ch = *src++)
|
||||||
|
if (!isspace(ch))
|
||||||
|
break;
|
||||||
|
/* Make sure there is another trailing = sign. */
|
||||||
|
if (ch != Pad64)
|
||||||
|
return (-1);
|
||||||
|
ch = *src++; /* Skip the = */
|
||||||
|
/* Fall through to "single trailing =" case. */
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
|
||||||
|
case 3: /* Valid, means two bytes of info */
|
||||||
|
/*
|
||||||
|
* We know this char is an =. Is there anything but
|
||||||
|
* whitespace after it?
|
||||||
|
*/
|
||||||
|
for ((void)NULL; ch != '\0'; ch = *src++)
|
||||||
|
if (!isspace(ch))
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now make sure for cases 2 and 3 that the "extra"
|
||||||
|
* bits that slopped past the last full byte were
|
||||||
|
* zeros. If we don't check them, they become a
|
||||||
|
* subliminal channel.
|
||||||
|
*/
|
||||||
|
if (target && target[tarindex] != 0)
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* We ended by seeing the end of the string. Make sure we
|
||||||
|
* have no partial bytes lying around.
|
||||||
|
*/
|
||||||
|
if (state != 0)
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tarindex);
|
||||||
|
}
|
||||||
|
|
@ -21,11 +21,13 @@
|
||||||
#include <fcntl.h> // daemonizing
|
#include <fcntl.h> // daemonizing
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <resolv.h> /* base64 encode/decode */
|
|
||||||
#include <openssl/md5.h> /* md5 hash */
|
#include <openssl/md5.h> /* md5 hash */
|
||||||
#include <openssl/sha.h> /* sha1 hash */
|
#include <openssl/sha.h> /* sha1 hash */
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
|
int ws_b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize);
|
||||||
|
int ws_b64_pton(char const *src, u_char *target, size_t targsize);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Global state
|
* Global state
|
||||||
*
|
*
|
||||||
|
|
@ -214,7 +216,7 @@ int encode_hixie(u_char const *src, size_t srclength,
|
||||||
char *target, size_t targsize) {
|
char *target, size_t targsize) {
|
||||||
int sz = 0, len = 0;
|
int sz = 0, len = 0;
|
||||||
target[sz++] = '\x00';
|
target[sz++] = '\x00';
|
||||||
len = b64_ntop(src, srclength, target+sz, targsize-sz);
|
len = ws_b64_ntop(src, srclength, target+sz, targsize-sz);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
@ -249,7 +251,7 @@ int decode_hixie(char *src, size_t srclength,
|
||||||
/* We may have more than one frame */
|
/* We may have more than one frame */
|
||||||
end = (char *)memchr(start, '\xff', srclength);
|
end = (char *)memchr(start, '\xff', srclength);
|
||||||
*end = '\x00';
|
*end = '\x00';
|
||||||
len = b64_pton(start, target+retlen, targsize-retlen);
|
len = ws_b64_pton(start, target+retlen, targsize-retlen);
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
|
@ -270,21 +272,29 @@ int encode_hybi(u_char const *src, size_t srclength,
|
||||||
{
|
{
|
||||||
unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0;
|
unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0;
|
||||||
|
|
||||||
if ((int)srclength <= 0)
|
if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) {
|
||||||
{
|
handler_emsg("Invalid opcode. Opcode must be 0x01 for text mode, or 0x02 for binary mode.\n");
|
||||||
return 0;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
b64_sz = ((srclength - 1) / 3) * 4 + 4;
|
|
||||||
|
|
||||||
target[0] = (char)(opcode & 0x0F | 0x80);
|
target[0] = (char)(opcode & 0x0F | 0x80);
|
||||||
|
|
||||||
if (b64_sz <= 125) {
|
if ((int)srclength <= 0) {
|
||||||
target[1] = (char) b64_sz;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (opcode & OPCODE_TEXT) {
|
||||||
|
len = ((srclength - 1) / 3) * 4 + 4;
|
||||||
|
} else {
|
||||||
|
len = srclength;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len <= 125) {
|
||||||
|
target[1] = (char) len;
|
||||||
payload_offset = 2;
|
payload_offset = 2;
|
||||||
} else if ((b64_sz > 125) && (b64_sz < 65536)) {
|
} else if ((len > 125) && (len < 65536)) {
|
||||||
target[1] = (char) 126;
|
target[1] = (char) 126;
|
||||||
*(u_short*)&(target[2]) = htons(b64_sz);
|
*(u_short*)&(target[2]) = htons(len);
|
||||||
payload_offset = 4;
|
payload_offset = 4;
|
||||||
} else {
|
} else {
|
||||||
handler_emsg("Sending frames larger than 65535 bytes not supported\n");
|
handler_emsg("Sending frames larger than 65535 bytes not supported\n");
|
||||||
|
|
@ -294,7 +304,12 @@ int encode_hybi(u_char const *src, size_t srclength,
|
||||||
//payload_offset = 10;
|
//payload_offset = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
len = b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset);
|
if (opcode & OPCODE_TEXT) {
|
||||||
|
len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset);
|
||||||
|
} else {
|
||||||
|
memcpy(target+payload_offset, src, srclength);
|
||||||
|
len = srclength;
|
||||||
|
}
|
||||||
|
|
||||||
if (len < 0) {
|
if (len < 0) {
|
||||||
return len;
|
return len;
|
||||||
|
|
@ -364,7 +379,7 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
||||||
//printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining);
|
//printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining);
|
||||||
payload = frame + hdr_length + 4*masked;
|
payload = frame + hdr_length + 4*masked;
|
||||||
|
|
||||||
if (*opcode != 1 && *opcode != 2) {
|
if (*opcode != OPCODE_TEXT && *opcode != OPCODE_BINARY) {
|
||||||
handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode);
|
handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -389,8 +404,13 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
||||||
payload[i] ^= mask[i%4];
|
payload[i] ^= mask[i%4];
|
||||||
}
|
}
|
||||||
|
|
||||||
// base64 decode the data
|
if (*opcode & OPCODE_TEXT) {
|
||||||
len = b64_pton((const char*)payload, target+target_offset, targsize);
|
// base64 decode the data
|
||||||
|
len = ws_b64_pton((const char*)payload, target+target_offset, targsize);
|
||||||
|
} else {
|
||||||
|
memcpy(target+target_offset, payload, payload_length);
|
||||||
|
len = payload_length;
|
||||||
|
}
|
||||||
|
|
||||||
// Restore the first character of the next frame
|
// Restore the first character of the next frame
|
||||||
payload[payload_length] = save_char;
|
payload[payload_length] = save_char;
|
||||||
|
|
@ -560,7 +580,7 @@ static void gen_sha1(headers_t *headers, char *target) {
|
||||||
SHA1_Update(&c, HYBI_GUID, 36);
|
SHA1_Update(&c, HYBI_GUID, 36);
|
||||||
SHA1_Final(hash, &c);
|
SHA1_Final(hash, &c);
|
||||||
|
|
||||||
r = b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN);
|
r = ws_b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN);
|
||||||
//assert(r == HYBI10_ACCEPTHDRLEN - 1);
|
//assert(r == HYBI10_ACCEPTHDRLEN - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -571,6 +591,7 @@ ws_ctx_t *do_handshake(int sock) {
|
||||||
headers_t *headers;
|
headers_t *headers;
|
||||||
int len, ret, i, offset;
|
int len, ret, i, offset;
|
||||||
ws_ctx_t * ws_ctx;
|
ws_ctx_t * ws_ctx;
|
||||||
|
char *response_protocol;
|
||||||
|
|
||||||
// Peek, but don't read the data
|
// Peek, but don't read the data
|
||||||
len = recv(sock, handshake, 1024, MSG_PEEK);
|
len = recv(sock, handshake, 1024, MSG_PEEK);
|
||||||
|
|
@ -640,10 +661,23 @@ ws_ctx_t *do_handshake(int sock) {
|
||||||
}
|
}
|
||||||
|
|
||||||
headers = ws_ctx->headers;
|
headers = ws_ctx->headers;
|
||||||
|
|
||||||
|
if (strstr(headers->protocols, "binary")) {
|
||||||
|
ws_ctx->opcode = OPCODE_BINARY;
|
||||||
|
response_protocol = "binary";
|
||||||
|
} else if (strstr(headers->protocols, "base64")) {
|
||||||
|
ws_ctx->opcode = OPCODE_TEXT;
|
||||||
|
response_protocol = "base64";
|
||||||
|
} else {
|
||||||
|
handler_emsg("Invalid protocol '%s', expecting 'binary' or 'base64'\n",
|
||||||
|
headers->protocols);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
if (ws_ctx->hybi > 0) {
|
if (ws_ctx->hybi > 0) {
|
||||||
handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi);
|
handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi);
|
||||||
gen_sha1(headers, sha1);
|
gen_sha1(headers, sha1);
|
||||||
sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, "base64");
|
sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, response_protocol);
|
||||||
} else {
|
} else {
|
||||||
if (ws_ctx->hixie == 76) {
|
if (ws_ctx->hixie == 76) {
|
||||||
handler_msg("using protocol Hixie 76\n");
|
handler_msg("using protocol Hixie 76\n");
|
||||||
|
|
@ -666,9 +700,16 @@ ws_ctx_t *do_handshake(int sock) {
|
||||||
|
|
||||||
void signal_handler(sig) {
|
void signal_handler(sig) {
|
||||||
switch (sig) {
|
switch (sig) {
|
||||||
case SIGHUP: break; // ignore for now
|
case SIGHUP:
|
||||||
|
if (settings.whitelist != NULL) {
|
||||||
|
load_whitelist();
|
||||||
|
}
|
||||||
|
break;
|
||||||
case SIGPIPE: pipe_error = 1; break; // handle inline
|
case SIGPIPE: pipe_error = 1; break; // handle inline
|
||||||
case SIGTERM: exit(0); break;
|
case SIGTERM:
|
||||||
|
remove(settings.pid);
|
||||||
|
exit(0);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -687,7 +728,17 @@ void daemonize(int keepfd) {
|
||||||
setsid(); // Obtain new process group
|
setsid(); // Obtain new process group
|
||||||
pid = fork();
|
pid = fork();
|
||||||
if (pid<0) { fatal("fork error"); }
|
if (pid<0) { fatal("fork error"); }
|
||||||
if (pid>0) { exit(0); } // parent exits
|
if (pid>0) {
|
||||||
|
// parent exits
|
||||||
|
FILE *pidf = fopen(settings.pid, "w");
|
||||||
|
if (pidf) {
|
||||||
|
fprintf(pidf, "%d", pid);
|
||||||
|
fclose(pidf);
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "Could not write daemon PID file '%s': %s\n", settings.pid, strerror(errno));
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
/* Signal handling */
|
/* Signal handling */
|
||||||
signal(SIGHUP, signal_handler); // catch HUP
|
signal(SIGHUP, signal_handler); // catch HUP
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,9 @@ Sec-WebSocket-Protocol: %s\r\n\
|
||||||
|
|
||||||
#define POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
|
#define POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
|
||||||
|
|
||||||
|
#define OPCODE_TEXT 0x01
|
||||||
|
#define OPCODE_BINARY 0x02
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
char path[1024+1];
|
char path[1024+1];
|
||||||
char host[1024+1];
|
char host[1024+1];
|
||||||
|
|
@ -44,6 +47,7 @@ typedef struct {
|
||||||
SSL *ssl;
|
SSL *ssl;
|
||||||
int hixie;
|
int hixie;
|
||||||
int hybi;
|
int hybi;
|
||||||
|
int opcode;
|
||||||
headers_t *headers;
|
headers_t *headers;
|
||||||
char *cin_buf;
|
char *cin_buf;
|
||||||
char *cout_buf;
|
char *cout_buf;
|
||||||
|
|
@ -62,6 +66,9 @@ typedef struct {
|
||||||
int ssl_only;
|
int ssl_only;
|
||||||
int daemon;
|
int daemon;
|
||||||
int run_once;
|
int run_once;
|
||||||
|
char *whitelist;
|
||||||
|
char *pattern;
|
||||||
|
char *pid;
|
||||||
} settings_t;
|
} settings_t;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
#include <signal.h>
|
||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
char traffic_legend[] = "\n\
|
char traffic_legend[] = "\n\
|
||||||
|
|
@ -33,20 +34,27 @@ Traffic Legend:\n\
|
||||||
";
|
";
|
||||||
|
|
||||||
char USAGE[] = "Usage: [options] " \
|
char USAGE[] = "Usage: [options] " \
|
||||||
"[source_addr:]source_port target_addr:target_port\n\n" \
|
"[source_addr:]source_port target_addr{:target_port}\n\n" \
|
||||||
" --verbose|-v verbose messages and per frame traffic\n" \
|
" --verbose|-v verbose messages and per frame traffic\n" \
|
||||||
" --daemon|-D become a daemon (background process)\n" \
|
" --daemon|-D become a daemon (background process)\n" \
|
||||||
" --cert CERT SSL certificate file\n" \
|
" --cert CERT SSL certificate file\n" \
|
||||||
" --key KEY SSL key file (if separate from cert)\n" \
|
" --key KEY SSL key file (if separate from cert)\n" \
|
||||||
" --ssl-only disallow non-encrypted connections";
|
" --ssl-only disallow non-encrypted connections\n" \
|
||||||
|
" --whitelist|-w LIST new-line separated target port whitelist file\n" \
|
||||||
|
" (target_port is not required only with this option)\n" \
|
||||||
|
" --pattern|-P target port request pattern. Default: '/%d'\n" \
|
||||||
|
" --pid|-p desired path of pid file. Default: '/var/run/websockify.pid'";
|
||||||
|
|
||||||
#define usage(fmt, args...) \
|
#define usage(fmt, args...) \
|
||||||
fprintf(stderr, "%s\n\n", USAGE); \
|
do { \
|
||||||
fprintf(stderr, fmt , ## args); \
|
fprintf(stderr, "%s\n\n", USAGE); \
|
||||||
exit(1);
|
fprintf(stderr, fmt , ## args); \
|
||||||
|
exit(1); \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
char target_host[256];
|
char target_host[256];
|
||||||
int target_port;
|
int target_port;
|
||||||
|
int *target_ports;
|
||||||
|
|
||||||
extern pipe_error;
|
extern pipe_error;
|
||||||
extern settings_t settings;
|
extern settings_t settings;
|
||||||
|
|
@ -156,7 +164,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
||||||
cout_start = 0;
|
cout_start = 0;
|
||||||
if (ws_ctx->hybi) {
|
if (ws_ctx->hybi) {
|
||||||
cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
|
cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
|
||||||
ws_ctx->cout_buf, BUFSIZE, 1);
|
ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode);
|
||||||
} else {
|
} else {
|
||||||
cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
|
cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
|
||||||
ws_ctx->cout_buf, BUFSIZE);
|
ws_ctx->cout_buf, BUFSIZE);
|
||||||
|
|
@ -203,7 +211,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opcode == 8) {
|
if (opcode == 8) {
|
||||||
handler_emsg("client sent orderly close frame\n");
|
handler_msg("client sent orderly close frame\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -237,6 +245,27 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||||
int tsock = 0;
|
int tsock = 0;
|
||||||
struct sockaddr_in taddr;
|
struct sockaddr_in taddr;
|
||||||
|
|
||||||
|
if (target_ports != NULL) {
|
||||||
|
if (sscanf(ws_ctx->headers->path, settings.pattern, &target_port) != 1) {
|
||||||
|
handler_emsg("Could not match pattern '%s' to request path '%s'\n",
|
||||||
|
settings.pattern, ws_ctx->headers->path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int *p;
|
||||||
|
int found = 0;
|
||||||
|
for (p = target_ports; *p; p++) {
|
||||||
|
if (*p == target_port) {
|
||||||
|
found = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
handler_emsg("Rejecting connection to non-whitelisted port: '%d'\n",
|
||||||
|
target_port);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
handler_msg("connecting to: %s:%d\n", target_host, target_port);
|
handler_msg("connecting to: %s:%d\n", target_host, target_port);
|
||||||
|
|
||||||
tsock = socket(AF_INET, SOCK_STREAM, 0);
|
tsock = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
|
@ -272,19 +301,77 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
||||||
close(tsock);
|
close(tsock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int load_whitelist() {
|
||||||
|
printf("loading port whitelist '%s'\n", settings.whitelist);
|
||||||
|
FILE *whitelist = fopen(settings.whitelist, "r");
|
||||||
|
if (whitelist == NULL) {
|
||||||
|
fprintf(stderr, "Error opening whitelist file '%s':\n\t%s\n",
|
||||||
|
settings.whitelist, strerror(errno));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const int tplen_grow = 512;
|
||||||
|
int tplen = tplen_grow, tpcount = 0;
|
||||||
|
target_ports = (int*)malloc(tplen*sizeof(int));
|
||||||
|
if (target_ports == NULL) {
|
||||||
|
fprintf(stderr, "Whitelist port malloc error");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *line = NULL;
|
||||||
|
ssize_t n = 0, nread = 0;
|
||||||
|
while ((nread = getline(&line, &n, whitelist)) > 0) {
|
||||||
|
if (line[0] == '\n') continue;
|
||||||
|
line[nread-1] = '\x00';
|
||||||
|
long int port = strtol(line, NULL, 10);
|
||||||
|
if (port < 1 || port > 65535) {
|
||||||
|
fprintf(stderr,
|
||||||
|
"Whitelist port '%s' is not between valid range 1 and 65535", line);
|
||||||
|
return -3;
|
||||||
|
}
|
||||||
|
tpcount++;
|
||||||
|
if (tpcount >= tplen) {
|
||||||
|
tplen += tplen_grow;
|
||||||
|
target_ports = (int*)realloc(target_ports, tplen*sizeof(int));
|
||||||
|
if (target_ports == NULL) {
|
||||||
|
fprintf(stderr, "Whitelist port realloc error");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
target_ports[tpcount-1] = port;
|
||||||
|
}
|
||||||
|
if (line != NULL) free(line);
|
||||||
|
|
||||||
|
if (tpcount == 0) {
|
||||||
|
fprintf(stderr, "0 ports read from whitelist file '%s'\n",
|
||||||
|
settings.whitelist);
|
||||||
|
return -4;
|
||||||
|
}
|
||||||
|
|
||||||
|
target_ports = (int*)realloc(target_ports, (tpcount + 1)*sizeof(int));
|
||||||
|
if (target_ports == NULL) {
|
||||||
|
fprintf(stderr, "Whitelist port realloc error");
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
target_ports[tpcount] = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int fd, c, option_index = 0;
|
int fd, c, option_index = 0;
|
||||||
static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0;
|
|
||||||
char *found;
|
char *found;
|
||||||
static struct option long_options[] = {
|
static struct option long_options[] = {
|
||||||
{"verbose", no_argument, &verbose, 'v'},
|
{"verbose", no_argument, 0, 'v'},
|
||||||
{"ssl-only", no_argument, &ssl_only, 1 },
|
{"ssl-only", no_argument, &settings.ssl_only, 1 },
|
||||||
{"daemon", no_argument, &daemon, 'D'},
|
{"daemon", no_argument, 0, 'D'},
|
||||||
/* ---- */
|
/* ---- */
|
||||||
{"run-once", no_argument, 0, 'r'},
|
{"run-once", no_argument, 0, 'r'},
|
||||||
{"cert", required_argument, 0, 'c'},
|
{"cert", required_argument, 0, 'c'},
|
||||||
{"key", required_argument, 0, 'k'},
|
{"key", required_argument, 0, 'k'},
|
||||||
|
{"whitelist", required_argument, 0, 'w'},
|
||||||
|
{"pattern", required_argument, 0, 'P'},
|
||||||
|
{"pid", required_argument, 0, 'p'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -294,13 +381,15 @@ int main(int argc, char *argv[])
|
||||||
settings.cert = "self.pem";
|
settings.cert = "self.pem";
|
||||||
}
|
}
|
||||||
settings.key = "";
|
settings.key = "";
|
||||||
|
settings.pattern = "/%d";
|
||||||
|
settings.pid = "/var/run/websockify.pid";
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
c = getopt_long (argc, argv, "vDrc:k:",
|
c = getopt_long (argc, argv, "vDrc:k:w:p:P:",
|
||||||
long_options, &option_index);
|
long_options, &option_index);
|
||||||
|
|
||||||
/* Detect the end */
|
/* Detect the end */
|
||||||
if (c == -1) { break; }
|
if (c == -1) break;
|
||||||
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 0:
|
case 0:
|
||||||
|
|
@ -308,13 +397,13 @@ int main(int argc, char *argv[])
|
||||||
case 1:
|
case 1:
|
||||||
break; // ignore
|
break; // ignore
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = 1;
|
settings.verbose = 1;
|
||||||
break;
|
break;
|
||||||
case 'D':
|
case 'D':
|
||||||
daemon = 1;
|
settings.daemon = 1;
|
||||||
break;
|
break;
|
||||||
case 'r':
|
case 'r':
|
||||||
run_once = 1;
|
settings.run_once = 1;
|
||||||
break;
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
settings.cert = realpath(optarg, NULL);
|
settings.cert = realpath(optarg, NULL);
|
||||||
|
|
@ -328,14 +417,22 @@ int main(int argc, char *argv[])
|
||||||
usage("No key file at %s\n", optarg);
|
usage("No key file at %s\n", optarg);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'w':
|
||||||
|
settings.whitelist = realpath(optarg, NULL);
|
||||||
|
if (! settings.whitelist) {
|
||||||
|
usage("No whitelist file at %s\n", optarg);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
settings.pattern = optarg;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
settings.pid = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
usage("");
|
usage(" ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settings.verbose = verbose;
|
|
||||||
settings.ssl_only = ssl_only;
|
|
||||||
settings.daemon = daemon;
|
|
||||||
settings.run_once = run_once;
|
|
||||||
|
|
||||||
if ((argc-optind) != 2) {
|
if ((argc-optind) != 2) {
|
||||||
usage("Invalid number of arguments\n");
|
usage("Invalid number of arguments\n");
|
||||||
|
|
@ -355,17 +452,25 @@ int main(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
found = strstr(argv[optind], ":");
|
found = strstr(argv[optind], ":");
|
||||||
if (found) {
|
if (found && settings.whitelist == NULL) {
|
||||||
memcpy(target_host, argv[optind], found-argv[optind]);
|
memcpy(target_host, argv[optind], found-argv[optind]);
|
||||||
target_port = strtol(found+1, NULL, 10);
|
target_port = strtol(found+1, NULL, 10);
|
||||||
|
target_ports = NULL;
|
||||||
|
} else if (!found && settings.whitelist != NULL) {
|
||||||
|
if (load_whitelist()) {
|
||||||
|
usage("Whitelist error.");
|
||||||
|
}
|
||||||
|
memcpy(target_host, argv[optind], strlen(argv[optind]));
|
||||||
|
target_port = -1;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
usage("Target argument must be host:port\n");
|
usage("Target argument must be host:port or provide host and a port whitelist\n");
|
||||||
}
|
}
|
||||||
if (target_port == 0) {
|
if (target_port == 0) {
|
||||||
usage("Could not parse target port\n");
|
usage("Could not parse target port\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssl_only) {
|
if (settings.ssl_only) {
|
||||||
if (access(settings.cert, R_OK) != 0) {
|
if (access(settings.cert, R_OK) != 0) {
|
||||||
usage("SSL only and cert file '%s' not found\n", settings.cert);
|
usage("SSL only and cert file '%s' not found\n", settings.cert);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue