Skip to content

Commit 80a47f1

Browse files
rustyrussellcdecker
authored andcommitted
lightning_websocketd: simple proxy for websockets.
WebSocket is a bit weird: 1. It starts like an HTTP connection, but they send special headers. 2. We reply with special headers, one of which involves SHA1 of one of theirs. 3. We are then in WebSocket mode, where each frame starts with a 2-20 byte header. We relay data in a simplistic way: if either side sends something, we read it and relay it synchronously. That avoids any gratuitous buffering. Signed-off-by: Rusty Russell <[email protected]>
1 parent f78184c commit 80a47f1

File tree

7 files changed

+741
-4
lines changed

7 files changed

+741
-4
lines changed

Makefile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,8 @@ PKGLIBEXEC_PROGRAMS = \
363363
lightningd/lightning_gossipd \
364364
lightningd/lightning_hsmd \
365365
lightningd/lightning_onchaind \
366-
lightningd/lightning_openingd
366+
lightningd/lightning_openingd \
367+
lightningd/lightning_websocketd
367368

368369
# Don't delete these intermediaries.
369370
.PRECIOUS: $(ALL_GEN_HEADERS) $(ALL_GEN_SOURCES)

connectd/Makefile

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,17 @@ CONNECTD_SRC := $(CONNECTD_HEADERS:.h=.c) connectd/connectd.c
1414
CONNECTD_OBJS := $(CONNECTD_SRC:.c=.o)
1515
$(CONNECTD_OBJS): $(CONNECTD_HEADERS)
1616

17+
WEBSOCKETD_HEADERS := connectd/sha1.h
18+
WEBSOCKETD_SRC := $(WEBSOCKETD_HEADERS:.h=.c) connectd/websocketd.c
19+
20+
WEBSOCKETD_OBJS := $(WEBSOCKETD_SRC:.c=.o)
21+
$(WEBSOCKETD_OBJS): $(WEBSOCKETD_HEADERS)
22+
1723
# Make sure these depend on everything.
18-
ALL_C_SOURCES += $(CONNECTD_SRC)
19-
ALL_C_HEADERS += $(CONNECTD_HEADERS)
24+
ALL_C_SOURCES += $(CONNECTD_SRC) $(WEBSOCKETD_SRC)
25+
ALL_C_HEADERS += $(CONNECTD_HEADERS) $(WEBSOCKETD_HEADERS)
2026
ALL_PROGRAMS += lightningd/lightning_connectd
27+
ALL_PROGRAMS += lightningd/lightning_websocketd
2128

2229
# Here's what lightningd depends on
2330
LIGHTNINGD_CONTROL_HEADERS += connectd/connectd_wiregen.h
@@ -69,4 +76,6 @@ CONNECTD_COMMON_OBJS := \
6976

7077
lightningd/lightning_connectd: $(CONNECTD_OBJS) $(CONNECTD_COMMON_OBJS) $(BITCOIN_OBJS) $(WIRE_OBJS) $(HSMD_CLIENT_OBJS)
7178

79+
lightningd/lightning_websocketd: $(WEBSOCKETD_OBJS) common/setup.o common/utils.o
80+
7281
include connectd/test/Makefile

connectd/sha1.c

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/* hex variants removed -- RR */
2+
#include <connectd/sha1.h>
3+
4+
/*******************************************************************************
5+
* Teeny SHA-1
6+
*
7+
* The below sha1digest() calculates a SHA-1 hash value for a
8+
* specified data buffer and generates a hex representation of the
9+
* result. This implementation is a re-forming of the SHA-1 code at
10+
* https://github.com/jinqiangshou/EncryptionLibrary.
11+
*
12+
* Copyright (c) 2017 CTrabant
13+
*
14+
* License: MIT, see included LICENSE file for details.
15+
*
16+
* To use the sha1digest() function either copy it into an existing
17+
* project source code file or include this file in a project and put
18+
* the declaration (example below) in the sources files where needed.
19+
******************************************************************************/
20+
21+
#include <string.h>
22+
23+
/* Declaration:
24+
extern int sha1digest(uint8_t *digest, const uint8_t *data, size_t databytes);
25+
*/
26+
27+
/*******************************************************************************
28+
* sha1digest: https://github.com/CTrabant/teeny-sha1
29+
*
30+
* Calculate the SHA-1 value for supplied data buffer and generate a
31+
* text representation in hexadecimal.
32+
*
33+
* Based on https://github.com/jinqiangshou/EncryptionLibrary, credit
34+
* goes to @jinqiangshou, all new bugs are mine.
35+
*
36+
* @input:
37+
* data -- data to be hashed
38+
* databytes -- bytes in data buffer to be hashed
39+
*
40+
* @output:
41+
* digest -- the result, MUST be at least 20 bytes
42+
*
43+
* @return: 0 on success and non-zero on error.
44+
******************************************************************************/
45+
int
46+
sha1digest(uint8_t *digest, const uint8_t *data, size_t databytes)
47+
{
48+
#define SHA1ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
49+
50+
uint32_t W[80];
51+
uint32_t H[] = {0x67452301,
52+
0xEFCDAB89,
53+
0x98BADCFE,
54+
0x10325476,
55+
0xC3D2E1F0};
56+
uint32_t a;
57+
uint32_t b;
58+
uint32_t c;
59+
uint32_t d;
60+
uint32_t e;
61+
uint32_t f = 0;
62+
uint32_t k = 0;
63+
64+
uint32_t idx;
65+
uint32_t lidx;
66+
uint32_t widx;
67+
uint32_t didx = 0;
68+
69+
int32_t wcount;
70+
uint32_t temp;
71+
uint64_t databits = ((uint64_t)databytes) * 8;
72+
uint32_t loopcount = (databytes + 8) / 64 + 1;
73+
uint32_t tailbytes = 64 * loopcount - databytes;
74+
uint8_t datatail[128] = {0};
75+
76+
if (!digest)
77+
return -1;
78+
79+
if (!data)
80+
return -1;
81+
82+
/* Pre-processing of data tail (includes padding to fill out 512-bit chunk):
83+
Add bit '1' to end of message (big-endian)
84+
Add 64-bit message length in bits at very end (big-endian) */
85+
datatail[0] = 0x80;
86+
datatail[tailbytes - 8] = (uint8_t) (databits >> 56 & 0xFF);
87+
datatail[tailbytes - 7] = (uint8_t) (databits >> 48 & 0xFF);
88+
datatail[tailbytes - 6] = (uint8_t) (databits >> 40 & 0xFF);
89+
datatail[tailbytes - 5] = (uint8_t) (databits >> 32 & 0xFF);
90+
datatail[tailbytes - 4] = (uint8_t) (databits >> 24 & 0xFF);
91+
datatail[tailbytes - 3] = (uint8_t) (databits >> 16 & 0xFF);
92+
datatail[tailbytes - 2] = (uint8_t) (databits >> 8 & 0xFF);
93+
datatail[tailbytes - 1] = (uint8_t) (databits >> 0 & 0xFF);
94+
95+
/* Process each 512-bit chunk */
96+
for (lidx = 0; lidx < loopcount; lidx++)
97+
{
98+
/* Compute all elements in W */
99+
memset (W, 0, 80 * sizeof (uint32_t));
100+
101+
/* Break 512-bit chunk into sixteen 32-bit, big endian words */
102+
for (widx = 0; widx <= 15; widx++)
103+
{
104+
wcount = 24;
105+
106+
/* Copy byte-per byte from specified buffer */
107+
while (didx < databytes && wcount >= 0)
108+
{
109+
W[widx] += (((uint32_t)data[didx]) << wcount);
110+
didx++;
111+
wcount -= 8;
112+
}
113+
/* Fill out W with padding as needed */
114+
while (wcount >= 0)
115+
{
116+
W[widx] += (((uint32_t)datatail[didx - databytes]) << wcount);
117+
didx++;
118+
wcount -= 8;
119+
}
120+
}
121+
122+
/* Extend the sixteen 32-bit words into eighty 32-bit words, with potential optimization from:
123+
"Improving the Performance of the Secure Hash Algorithm (SHA-1)" by Max Locktyukhin */
124+
for (widx = 16; widx <= 31; widx++)
125+
{
126+
W[widx] = SHA1ROTATELEFT ((W[widx - 3] ^ W[widx - 8] ^ W[widx - 14] ^ W[widx - 16]), 1);
127+
}
128+
for (widx = 32; widx <= 79; widx++)
129+
{
130+
W[widx] = SHA1ROTATELEFT ((W[widx - 6] ^ W[widx - 16] ^ W[widx - 28] ^ W[widx - 32]), 2);
131+
}
132+
133+
/* Main loop */
134+
a = H[0];
135+
b = H[1];
136+
c = H[2];
137+
d = H[3];
138+
e = H[4];
139+
140+
for (idx = 0; idx <= 79; idx++)
141+
{
142+
if (idx <= 19)
143+
{
144+
f = (b & c) | ((~b) & d);
145+
k = 0x5A827999;
146+
}
147+
else if (idx >= 20 && idx <= 39)
148+
{
149+
f = b ^ c ^ d;
150+
k = 0x6ED9EBA1;
151+
}
152+
else if (idx >= 40 && idx <= 59)
153+
{
154+
f = (b & c) | (b & d) | (c & d);
155+
k = 0x8F1BBCDC;
156+
}
157+
else if (idx >= 60 && idx <= 79)
158+
{
159+
f = b ^ c ^ d;
160+
k = 0xCA62C1D6;
161+
}
162+
temp = SHA1ROTATELEFT (a, 5) + f + e + k + W[idx];
163+
e = d;
164+
d = c;
165+
c = SHA1ROTATELEFT (b, 30);
166+
b = a;
167+
a = temp;
168+
}
169+
170+
H[0] += a;
171+
H[1] += b;
172+
H[2] += c;
173+
H[3] += d;
174+
H[4] += e;
175+
}
176+
177+
/* Store binary digest in supplied buffer */
178+
if (digest)
179+
{
180+
for (idx = 0; idx < 5; idx++)
181+
{
182+
digest[idx * 4 + 0] = (uint8_t) (H[idx] >> 24);
183+
digest[idx * 4 + 1] = (uint8_t) (H[idx] >> 16);
184+
digest[idx * 4 + 2] = (uint8_t) (H[idx] >> 8);
185+
digest[idx * 4 + 3] = (uint8_t) (H[idx]);
186+
}
187+
}
188+
189+
return 0;
190+
} /* End of sha1digest() */

connectd/sha1.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#ifndef LIGHTNING_CONNECTD_SHA1_H
2+
#define LIGHTNING_CONNECTD_SHA1_H
3+
#include "config.h"
4+
#include <stdint.h>
5+
#include <stdlib.h>
6+
7+
extern int sha1digest(uint8_t *digest, const uint8_t *data, size_t databytes);
8+
9+
#endif /* LIGHTNING_CONNECTD_SHA1_H */

connectd/test/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ ALL_TEST_PROGRAMS += $(CONNECTD_TEST_PROGRAMS)
1818
$(CONNECTD_TEST_PROGRAMS): $(CONNECTD_TEST_COMMON_OBJS) $(BITCOIN_OBJS)
1919

2020
# Test objects depend on ../ src and headers.
21-
$(CONNECTD_TEST_OBJS): $(CONNECTD_HEADERS) $(CONNECTD_SRC)
21+
$(CONNECTD_TEST_OBJS): $(CONNECTD_HEADERS) $(CONNECTD_SRC) $(WEBSOCKETD_HEADERS) $(WEBSOCKETD_SRC)
2222

2323
check-units: $(CONNECTD_TEST_PROGRAMS:%=unittest/%)
2424

0 commit comments

Comments
 (0)