17
17
// #define PROXY_DEEP_DEBUG
18
18
19
19
static const unsigned char b64[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" ;
20
- static void base64_encode (void *dst, const void *src, size_t len) // thread-safe, re-entrant
21
- {
20
+ static void base64_encode (void *dst, const void *src, size_t len) { // thread-safe, re-entrant
22
21
assert (dst != src);
23
22
unsigned int *d = (unsigned int *)dst;
24
23
const unsigned char *s = (const unsigned char *)src;
25
24
const unsigned char *end = s + len;
26
- while (s < end)
27
- {
25
+ while (s < end) {
28
26
uint32_t e = *s++ << 16 ;
29
27
if (s < end) e |= *s++ << 8 ;
30
28
if (s < end) e |= *s++;
@@ -37,23 +35,22 @@ static void base64_encode(void *dst, const void *src, size_t len) // thread-safe
37
35
#define on_error (...) { fprintf (stderr, __VA_ARGS__); fflush (stderr); exit (1 ); }
38
36
#define MIN (a, b ) ((a) <= (b) ? (a) : (b))
39
37
40
- // Given a multiline string of HTTP headers, returns a pointer to the beginning of the value of given header inside the string that was passed in.
41
- static int GetHttpHeader ( const char *headers, const char * header, char *out, int maxBytesOut) // thread-safe, re-entrant
42
- {
38
+ // Given a multiline string of HTTP headers, returns a pointer to the beginning
39
+ // of the value of given header inside the string that was passed in.
40
+ static int GetHttpHeader ( const char *headers, const char *header, char *out, int maxBytesOut) { // thread-safe, re-entrant
43
41
const char *pos = strstr (headers, header);
44
42
if (!pos) return 0 ;
45
43
pos += strlen (header);
46
44
const char *end = pos;
47
- while (*end != ' \r ' && *end != ' \n ' && *end != ' \0 ' ) ++end;
45
+ while (*end != ' \r ' && *end != ' \n ' && *end != ' \0 ' ) ++end;
48
46
int numBytesToWrite = MIN ((int )(end-pos), maxBytesOut-1 );
49
47
memcpy (out, pos, numBytesToWrite);
50
48
out[numBytesToWrite] = ' \0 ' ;
51
49
return (int )(end-pos);
52
50
}
53
51
54
52
// Sends WebSocket handshake back to the given WebSocket connection.
55
- void SendHandshake (int fd, const char *request)
56
- {
53
+ void SendHandshake (int fd, const char *request) {
57
54
const char webSocketGlobalGuid[] = " 258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ; // 36 characters long
58
55
char key[128 +sizeof (webSocketGlobalGuid)];
59
56
GetHttpHeader (request, " Sec-WebSocket-Key: " , key, sizeof (key)/2 );
@@ -77,32 +74,30 @@ void SendHandshake(int fd, const char *request)
77
74
printf (" Sent handshake:\n %s\n " , handshakeMsg);
78
75
}
79
76
80
- // Validates if the given, possibly partially received WebSocket message has enough bytes to contain a full WebSocket header.
81
- static bool WebSocketHasFullHeader ( uint8_t *data, uint64_t obtainedNumBytes)
82
- {
77
+ // Validates if the given, possibly partially received WebSocket message has
78
+ // enough bytes to contain a full WebSocket header.
79
+ static bool WebSocketHasFullHeader ( uint8_t *data, uint64_t obtainedNumBytes) {
83
80
if (obtainedNumBytes < 2 ) return false ;
84
81
uint64_t expectedNumBytes = 2 ;
85
82
WebSocketMessageHeader *header = (WebSocketMessageHeader *)data;
86
83
if (header->mask ) expectedNumBytes += 4 ;
87
- switch (header->payloadLength )
88
- {
84
+ switch (header->payloadLength ) {
89
85
case 127 : return expectedNumBytes += 8 ; break ;
90
86
case 126 : return expectedNumBytes += 2 ; break ;
91
87
default : break ;
92
88
}
93
89
return obtainedNumBytes >= expectedNumBytes;
94
90
}
95
91
96
- // Computes the total number of bytes that the given WebSocket message will take up.
97
- uint64_t WebSocketFullMessageSize ( uint8_t *data, uint64_t obtainedNumBytes)
98
- {
92
+ // Computes the total number of bytes that the given WebSocket message will take
93
+ // up.
94
+ uint64_t WebSocketFullMessageSize ( uint8_t *data, uint64_t obtainedNumBytes) {
99
95
assert (WebSocketHasFullHeader (data, obtainedNumBytes));
100
96
101
97
uint64_t expectedNumBytes = 2 ;
102
98
WebSocketMessageHeader *header = (WebSocketMessageHeader *)data;
103
99
if (header->mask ) expectedNumBytes += 4 ;
104
- switch (header->payloadLength )
105
- {
100
+ switch (header->payloadLength ) {
106
101
case 127 : return expectedNumBytes += 8 + ntoh64 (*(uint64_t *)(data+2 )); break ;
107
102
case 126 : return expectedNumBytes += 2 + ntohs (*(uint16_t *)(data+2 )); break ;
108
103
default : expectedNumBytes += header->payloadLength ; break ;
@@ -111,74 +106,79 @@ uint64_t WebSocketFullMessageSize(uint8_t *data, uint64_t obtainedNumBytes)
111
106
}
112
107
113
108
// Tests the structure integrity of the websocket message length.
114
- bool WebSocketValidateMessageSize (uint8_t *data, uint64_t obtainedNumBytes)
115
- {
109
+ bool WebSocketValidateMessageSize (uint8_t *data, uint64_t obtainedNumBytes) {
116
110
uint64_t expectedNumBytes = WebSocketFullMessageSize (data, obtainedNumBytes);
117
111
118
- if (expectedNumBytes != obtainedNumBytes)
119
- {
112
+ if (expectedNumBytes != obtainedNumBytes) {
120
113
printf (" Corrupt WebSocket message size! (got %llu bytes, expected %llu bytes)\n " , obtainedNumBytes, expectedNumBytes);
121
114
printf (" Received data:" );
122
- for (size_t i = 0 ; i < obtainedNumBytes; ++i)
115
+ for (size_t i = 0 ; i < obtainedNumBytes; ++i)
123
116
printf (" %02X" , data[i]);
124
117
printf (" \n " );
125
118
}
126
119
return expectedNumBytes == obtainedNumBytes;
127
120
}
128
121
129
- uint64_t WebSocketMessagePayloadLength (uint8_t *data, uint64_t numBytes)
130
- {
122
+ uint64_t WebSocketMessagePayloadLength (uint8_t *data, uint64_t numBytes) {
131
123
WebSocketMessageHeader *header = (WebSocketMessageHeader *)data;
132
- switch (header->payloadLength )
133
- {
124
+ switch (header->payloadLength ) {
134
125
case 127 : return ntoh64 (*(uint64_t *)(data+2 ));
135
126
case 126 : return ntohs (*(uint16_t *)(data+2 ));
136
127
default : return header->payloadLength ;
137
128
}
138
129
}
139
130
140
- uint32_t WebSocketMessageMaskingKey (uint8_t *data, uint64_t numBytes)
141
- {
131
+ uint32_t WebSocketMessageMaskingKey (uint8_t *data, uint64_t numBytes) {
142
132
WebSocketMessageHeader *header = (WebSocketMessageHeader *)data;
143
133
if (!header->mask ) return 0 ;
144
- switch (header->payloadLength )
145
- {
134
+ switch (header->payloadLength ) {
146
135
case 127 : return *(uint32_t *)(data+10 );
147
136
case 126 : return *(uint32_t *)(data+4 );
148
137
default : return *(uint32_t *)(data+2 );
149
138
}
150
139
}
151
140
152
- uint8_t *WebSocketMessageData (uint8_t *data, uint64_t numBytes)
153
- {
141
+ uint8_t *WebSocketMessageData (uint8_t *data, uint64_t numBytes) {
154
142
WebSocketMessageHeader *header = (WebSocketMessageHeader *)data;
155
143
data += 2 ; // Two bytes of fixed size header
156
144
if (header->mask ) data += 4 ; // If there is a masking key present in the header, that takes up 4 bytes
157
- switch (header->payloadLength )
158
- {
145
+ switch (header->payloadLength ) {
159
146
case 127 : return data + 8 ; // 64-bit length
160
147
case 126 : return data + 2 ; // 16-bit length
161
148
default : return data; // 7-bit length that was embedded in fixed size header.
162
149
}
163
150
}
164
151
165
- void CloseWebSocket (int client_fd)
166
- {
152
+ void CloseWebSocket (int client_fd) {
167
153
printf (" Closing WebSocket connection %d\n " , client_fd);
168
154
CloseAllSocketsByConnection (client_fd);
169
155
shutdown (client_fd, SHUTDOWN_BIDIRECTIONAL);
170
156
CLOSE_SOCKET (client_fd);
171
157
}
172
158
173
- const char *WebSocketOpcodeToString (int opcode)
174
- {
175
- static const char *opcodes[] = { " continuation frame (0x0)" , " text frame (0x1)" , " binary frame (0x2)" , " reserved(0x3)" , " reserved(0x4)" , " reserved(0x5)" ,
176
- " reserved(0x6)" , " reserved(0x7)" , " connection close (0x8)" , " ping (0x9)" , " pong (0xA)" , " reserved(0xB)" , " reserved(0xC)" , " reserved(0xD)" , " reserved(0xE)" , " reserved(0xF)" };
159
+ const char *WebSocketOpcodeToString (int opcode) {
160
+ static const char *opcodes[] = {
161
+ " continuation frame (0x0)" ,
162
+ " text frame (0x1)" ,
163
+ " binary frame (0x2)" ,
164
+ " reserved(0x3)" ,
165
+ " reserved(0x4)" ,
166
+ " reserved(0x5)" ,
167
+ " reserved(0x6)" ,
168
+ " reserved(0x7)" ,
169
+ " connection close (0x8)" ,
170
+ " ping (0x9)" ,
171
+ " pong (0xA)" ,
172
+ " reserved(0xB)" ,
173
+ " reserved(0xC)" ,
174
+ " reserved(0xD)" ,
175
+ " reserved(0xE)" ,
176
+ " reserved(0xF)"
177
+ };
177
178
return opcodes[opcode];
178
179
}
179
180
180
- void DumpWebSocketMessage (uint8_t *data, uint64_t numBytes)
181
- {
181
+ void DumpWebSocketMessage (uint8_t *data, uint64_t numBytes) {
182
182
bool goodMessageSize = WebSocketValidateMessageSize (data, numBytes);
183
183
if (!goodMessageSize)
184
184
return ;
@@ -189,13 +189,11 @@ void DumpWebSocketMessage(uint8_t *data, uint64_t numBytes)
189
189
190
190
printf (" Received: FIN: %d, opcode: %s, mask: 0x%08X, payload length: %llu bytes, unmasked payload:" , header->fin , WebSocketOpcodeToString (header->opcode ),
191
191
WebSocketMessageMaskingKey (data, numBytes), payloadLength);
192
- for (uint64_t i = 0 ; i < payloadLength; ++i)
193
- {
192
+ for (uint64_t i = 0 ; i < payloadLength; ++i) {
194
193
if (i%16 == 0 ) printf (" \n " );
195
194
if (i%8 ==0 ) printf (" " );
196
195
printf (" %02X" , payload[i]);
197
- if (i >= 63 && payloadLength > 64 )
198
- {
196
+ if (i >= 63 && payloadLength > 64 ) {
199
197
printf (" \n ... (%llu more bytes)" , payloadLength-i);
200
198
break ;
201
199
}
@@ -204,36 +202,33 @@ void DumpWebSocketMessage(uint8_t *data, uint64_t numBytes)
204
202
}
205
203
206
204
// connection thread manages a single active proxy connection.
207
- THREAD_RETURN_T connection_thread (void *arg)
208
- {
205
+ THREAD_RETURN_T connection_thread (void *arg) {
209
206
int client_fd = (int )(uintptr_t )arg;
210
- printf (" Established new proxy connection handler thread for incoming connection, at fd=%d\n " , client_fd); // TODO: print out getpeername()+getsockname() for more info
207
+ // TODO: print out getpeername()+getsockname() for more info
208
+ printf (" Established new proxy connection handler thread for incoming connection, at fd=%d\n " , client_fd);
211
209
212
210
// Waiting for connection upgrade handshake
213
211
char buf[BUFFER_SIZE];
214
212
int read = recv (client_fd, buf, BUFFER_SIZE, 0 );
215
213
216
- if (!read)
217
- {
214
+ if (!read) {
218
215
CloseWebSocket (client_fd);
219
216
EXIT_THREAD (0 );
220
217
}
221
218
222
- if (read < 0 )
223
- {
219
+ if (read < 0 ) {
224
220
fprintf (stderr, " Client read failed\n " );
225
221
CloseWebSocket (client_fd);
226
222
EXIT_THREAD (0 );
227
223
}
228
224
229
225
#ifdef PROXY_DEEP_DEBUG
230
226
printf (" Received:" );
231
- for (int i = 0 ; i < read; ++i)
232
- {
227
+ for (int i = 0 ; i < read; ++i) {
233
228
printf (" %02X" , buf[i]);
234
229
}
235
230
printf (" \n " );
236
- // printf("In text:\n%s\n", buf);
231
+ // printf("In text:\n%s\n", buf);
237
232
#endif
238
233
SendHandshake (client_fd, buf);
239
234
@@ -244,21 +239,18 @@ THREAD_RETURN_T connection_thread(void *arg)
244
239
std::vector<uint8_t > fragmentData;
245
240
246
241
bool connectionAlive = true ;
247
- while (connectionAlive)
248
- {
242
+ while (connectionAlive) {
249
243
int read = recv (client_fd, buf, BUFFER_SIZE, 0 );
250
244
251
245
if (!read) break ; // done reading
252
- if (read < 0 )
253
- {
246
+ if (read < 0 ) {
254
247
fprintf (stderr, " Client read failed\n " );
255
248
EXIT_THREAD (0 );
256
249
}
257
250
258
251
#ifdef PROXY_DEEP_DEBUG
259
252
printf (" Received:" );
260
- for (int i = 0 ; i < read; ++i)
261
- {
253
+ for (int i = 0 ; i < read; ++i) {
262
254
printf (" %02X" , ((unsigned char *)buf)[i]);
263
255
}
264
256
printf (" \n " );
@@ -271,19 +263,16 @@ THREAD_RETURN_T connection_thread(void *arg)
271
263
fragmentData.insert (fragmentData.end (), buf, buf+read);
272
264
273
265
// Process received fragments until there is not enough data for a full message
274
- while (!fragmentData.empty ())
275
- {
266
+ while (!fragmentData.empty ()) {
276
267
bool hasFullHeader = WebSocketHasFullHeader (&fragmentData[0 ], fragmentData.size ());
277
- if (!hasFullHeader)
278
- {
268
+ if (!hasFullHeader) {
279
269
#ifdef PROXY_DEEP_DEBUG
280
270
printf (" (not enough for a full WebSocket header)\n " );
281
271
#endif
282
272
break ;
283
273
}
284
274
uint64_t neededBytes = WebSocketFullMessageSize (&fragmentData[0 ], fragmentData.size ());
285
- if (fragmentData.size () < neededBytes)
286
- {
275
+ if (fragmentData.size () < neededBytes) {
287
276
#ifdef PROXY_DEEP_DEBUG
288
277
printf (" (not enough for a full WebSocket message, needed %d bytes)\n " , (int )neededBytes);
289
278
#endif
@@ -302,8 +291,7 @@ THREAD_RETURN_T connection_thread(void *arg)
302
291
DumpWebSocketMessage (&fragmentData[0 ], neededBytes);
303
292
#endif
304
293
305
- switch (header->opcode )
306
- {
294
+ switch (header->opcode ) {
307
295
case 0x02 : /* binary message*/ ProcessWebSocketMessage (client_fd, payload, payloadLength); break ;
308
296
case 0x08 : connectionAlive = false ; break ;
309
297
default :
@@ -323,22 +311,22 @@ THREAD_RETURN_T connection_thread(void *arg)
323
311
EXIT_THREAD (0 );
324
312
}
325
313
326
- // Technically only would need one lock per connection, but this is now one lock per all connections, which would be
327
- // slightly inefficient if we were handling multiple proxied connections at the same time. (currently that is a rare
328
- // use case, expected to only be proxying one connection at a time - if this proxy bridge is expected to be used
329
- // for hundreds of connections simultaneously, this mutex should be refactored to be per-connection)
314
+ // Technically only would need one lock per connection, but this is now one lock
315
+ // per all connections, which would be slightly inefficient if we were handling
316
+ // multiple proxied connections at the same time. (currently that is a rare use
317
+ // case, expected to only be proxying one connection at a time - if this proxy
318
+ // bridge is expected to be used for hundreds of connections simultaneously,
319
+ // this mutex should be refactored to be per-connection)
330
320
MUTEX_T webSocketSendLock;
331
321
MUTEX_T socketRegistryLock;
332
322
333
- int main (int argc, char *argv[])
334
- {
323
+ int main (int argc, char *argv[]) {
335
324
if (argc < 2 ) on_error (" websocket_to_posix_proxy creates a bridge that allows WebSocket connections on a web page to proxy out to perform TCP/UDP connections.\n Usage: %s [port]\n " , argv[0 ]);
336
325
337
326
#ifdef _WIN32
338
327
WSADATA wsaData;
339
328
int failed = WSAStartup (MAKEWORD (2 ,2 ), &wsaData);
340
- if (failed)
341
- {
329
+ if (failed) {
342
330
printf (" WSAStartup failed: %d\n " , failed);
343
331
return 1 ;
344
332
}
@@ -369,19 +357,16 @@ int main(int argc, char *argv[])
369
357
CREATE_MUTEX (&webSocketSendLock);
370
358
CREATE_MUTEX (&socketRegistryLock);
371
359
372
- while (1 )
373
- {
360
+ while (1 ) {
374
361
SOCKET_T client_fd = accept (server_fd, 0 , 0 );
375
- if (client_fd < 0 )
376
- {
362
+ if (client_fd < 0 ) {
377
363
fprintf (stderr, " Could not establish new incoming proxy connection\n " );
378
364
continue ; // Do not quit here, but keep serving any existing proxy connections.
379
365
}
380
366
381
367
THREAD_T connection;
382
368
CREATE_THREAD_RETURN_T ret = CREATE_THREAD (connection, connection_thread, (void *)(uintptr_t )client_fd);
383
- if (!CREATE_THREAD_SUCCEEDED (ret))
384
- {
369
+ if (!CREATE_THREAD_SUCCEEDED (ret)) {
385
370
fprintf (stderr, " Failed to create a connection handler thread for incoming proxy connection!\n " );
386
371
continue ; // Do not quit here, but keep program alive to manage other existing proxy connections.
387
372
}
0 commit comments