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