Tue, 07 May 2019 22:03:38 +0200
Finished websockets screens.
0 | 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 | ||
34
5c92103c5e72
Version 0.2.10. Fixed spelling error in websocket component. Updated esp-idf to v4.0-dev-459-gba1ff1692 and adjusted several sources for changed headers. Finalized keeping the AP running at all times. Increased websocket server stack depth from 6000 to 7000.
Michiel Broek <mbroek@mbse.eu>
parents:
0
diff
changeset
|
15 | static const char *TAG = "websocket"; |
0 | 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 | } |