|
1 /** |
|
2 * @file websocket.c |
|
3 * @brief Websocket functions. |
|
4 * @author Blake Felt - blake.w.felt@gmail.com` |
|
5 */ |
|
6 |
|
7 #include "websocket.h" |
|
8 #include "lwip/tcp.h" // for the netconn structure |
|
9 #include "esp_system.h" // for esp_random |
|
10 #include "esp_log.h" |
|
11 #include "mbedtls/base64.h" |
|
12 #include "mbedtls/sha1.h" |
|
13 #include <string.h> |
|
14 |
|
15 static const char *TAG = "websoket"; |
|
16 |
|
17 ws_client_t ws_connect_client(struct netconn* conn, |
|
18 char* url, |
|
19 void (*ccallback)(WEBSOCKET_TYPE_t type,char* msg,uint64_t len), |
|
20 void (*scallback)(uint8_t num,WEBSOCKET_TYPE_t type,char* msg,uint64_t len) |
|
21 ) { |
|
22 ws_client_t client; |
|
23 client.conn = conn; |
|
24 client.connected = true; |
|
25 client.url = url; |
|
26 client.ping = 0; |
|
27 client.last_opcode = 0; |
|
28 client.contin = NULL; |
|
29 client.len = 0; |
|
30 client.unfinished = 0; |
|
31 client.ccallback = ccallback; |
|
32 client.scallback = scallback; |
|
33 return client; |
|
34 } |
|
35 |
|
36 void ws_disconnect_client(ws_client_t* client) { |
|
37 ws_send(client,WEBSOCKET_OPCODE_CLOSE,NULL,0,1); // tell the client to close |
|
38 if(client->conn) { |
|
39 client->conn->callback = NULL; // shut off the callback |
|
40 netconn_close(client->conn); |
|
41 netconn_delete(client->conn); |
|
42 client->conn = NULL; |
|
43 } |
|
44 client->connected = false; |
|
45 client->url = NULL; |
|
46 client->last_opcode = 0; |
|
47 if(client->len) { |
|
48 if(client->contin) |
|
49 free(client->contin); |
|
50 client->len = 0; |
|
51 } |
|
52 client->ccallback = NULL; |
|
53 client->scallback = NULL; |
|
54 } |
|
55 |
|
56 bool ws_is_connected(ws_client_t client) { |
|
57 if (client.conn && client.connected) |
|
58 return 1; |
|
59 return 0; |
|
60 } |
|
61 |
|
62 static void ws_generate_mask(ws_header_t* header) { |
|
63 header->param.bit.MASK = 1; |
|
64 header->key.full = esp_random(); // generate a random 32 bit number |
|
65 } |
|
66 |
|
67 static void ws_encrypt_decrypt(char* msg,ws_header_t header) { |
|
68 if(header.param.bit.MASK) { |
|
69 for(uint64_t i=0; i<header.length; i++) { |
|
70 msg[i] ^= header.key.part[i%4]; |
|
71 } |
|
72 } |
|
73 } |
|
74 |
|
75 |
|
76 |
|
77 err_t ws_send(ws_client_t* client,WEBSOCKET_OPCODES_t opcode,char* msg,uint64_t len,bool mask) |
|
78 { |
|
79 static char *out; |
|
80 static char *encrypt; |
|
81 static uint64_t pos; |
|
82 static uint64_t true_len; |
|
83 ws_header_t header; |
|
84 static err_t err; |
|
85 |
|
86 header.param.pos.ZERO = 0; // reset the whole header |
|
87 header.param.pos.ONE = 0; |
|
88 |
|
89 header.param.bit.FIN = 1; // all pieces are done (you don't need a huge message anyway...) |
|
90 header.param.bit.OPCODE = opcode; |
|
91 // populate LEN field |
|
92 pos = 2; |
|
93 header.length = len; |
|
94 if (len<125) { |
|
95 header.param.bit.LEN = len; |
|
96 } else if (len < 65536) { |
|
97 header.param.bit.LEN = 126; |
|
98 pos += 2; |
|
99 } else { |
|
100 header.param.bit.LEN = 127; |
|
101 pos += 8; |
|
102 } |
|
103 |
|
104 if (mask) { |
|
105 ws_generate_mask(&header); // get a key |
|
106 encrypt = malloc(len); // allocate memory for the encryption |
|
107 memcpy(encrypt,msg,len); |
|
108 ws_encrypt_decrypt(encrypt,header); // encrypt it! |
|
109 pos += 4; // add the position |
|
110 } |
|
111 |
|
112 true_len = pos+len; // get the length of the entire message |
|
113 pos = 2; |
|
114 out = malloc(true_len); // allocate dat memory |
|
115 |
|
116 if (out == NULL) { |
|
117 ESP_LOGE(TAG, "ws_send malloc error for %llu bytes", true_len); |
|
118 return ERR_MEM; |
|
119 } |
|
120 |
|
121 out[0] = header.param.pos.ZERO; // save header |
|
122 out[1] = header.param.pos.ONE; |
|
123 |
|
124 // put in the length, if necessary |
|
125 if(header.param.bit.LEN == 126) { |
|
126 out[2] = (len >> 8) & 0xFF; |
|
127 out[3] = (len ) & 0xFF; |
|
128 pos = 4; |
|
129 } |
|
130 if(header.param.bit.LEN == 127) { |
|
131 //memcpy(&out[2],&len,8); |
|
132 out[2] = (len >> 56) & 0xFF; |
|
133 out[3] = (len >> 48) & 0xFF; |
|
134 out[4] = (len >> 40) & 0xFF; |
|
135 out[5] = (len >> 32) & 0xFF; |
|
136 out[6] = (len >> 24) & 0xFF; |
|
137 out[7] = (len >> 16) & 0xFF; |
|
138 out[8] = (len >> 8) & 0xFF; |
|
139 out[9] = (len) & 0xFF; |
|
140 pos = 10; |
|
141 } |
|
142 |
|
143 if(mask) { |
|
144 out[pos] = header.key.part[0]; pos++; |
|
145 out[pos] = header.key.part[1]; pos++; |
|
146 out[pos] = header.key.part[2]; pos++; |
|
147 out[pos] = header.key.part[3]; pos++; |
|
148 memcpy(&out[pos],encrypt,len); // put in the encrypted message |
|
149 free(encrypt); |
|
150 } else { |
|
151 memcpy(&out[pos],msg,len); |
|
152 } |
|
153 |
|
154 err = netconn_write(client->conn,out,true_len,NETCONN_COPY); // finally! send it. |
|
155 if (err != ERR_OK) { |
|
156 client->connected = false; |
|
157 ESP_LOGE(TAG, "ws_send netconn_write error %d", err); |
|
158 } |
|
159 free(out); // free the entire message |
|
160 return err; |
|
161 } |
|
162 |
|
163 |
|
164 |
|
165 char* ws_read(ws_client_t* client,ws_header_t* header) |
|
166 { |
|
167 char *ret, *append; |
|
168 err_t err; |
|
169 struct netbuf *inbuf, *inbuf2; |
|
170 char *buf, *buf2; |
|
171 uint16_t len, len2; |
|
172 uint64_t pos, cont_len, cont_pos; |
|
173 |
|
174 // if we read from this previously (not cont frames), stop reading |
|
175 if(client->unfinished) { |
|
176 client->unfinished--; |
|
177 return NULL; |
|
178 } |
|
179 |
|
180 err = netconn_recv(client->conn,&inbuf); |
|
181 if (err != ERR_OK) { |
|
182 client->connected = false; |
|
183 ESP_LOGE(TAG, "ws_read netconn_recv error %d", err); |
|
184 return NULL; |
|
185 } |
|
186 netbuf_data(inbuf,(void**)&buf, &len); |
|
187 if(!buf) return NULL; |
|
188 |
|
189 // get the header |
|
190 header->param.pos.ZERO = buf[0]; |
|
191 header->param.pos.ONE = buf[1]; |
|
192 |
|
193 // get the message length |
|
194 pos = 2; |
|
195 if(header->param.bit.LEN < 125) { |
|
196 header->length = header->param.bit.LEN; |
|
197 } else if(header->param.bit.LEN == 126) { |
|
198 header->length = buf[2] << 8 | buf[3]; |
|
199 pos = 4; |
|
200 } else { // LEN = 127 |
|
201 header->length = (uint64_t)buf[2] << 56 | (uint64_t)buf[3] << 48 |
|
202 | (uint64_t)buf[4] << 40 | (uint64_t)buf[5] << 32 |
|
203 | (uint64_t)buf[6] << 24 | (uint64_t)buf[7] << 16 |
|
204 | (uint64_t)buf[8] << 8 | (uint64_t)buf[9]; |
|
205 pos = 10; |
|
206 } |
|
207 |
|
208 if(header->param.bit.MASK) { |
|
209 memcpy(&(header->key.full),&buf[pos],4); // extract the key |
|
210 pos += 4; |
|
211 } |
|
212 |
|
213 ret = malloc(header->length+1); // allocate memory, plus a byte |
|
214 if(!ret) { |
|
215 ESP_LOGE(TAG, "ws_read malloc error for %llu bytes", header->length+1); |
|
216 netbuf_delete(inbuf); |
|
217 header->received = 0; |
|
218 return NULL; |
|
219 } |
|
220 |
|
221 cont_len = len-pos; // get the actual length |
|
222 memcpy(ret,&buf[pos],header->length); // allocate the total memory |
|
223 cont_pos = cont_len; // get the initial position |
|
224 |
|
225 // netconn gives messages in pieces, so we need to get those (different than OPCODE_CONT) |
|
226 while(cont_len < header->length) { // while the actual length is less than the header stated |
|
227 err = netconn_recv(client->conn,&inbuf2); |
|
228 if (err != ERR_OK) { |
|
229 ESP_LOGE(TAG, "ws_read netconn_recv error %d", err); |
|
230 netbuf_delete(inbuf2); |
|
231 free(ret); |
|
232 client->unfinished = 0; |
|
233 client->connected = false; |
|
234 header->received = 0; |
|
235 return NULL; |
|
236 } |
|
237 |
|
238 netbuf_data(inbuf2,(void**)&buf2, &len2); |
|
239 memcpy(&ret[cont_pos],buf2,len2); |
|
240 cont_pos += len2; |
|
241 if (!buf2) { |
|
242 client->unfinished = 0; |
|
243 header->received = 0; |
|
244 } |
|
245 netbuf_delete(inbuf2); |
|
246 client->unfinished++; |
|
247 cont_len += len2; |
|
248 } |
|
249 |
|
250 ret[header->length] = '\0'; // end string |
|
251 ws_encrypt_decrypt(ret,*header); // unencrypt, if necessary |
|
252 |
|
253 if(header->param.bit.FIN == 0) { // if the message isn't done |
|
254 if((header->param.bit.OPCODE == WEBSOCKET_OPCODE_CONT) && |
|
255 ((client->last_opcode==WEBSOCKET_OPCODE_BIN) || (client->last_opcode==WEBSOCKET_OPCODE_TEXT))) { |
|
256 cont_len = header->length + client->len; |
|
257 append = malloc(cont_len); |
|
258 memcpy(append,client->contin,client->len); |
|
259 memcpy(&append[client->len],ret,header->length); |
|
260 free(client->contin); |
|
261 client->contin = malloc(cont_len); |
|
262 client->len = cont_len; |
|
263 |
|
264 free(append); |
|
265 free(ret); |
|
266 netbuf_delete(inbuf); |
|
267 //free(buf); |
|
268 return NULL; |
|
269 } |
|
270 else if((header->param.bit.OPCODE==WEBSOCKET_OPCODE_BIN) || (header->param.bit.OPCODE==WEBSOCKET_OPCODE_TEXT)) { |
|
271 if(client->len) { |
|
272 free(client->contin); |
|
273 } |
|
274 client->contin = malloc(header->length); |
|
275 memcpy(client->contin,ret,header->length); |
|
276 client->len = header->length; |
|
277 client->last_opcode = header->param.bit.OPCODE; |
|
278 |
|
279 free(ret); |
|
280 netbuf_delete(inbuf); |
|
281 //free(buf); |
|
282 return NULL; |
|
283 } |
|
284 else { // there shouldn't be another FIN code.... |
|
285 free(ret); |
|
286 netbuf_delete(inbuf); |
|
287 //free(buf); |
|
288 return NULL; |
|
289 } |
|
290 } |
|
291 client->last_opcode = header->param.bit.OPCODE; |
|
292 if(inbuf) netbuf_delete(inbuf); |
|
293 header->received = 1; |
|
294 return ret; |
|
295 } |
|
296 |
|
297 char* ws_hash_handshake(char* handshake,uint8_t len) { |
|
298 const char hash[] = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; |
|
299 const uint8_t hash_len = sizeof(hash); |
|
300 char* ret; |
|
301 char key[64]; |
|
302 unsigned char sha1sum[20]; |
|
303 unsigned int ret_len; |
|
304 |
|
305 if(!len) return NULL; |
|
306 ret = malloc(32); |
|
307 |
|
308 memcpy(key,handshake,len); |
|
309 strlcpy(&key[len],hash,sizeof(key)); |
|
310 mbedtls_sha1((unsigned char*)key,len+hash_len-1,sha1sum); |
|
311 mbedtls_base64_encode(NULL, 0, &ret_len, sha1sum, 20); |
|
312 if(!mbedtls_base64_encode((unsigned char*)ret,32,&ret_len,sha1sum,20)) { |
|
313 ret[ret_len] = '\0'; |
|
314 return ret; |
|
315 } |
|
316 free(ret); |
|
317 return NULL; |
|
318 } |