components/websocket/websocket.c

changeset 0
b74b0e4902c3
child 34
5c92103c5e72
equal deleted inserted replaced
-1:000000000000 0:b74b0e4902c3
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 }

mercurial