1
2
3 /*
4 * Class to deal with TLS connection to ChromCast,
5 * and send and recieve packat format data.
6 *
7 * Author: Graeme W. Gill
8 * Date: 3/9/2014
9 *
10 * Copyright 2014 Graeme W. Gill
11 * All rights reserved.
12 *
13 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
14 * see the License2.txt file for licencing details.
15 *
16 */
17
18 #include <time.h>
19 #include <stdlib.h>
20 #include <stddef.h>
21 #include <stdio.h>
22 #include <stdarg.h>
23 #include <math.h>
24 #include <assert.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <limits.h>
28 #include "copyright.h"
29 #include "aconfig.h"
30 #include <sys/types.h>
31 #include "numlib.h"
32 #include "conv.h"
33
34 #ifdef USING_AXTLS
35 # include "ssl.h" /* axTLS header */
36 #else
37 # include "openssl/ssl.h" /* OpenSSL header */
38 #endif
39
40 #undef DEBUG /* [und] */
41 #undef DUMPSDATA /* [und] Send data */
42 #undef DUMPRDATA /* [und] Receive data */
43
44 #if defined(NT) // Windows specific
45 # if _WIN32_WINNT < 0x0400
46 # undef _WIN32_WINNT
47 # define _WIN32_WINNT 0x0400 // To make it link in VS2005
48 # endif
49 # define WIN32_LEAN_AND_MEAN
50 # include <winsock2.h>
51 # include <windows.h>
52 # include <ws2tcpip.h>
53
54 # include <process.h>
55 # include <direct.h>
56 # include <io.h>
57
58 # define ERRNO GetLastError()
59 # ifndef ETIMEDOUT
60 # define ETIMEDOUT WSAETIMEDOUT
61 # endif
62
63 #else /* !NT = Unix, OS X */
64
65 # include <errno.h>
66 # include <sys/types.h>
67 # include <sys/wait.h>
68 # include <sys/socket.h>
69 # include <sys/select.h>
70 # include <netinet/in.h>
71 # include <arpa/inet.h>
72 # include <sys/time.h>
73 # include <stdint.h>
74 # include <inttypes.h>
75 # include <netdb.h>
76
77 # include <pwd.h>
78 # include <unistd.h>
79 # include <dirent.h>
80
81 #undef SYNC_SSL /* Use lock to make sure ssl_read & write don't clash. */
82
83 typedef int SOCKET;
84 # define ERRNO errno
85
86 # define INVALID_SOCKET -1
87 # define SOCKET_ERROR -1
88
89 #define closesocket(xxx) close(xxx);
90
91 #endif
92
93 #define CCPACKET_IMPL
94 #include "ccpacket.h"
95
96 /* ------------------------------------------------------------------- */
97
98 #ifdef DEBUG
99 # define DBG(xxx) a1logd xxx ;
100 #else
101 # define DBG(xxx) ;
102 #endif /* DEBUG */
103
104 /* Some OpenSSL's support
105 ERR_print_errors_fp(stderr);
106 */
107
108 /* Error message from error number */
ccpacket_emes(ccpacket_err rv)109 char *ccpacket_emes(ccpacket_err rv) {
110 #ifndef SERVER_ONLY
111 if (rv & 0x10000000) {
112 return ccpacket_emes(rv & 0x0fffffff);
113 }
114 #endif
115
116 switch(rv) {
117 case ccpacket_OK:
118 return "Packet: OK";
119 case ccpacket_context:
120 return "Packet: getting a ssl contextfailed";
121 case ccpacket_malloc:
122 return "Packet: malloc failed";
123 case ccpacket_connect:
124 return "Packet: connecting to host failed";
125 case ccpacket_ssl:
126 return "Packet: ssl connect to host failed";
127
128 case ccpacket_timeout:
129 return "Packet:: i/o timed out";
130 case ccpacket_send:
131 return "Packet: message failed to send";
132 case ccpacket_recv:
133 return "Packet: failed to read message";
134 }
135
136 return "Uknown ccpacket error";
137 }
138
139 /* Establish an ccpacket connection - implementation */
connect_ccpacket_imp(ccpacket * p)140 static ccpacket_err connect_ccpacket_imp(
141 ccpacket *p
142 ) {
143 struct sockaddr_in server; /* Server address */
144 uint8_t sesid[32] = { 0 };
145 int rv;
146
147 #ifdef NT
148 WSADATA data;
149 WSAStartup(MAKEWORD(2,2), &data);
150 #endif
151
152 server.sin_family = AF_INET;
153 server.sin_addr.s_addr = inet_addr(p->dip);
154 server.sin_port = htons((short)p->dport);
155
156 #ifdef USING_AXTLS
157 if ((p->ctx = ssl_ctx_new(SSL_SERVER_VERIFY_LATER, 1)) == NULL)
158 #else
159 // Want to use TLS_client_method(), but older OpenSSL doesn't have it...
160 if ((p->ctx = SSL_CTX_new(TLSv1_client_method())) == NULL)
161 #endif
162 {
163 DBG((g_log,0, "connect ssl_ctx_new failed\n"))
164 return ccpacket_context;
165 }
166
167 #ifndef USING_AXTLS
168 // SSL_CTX_set_mode();
169 SSL_CTX_set_verify(p->ctx, SSL_VERIFY_NONE, NULL);
170 #endif
171
172 /* Open socket */
173 if ((p->sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET) {
174 DBG((g_log,0, "socket() failed with errno %d\n",ERRNO))
175 return ccpacket_connect;
176 }
177
178 /* Make recieve timeout after 100 msec, and send timeout after 2 seconds */
179 {
180 struct linger ling;
181 #ifdef NT
182 DWORD tv;
183 #ifdef SYNC_SSL
184 tv = 100;
185 #else
186 tv = 2000;
187 #endif
188 if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,
189 sizeof(tv))) < 0) {
190 DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
191 return ccpacket_connect;
192 }
193 tv = 2000;
194 if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv,
195 sizeof(tv))) < 0) {
196 DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
197 return ccpacket_connect;
198 }
199 #else
200 struct timeval tv;
201 #ifdef SYNC_SSL
202 tv.tv_sec = 0;
203 tv.tv_usec = 100 * 1000;
204 #else
205 tv.tv_sec = 2;
206 tv.tv_usec = 0;
207 #endif
208 if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv,
209 sizeof(tv))) < 0) {
210 DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
211 return ccpacket_connect;
212 }
213 tv.tv_sec = 2;
214 tv.tv_usec = 0;
215 if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv,
216 sizeof(tv))) < 0) {
217 DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
218 return ccpacket_connect;
219 }
220 #endif
221
222 #ifdef NEVER /* This stops send timeout working - why ? */
223 ling.l_onoff = 1;
224 ling.l_linger = 2; /* Two seconds */
225 if ((rv = setsockopt(p->sock, SOL_SOCKET, SO_LINGER, (const char*)&ling,
226 sizeof(ling))) < 0) {
227 DBG((g_log,0,"setsockopt timout failed with %d, errno %d",rv,ERRNO))
228 return ccpacket_connect;
229 }
230 #endif /* NEVER */
231 }
232
233 /* Connect */
234 if ((rv = (connect(p->sock, (struct sockaddr *)&server, sizeof(server)))) != 0) {
235 DBG((g_log,0, "TCP connect IP '%s' port %d failed with %d, errno %d\n",p->dip, p->dport,rv,ERRNO))
236 return ccpacket_connect;
237 }
238 DBG((g_log,0, "socket connect IP '%s' port %d success\n",p->dip, p->dport))
239
240 /* Establish TLS */
241 /* Return nz if we can send PNG directly as base64 + bg RGB, */
242 /* else have to setup webserver and send URL */
243 #ifdef USING_AXTLS
244 if ((p->ssl = ssl_client_new(p->ctx, p->sock, sesid, 32)) == NULL)
245 #else
246 if ((p->ssl = SSL_new(p->ctx)) == NULL
247 || SSL_set_fd(p->ssl, p->sock) != 1
248 || SSL_connect(p->ssl) != 1)
249 #endif
250 {
251 DBG((g_log,0, "connect IP '%s' port %d ssl_ctx_new failed\n",p->dip, p->dport))
252 return ccpacket_ssl;
253 }
254 DBG((g_log,0, "TLS connect IP '%s' port %d success\n",p->dip, p->dport))
255 return ccpacket_OK;
256 }
257
258 /* Establish an ccpacket connection */
connect_ccpacket(ccpacket * p,char * dip,int dport)259 static ccpacket_err connect_ccpacket(
260 ccpacket *p,
261 char *dip, /* Destination IP address */
262 int dport /* Destination Port number */
263 ) {
264
265 if ((p->dip = strdup(dip)) == NULL)
266 return ccpacket_malloc;
267 p->dport = dport;
268
269 return connect_ccpacket_imp(p);
270 }
271
272 static void clear_ccpacket(ccpacket *p);
273
274 /* Re-establish communication */
re_connect_ccpacket(ccpacket * p)275 static ccpacket_err re_connect_ccpacket(
276 ccpacket *p
277 ) {
278 clear_ccpacket(p);
279 return connect_ccpacket_imp(p);
280 }
281
282 /* It appears that the axTLS library can't deal with simultanous */
283 /* send and recieve, due to the sharing of a single buffer and */
284 /* intricate dependence on this in dealing with handshaking, so */
285 /* rather than try and fix this limitation, we avoid the problem */
286 /* with a lock. Note that we need to make sure that a receive */
287 /* doesn't wait for too long, or it will block sends. */
288
289 /* Send a message */
290 /* Return ccpacket_err on error */
send_ccpacket(ccpacket * p,ORD8 * buf,ORD32 len)291 static ccpacket_err send_ccpacket(ccpacket *p,
292 ORD8 *buf, ORD32 len /* Message body to send */
293 ) {
294 int lens, ilen;
295 ORD8 *sbuf;
296 ORD32 slen;
297
298 if (p->ssl == NULL)
299 return ccpacket_ssl;
300
301 if ((sbuf = malloc(4 + len)) == NULL) {
302 return ccpacket_malloc;
303 }
304
305 write_ORD32_be(sbuf, len);
306 memcpy(sbuf+4, buf, len);
307 slen = len + 4;
308
309 #if defined(DEBUG) && defined(DUMPSDATA)
310 printf("send_ccpacket sending packet:\n");
311 adump_bytes(g_log," ", sbuf, 0, slen);
312 #endif
313
314 for (lens = 0; lens < slen; lens += ilen) {
315 DBG((g_log,0, "Sending packet %d bytes\n",slen - lens))
316 if (p->ssl == NULL) {
317 return ccpacket_ssl;
318 }
319 #ifdef SYNC_SSL
320 amutex_lock(p->lock);
321 printf("~1 send locked\n");
322 #endif
323 #ifdef USING_AXTLS
324 if ((ilen = ssl_write(p->ssl, sbuf + lens, slen - lens)) < 0)
325 #else
326 if ((ilen = SSL_write(p->ssl, sbuf + lens, slen - lens)) < 0)
327 #endif
328 {
329 #ifdef USING_AXTLS
330 DBG((g_log,0, "send failed with '%s'\n",ssl_error_string(ilen)))
331 #else
332 DBG((g_log,0, "send failed with %d\n",SSL_get_error(p->ssl, ilen)))
333 #endif
334 #ifdef SYNC_SSL
335 printf("~1 send unlocked\n");
336 amutex_unlock(p->lock);
337 #endif
338 free(sbuf);
339
340 #ifdef USING_AXTLS
341 if (ilen == SSL_TIMEDOUT)
342 #else
343 if (SSL_get_error(p->ssl, ilen) == SSL_ERROR_WANT_READ
344 || SSL_get_error(p->ssl, ilen) == SSL_ERROR_WANT_WRITE)
345 #endif
346 return ccpacket_timeout;
347 return ccpacket_send;
348 }
349 #ifdef SYNC_SSL
350 printf("~1 send unlocked\n");
351 amutex_unlock(p->lock);
352 #endif
353 }
354 free(sbuf);
355
356 return ccpacket_OK;
357 }
358
359 /* Receive a message */
360 /* Return ccpacket_err on error */
receive_ccpacket(ccpacket * p,ORD8 ** pbuf,ORD32 * plen)361 static ccpacket_err receive_ccpacket(ccpacket *p,
362 ORD8 **pbuf, ORD32 *plen /* ccpacket received, free after use */
363 ) {
364 ORD8 *ibuf; /* Buffer from ssl_read() */
365 int ioff, ilen; /* Remaining data at ioff lenght ilen in ibuf */
366 ORD8 hbuf[4], *rbuf = hbuf; /* Header buffer, returned buffer */
367 int tlen, clen, rlen; /* Target length, copy length, return length */
368
369 if (p->ssl == NULL)
370 return ccpacket_ssl;
371
372 #ifdef SYNC_SSL
373 amutex_lock(p->lock);
374 printf("~1 receive locked\n");
375 #endif
376
377 tlen = 4;
378
379 #ifndef USING_AXTLS
380 if ((ibuf = malloc(tlen)) == NULL) {
381 return ccpacket_malloc;
382 }
383 ibuf[0] = ibuf[1] = ibuf[2] = ibuf[2] = 0;
384 #endif
385
386 /* Until we have 4 bytes for the header */
387 for (rlen = 0; rlen < tlen;) {
388 ioff = 0;
389 #ifdef USING_AXTLS
390 if ((ilen = ssl_read(p->ssl, &ibuf)) < 0)
391 #else
392 if ((ilen = SSL_read(p->ssl, ibuf, tlen)) < 0)
393 #endif
394 {
395 #ifdef USING_AXTLS
396 DBG((g_log,0, "header recv failed with '%s'\n",ssl_error_string(ilen)))
397 #else
398 DBG((g_log,0, "header recv failed with %d\n",SSL_get_error(p->ssl, ilen)))
399 #endif
400 #ifdef SYNC_SSL
401 printf("~1 receive unlocked\n");
402 amutex_unlock(p->lock);
403 #endif
404 #ifdef USING_AXTLS
405 if (ilen == SSL_TIMEDOUT)
406 #else
407 free(ibuf);
408 if (SSL_get_error(p->ssl, ilen) == SSL_ERROR_WANT_READ
409 || SSL_get_error(p->ssl, ilen) == SSL_ERROR_WANT_WRITE)
410 #endif
411 return ccpacket_timeout;
412 return ccpacket_recv;
413 }
414 DBG((g_log,0, "receive_ccpacket read %d bytes\n",ilen))
415 if (ilen == 0) {
416 #ifdef USING_AXTLS
417 continue;
418 #else
419 DBG((g_log,0, "SSL_read failed\n"))
420 free(ibuf);
421 return ccpacket_recv;
422 #endif
423 }
424 if ((clen = ilen) > (tlen - rlen)) /* More than we need */
425 clen = tlen - rlen;
426 memcpy(rbuf + rlen, ibuf + ioff, clen);
427 rlen += clen;
428 ioff += clen;
429 ilen -= clen;
430 }
431 /* We have ilen left in ibuf at offset ioff */
432 DBG((g_log,0, "receive_ccpacket %d bytes left over\n",ilen))
433
434 tlen = read_ORD32_be(rbuf);
435 DBG((g_log,0, "receive_ccpacket expecting %d more bytes\n",tlen))
436
437 if (tlen < 0 || tlen > 64 * 2014) {
438 #ifdef SYNC_SSL
439 printf("~1 receive unlocked\n");
440 amutex_unlock(p->lock);
441 #endif
442 DBG((g_log,0, "receive_ccpacket got bad data length - returning error\n"))
443 return ccpacket_recv;
444 }
445
446 if ((rbuf = malloc(tlen)) == NULL) {
447 #ifdef SYNC_SSL
448 printf("~1 receive unlocked\n");
449 amutex_unlock(p->lock);
450 #endif
451 DBG((g_log,0, "receive_ccpacket malloc failed\n"))
452 return ccpacket_malloc;
453 }
454 rlen = 0;
455
456 /* Use any data we have left over from first read */
457 if (ilen > 0) {
458 if ((clen = ilen) > (tlen - rlen))
459 clen = tlen - rlen;
460 DBG((g_log,0, "receive_ccpacket using %d spair bytesr\n",clen))
461 memcpy(rbuf + rlen, ibuf + ioff, clen);
462 rlen += clen;
463 ioff += clen;
464 ilen -= clen;
465 }
466
467 #ifndef USING_AXTLS
468 free(ibuf);
469 if ((ibuf = malloc(tlen)) == NULL) {
470 return ccpacket_malloc;
471 }
472 #endif
473
474 /* Get the remainder of the body if we need to */
475 for (; rlen < tlen;) {
476 ioff = 0;
477 #ifdef USING_AXTLS
478 if ((ilen = ssl_read(p->ssl, &ibuf)) < 0)
479 #else
480 if ((ilen = SSL_read(p->ssl, ibuf, tlen)) < 0)
481 #endif
482 {
483 #ifdef USING_AXTLS
484 DBG((g_log,0, "body recv failed with '%s'\n",ssl_error_string(ilen)))
485 #else
486 DBG((g_log,0, "body recv failed with %d\n",SSL_get_error(p->ssl, ilen)))
487 #endif
488 #ifdef SYNC_SSL
489 printf("~1 receive unlocked\n");
490 amutex_unlock(p->lock);
491 #endif
492 #ifdef USING_AXTLS
493 if (ilen == SSL_TIMEDOUT)
494 #else
495 free(ibuf);
496 if (SSL_get_error(p->ssl, ilen) == SSL_ERROR_WANT_READ
497 || SSL_get_error(p->ssl, ilen) == SSL_ERROR_WANT_WRITE)
498 #endif
499 return ccpacket_timeout;
500 return ccpacket_recv;
501 }
502 DBG((g_log,0, "receive_ccpacket read %d bytes\n",ilen))
503 if (ilen == 0) {
504 #ifdef USING_AXTLS
505 continue;
506 #else
507 DBG((g_log,0, "SSL_read failed\n"))
508 free(ibuf);
509 return ccpacket_recv;
510 #endif
511 }
512 if ((clen = ilen) > (tlen - rlen))
513 clen = tlen - rlen;
514 memcpy(rbuf + rlen, ibuf + ioff, clen);
515 rlen += clen;
516 ioff += clen;
517 ilen -= clen;
518 }
519 #ifdef SYNC_SSL
520 printf("~1 receive unlocked\n");
521 amutex_unlock(p->lock);
522 #endif
523 if (ilen > 0) { /* Hmm. We should keep this for the next read ?*/
524 DBG((g_log,0, " ################## got %d byts left over ###########\n", ilen))
525 }
526 #ifndef USING_AXTLS
527 free(ibuf);
528 #endif
529 #if defined(DEBUG) && defined(DUMPRDATA)
530 printf("receive_ccpacket got:\n");
531 adump_bytes(g_log," ", rbuf, 0, rlen);
532 #endif
533 *pbuf = rbuf;
534 *plen = rlen;
535
536 return ccpacket_OK;
537 }
538
539 /* Clear the ccpacket */
clear_ccpacket(ccpacket * p)540 static void clear_ccpacket(ccpacket *p) {
541 if (p != NULL) {
542
543 if (p->ssl != NULL) {
544 #ifdef USING_AXTLS
545 ssl_free(p->ssl);
546 #else
547 SSL_free(p->ssl);
548 #endif
549 p->ssl = NULL;
550 }
551 if (p->ctx != NULL) {
552 #ifdef USING_AXTLS
553 ssl_ctx_free(p->ctx);
554 #else
555 SSL_CTX_free(p->ctx);
556 #endif
557 p->ctx = NULL;
558 }
559 if (p->sock != INVALID_SOCKET) {
560 closesocket(p->sock);
561 p->sock = 0;
562 }
563 }
564 }
565
566 /* Delete the ccpacket */
del_ccpacket(ccpacket * p)567 static void del_ccpacket(ccpacket *p) {
568 if (p != NULL) {
569 clear_ccpacket(p);
570 if (p->dip != NULL) {
571 free(p->dip);
572 p->dip = NULL;
573 }
574 #ifdef SYNC_SSL
575 amutex_del(p->lock);
576 #endif
577 free(p);
578 }
579 }
580
581 /* Create an ccpacket. */
582 /* Return NULL on error */
new_ccpacket()583 ccpacket *new_ccpacket() {
584 ccpacket *p = NULL;
585
586 #ifndef USING_AXTLS
587 SSL_load_error_strings();
588 SSL_library_init();
589 #endif
590
591 if ((p = (ccpacket *)calloc(1, sizeof(ccpacket))) == NULL) {
592 DBG((g_log,0, "calloc failed\n"))
593 return NULL;
594 }
595
596 #ifdef SYNC_SSL
597 amutex_init(p->lock);
598 #endif
599
600 /* Init method pointers */
601 p->del = del_ccpacket;
602 p->connect = connect_ccpacket;
603 p->reconnect = re_connect_ccpacket;
604 p->send = send_ccpacket;
605 p->receive = receive_ccpacket;
606
607 return p;
608 }
609
610