Merge 0ec8b7008c into 414d1d8b44
This commit is contained in:
commit
4266dfa88c
|
|
@ -0,0 +1 @@
|
|||
websockify
|
||||
|
|
@ -3,11 +3,12 @@ CFLAGS += -fPIC
|
|||
|
||||
all: $(TARGETS)
|
||||
|
||||
websockify: websockify.o websocket.o
|
||||
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -lresolv -o $@
|
||||
websockify: websockify.o websocket.o base64.o
|
||||
$(CC) $(LDFLAGS) $^ -lssl -lcrypto -o $@
|
||||
|
||||
websocket.o: websocket.c websocket.h
|
||||
websockify.o: websockify.c websocket.h
|
||||
base64.o: base64.c
|
||||
|
||||
clean:
|
||||
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 <openssl/err.h>
|
||||
#include <openssl/ssl.h>
|
||||
#include <resolv.h> /* base64 encode/decode */
|
||||
#include <openssl/md5.h> /* md5 hash */
|
||||
#include <openssl/sha.h> /* sha1 hash */
|
||||
#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
|
||||
*
|
||||
|
|
@ -214,7 +216,7 @@ int encode_hixie(u_char const *src, size_t srclength,
|
|||
char *target, size_t targsize) {
|
||||
int sz = 0, len = 0;
|
||||
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) {
|
||||
return len;
|
||||
}
|
||||
|
|
@ -249,7 +251,7 @@ int decode_hixie(char *src, size_t srclength,
|
|||
/* We may have more than one frame */
|
||||
end = (char *)memchr(start, '\xff', srclength);
|
||||
*end = '\x00';
|
||||
len = b64_pton(start, target+retlen, targsize-retlen);
|
||||
len = ws_b64_pton(start, target+retlen, targsize-retlen);
|
||||
if (len < 0) {
|
||||
return len;
|
||||
}
|
||||
|
|
@ -269,22 +271,30 @@ int encode_hybi(u_char const *src, size_t srclength,
|
|||
char *target, size_t targsize, unsigned int opcode)
|
||||
{
|
||||
unsigned long long b64_sz, len_offset = 1, payload_offset = 2, len = 0;
|
||||
|
||||
if ((int)srclength <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
b64_sz = ((srclength - 1) / 3) * 4 + 4;
|
||||
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 -1;
|
||||
}
|
||||
|
||||
target[0] = (char)(opcode & 0x0F | 0x80);
|
||||
|
||||
if (b64_sz <= 125) {
|
||||
target[1] = (char) b64_sz;
|
||||
if ((int)srclength <= 0) {
|
||||
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;
|
||||
} else if ((b64_sz > 125) && (b64_sz < 65536)) {
|
||||
} else if ((len > 125) && (len < 65536)) {
|
||||
target[1] = (char) 126;
|
||||
*(u_short*)&(target[2]) = htons(b64_sz);
|
||||
*(u_short*)&(target[2]) = htons(len);
|
||||
payload_offset = 4;
|
||||
} else {
|
||||
handler_emsg("Sending frames larger than 65535 bytes not supported\n");
|
||||
|
|
@ -294,8 +304,13 @@ int encode_hybi(u_char const *src, size_t srclength,
|
|||
//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) {
|
||||
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);
|
||||
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);
|
||||
continue;
|
||||
}
|
||||
|
|
@ -389,8 +404,13 @@ int decode_hybi(unsigned char *src, size_t srclength,
|
|||
payload[i] ^= mask[i%4];
|
||||
}
|
||||
|
||||
// base64 decode the data
|
||||
len = b64_pton((const char*)payload, target+target_offset, targsize);
|
||||
if (*opcode & OPCODE_TEXT) {
|
||||
// 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
|
||||
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_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);
|
||||
}
|
||||
|
||||
|
|
@ -571,6 +591,7 @@ ws_ctx_t *do_handshake(int sock) {
|
|||
headers_t *headers;
|
||||
int len, ret, i, offset;
|
||||
ws_ctx_t * ws_ctx;
|
||||
char *response_protocol;
|
||||
|
||||
// Peek, but don't read the data
|
||||
len = recv(sock, handshake, 1024, MSG_PEEK);
|
||||
|
|
@ -640,10 +661,23 @@ ws_ctx_t *do_handshake(int sock) {
|
|||
}
|
||||
|
||||
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) {
|
||||
handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi);
|
||||
gen_sha1(headers, sha1);
|
||||
sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, "base64");
|
||||
sprintf(response, SERVER_HANDSHAKE_HYBI, sha1, response_protocol);
|
||||
} else {
|
||||
if (ws_ctx->hixie == 76) {
|
||||
handler_msg("using protocol Hixie 76\n");
|
||||
|
|
@ -666,9 +700,16 @@ ws_ctx_t *do_handshake(int sock) {
|
|||
|
||||
void signal_handler(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 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
|
||||
pid = fork();
|
||||
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(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 OPCODE_TEXT 0x01
|
||||
#define OPCODE_BINARY 0x02
|
||||
|
||||
typedef struct {
|
||||
char path[1024+1];
|
||||
char host[1024+1];
|
||||
|
|
@ -44,6 +47,7 @@ typedef struct {
|
|||
SSL *ssl;
|
||||
int hixie;
|
||||
int hybi;
|
||||
int opcode;
|
||||
headers_t *headers;
|
||||
char *cin_buf;
|
||||
char *cout_buf;
|
||||
|
|
@ -62,6 +66,9 @@ typedef struct {
|
|||
int ssl_only;
|
||||
int daemon;
|
||||
int run_once;
|
||||
char *whitelist;
|
||||
char *pattern;
|
||||
char *pid;
|
||||
} settings_t;
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include "websocket.h"
|
||||
|
||||
char traffic_legend[] = "\n\
|
||||
|
|
@ -33,20 +34,27 @@ Traffic Legend:\n\
|
|||
";
|
||||
|
||||
char USAGE[] = "Usage: [options] " \
|
||||
"[source_addr:]source_port target_addr:target_port\n\n" \
|
||||
" --verbose|-v verbose messages and per frame traffic\n" \
|
||||
" --daemon|-D become a daemon (background process)\n" \
|
||||
" --cert CERT SSL certificate file\n" \
|
||||
" --key KEY SSL key file (if separate from cert)\n" \
|
||||
" --ssl-only disallow non-encrypted connections";
|
||||
"[source_addr:]source_port target_addr{:target_port}\n\n" \
|
||||
" --verbose|-v verbose messages and per frame traffic\n" \
|
||||
" --daemon|-D become a daemon (background process)\n" \
|
||||
" --cert CERT SSL certificate file\n" \
|
||||
" --key KEY SSL key file (if separate from cert)\n" \
|
||||
" --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...) \
|
||||
fprintf(stderr, "%s\n\n", USAGE); \
|
||||
fprintf(stderr, fmt , ## args); \
|
||||
exit(1);
|
||||
do { \
|
||||
fprintf(stderr, "%s\n\n", USAGE); \
|
||||
fprintf(stderr, fmt , ## args); \
|
||||
exit(1); \
|
||||
} while(0)
|
||||
|
||||
char target_host[256];
|
||||
int target_port;
|
||||
int *target_ports;
|
||||
|
||||
extern pipe_error;
|
||||
extern settings_t settings;
|
||||
|
|
@ -156,7 +164,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
|||
cout_start = 0;
|
||||
if (ws_ctx->hybi) {
|
||||
cout_end = encode_hybi(ws_ctx->cin_buf, bytes,
|
||||
ws_ctx->cout_buf, BUFSIZE, 1);
|
||||
ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode);
|
||||
} else {
|
||||
cout_end = encode_hixie(ws_ctx->cin_buf, bytes,
|
||||
ws_ctx->cout_buf, BUFSIZE);
|
||||
|
|
@ -203,7 +211,7 @@ void do_proxy(ws_ctx_t *ws_ctx, int target) {
|
|||
}
|
||||
|
||||
if (opcode == 8) {
|
||||
handler_emsg("client sent orderly close frame\n");
|
||||
handler_msg("client sent orderly close frame\n");
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +245,27 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
|||
int tsock = 0;
|
||||
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);
|
||||
|
||||
tsock = socket(AF_INET, SOCK_STREAM, 0);
|
||||
|
|
@ -272,19 +301,77 @@ void proxy_handler(ws_ctx_t *ws_ctx) {
|
|||
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 fd, c, option_index = 0;
|
||||
static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0;
|
||||
char *found;
|
||||
static struct option long_options[] = {
|
||||
{"verbose", no_argument, &verbose, 'v'},
|
||||
{"ssl-only", no_argument, &ssl_only, 1 },
|
||||
{"daemon", no_argument, &daemon, 'D'},
|
||||
{"verbose", no_argument, 0, 'v'},
|
||||
{"ssl-only", no_argument, &settings.ssl_only, 1 },
|
||||
{"daemon", no_argument, 0, 'D'},
|
||||
/* ---- */
|
||||
{"run-once", no_argument, 0, 'r'},
|
||||
{"cert", required_argument, 0, 'c'},
|
||||
{"key", required_argument, 0, 'k'},
|
||||
{"run-once", no_argument, 0, 'r'},
|
||||
{"cert", required_argument, 0, 'c'},
|
||||
{"key", required_argument, 0, 'k'},
|
||||
{"whitelist", required_argument, 0, 'w'},
|
||||
{"pattern", required_argument, 0, 'P'},
|
||||
{"pid", required_argument, 0, 'p'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
|
|
@ -294,13 +381,15 @@ int main(int argc, char *argv[])
|
|||
settings.cert = "self.pem";
|
||||
}
|
||||
settings.key = "";
|
||||
settings.pattern = "/%d";
|
||||
settings.pid = "/var/run/websockify.pid";
|
||||
|
||||
while (1) {
|
||||
c = getopt_long (argc, argv, "vDrc:k:",
|
||||
c = getopt_long (argc, argv, "vDrc:k:w:p:P:",
|
||||
long_options, &option_index);
|
||||
|
||||
/* Detect the end */
|
||||
if (c == -1) { break; }
|
||||
if (c == -1) break;
|
||||
|
||||
switch (c) {
|
||||
case 0:
|
||||
|
|
@ -308,13 +397,13 @@ int main(int argc, char *argv[])
|
|||
case 1:
|
||||
break; // ignore
|
||||
case 'v':
|
||||
verbose = 1;
|
||||
settings.verbose = 1;
|
||||
break;
|
||||
case 'D':
|
||||
daemon = 1;
|
||||
settings.daemon = 1;
|
||||
break;
|
||||
case 'r':
|
||||
run_once = 1;
|
||||
settings.run_once = 1;
|
||||
break;
|
||||
case 'c':
|
||||
settings.cert = realpath(optarg, NULL);
|
||||
|
|
@ -328,14 +417,22 @@ int main(int argc, char *argv[])
|
|||
usage("No key file at %s\n", optarg);
|
||||
}
|
||||
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:
|
||||
usage("");
|
||||
usage(" ");
|
||||
}
|
||||
}
|
||||
settings.verbose = verbose;
|
||||
settings.ssl_only = ssl_only;
|
||||
settings.daemon = daemon;
|
||||
settings.run_once = run_once;
|
||||
|
||||
if ((argc-optind) != 2) {
|
||||
usage("Invalid number of arguments\n");
|
||||
|
|
@ -355,17 +452,25 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
found = strstr(argv[optind], ":");
|
||||
if (found) {
|
||||
if (found && settings.whitelist == NULL) {
|
||||
memcpy(target_host, argv[optind], found-argv[optind]);
|
||||
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 {
|
||||
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) {
|
||||
usage("Could not parse target port\n");
|
||||
}
|
||||
|
||||
if (ssl_only) {
|
||||
if (settings.ssl_only) {
|
||||
if (access(settings.cert, R_OK) != 0) {
|
||||
usage("SSL only and cert file '%s' not found\n", settings.cert);
|
||||
}
|
||||
|
|
@ -380,7 +485,7 @@ int main(int argc, char *argv[])
|
|||
//printf(" cert: %s\n", settings.cert);
|
||||
//printf(" key: %s\n", settings.key);
|
||||
|
||||
settings.handler = proxy_handler;
|
||||
settings.handler = proxy_handler;
|
||||
start_server();
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue