Sun, 24 Nov 2019 16:44:00 +0100
Version 0.3.7. The WiFi task uses the new event handlers. Cooling temperature top is now 45 instead of 30 degreees for pitching Kveik. One extra cosmetic message during OTA update.
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 | } |