source: trunk/module-ghttp.c

Last change on this file was 11666, checked in by gorgone, 3 years ago

fix build warnings for
module-cccam
module-ghttp
oscam-config

File size: 21.3 KB
Line 
1#define MODULE_LOG_PREFIX "ghttp"
2
3#include "globals.h"
4#ifdef MODULE_GHTTP
5#include "oscam-client.h"
6#include "oscam-net.h"
7#include "oscam-string.h"
8#include "oscam-reader.h"
9#include "oscam-work.h"
10#include "module-dvbapi.h"
11#ifdef WITH_SSL
12#include <openssl/crypto.h>
13#include <openssl/ssl.h>
14#include <openssl/err.h>
15#endif
16
17typedef struct
18{
19 uint8_t *session_id;
20 uint8_t *host_id;
21 uint8_t *fallback_id;
22 pthread_mutex_t conn_mutex;
23 LLIST *post_contexts;
24 LLIST *ecm_q;
25#ifdef WITH_SSL
26 SSL *ssl_handle;
27#endif
28} s_ghttp;
29
30typedef struct
31{
32 uint16_t onid;
33 uint16_t tsid;
34 uint16_t sid;
35 uint16_t pid;
36} s_ca_context;
37
38static LLIST *ghttp_ignored_contexts;
39#ifdef WITH_SSL
40static SSL_CTX *ghttp_ssl_context;
41#endif
42
43static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST *er);
44
45#ifdef WITH_SSL
46static bool _ssl_connect(struct s_client *client, int32_t fd)
47{
48 s_ghttp *context = (s_ghttp *)client->ghttp;
49
50 if(context->ssl_handle) // cleanup previous
51 {
52 SSL_shutdown(context->ssl_handle);
53 SSL_free(context->ssl_handle);
54 }
55
56 cs_log_dbg(D_CLIENT, "%s: trying ssl...", client->reader->label);
57
58 context->ssl_handle = SSL_new(ghttp_ssl_context);
59 if(context->ssl_handle == NULL)
60 {
61 ERR_print_errors_fp(stderr);
62#if OPENSSL_VERSION_NUMBER < 0x1010005fL
63 ERR_remove_state(0);
64#endif
65 return false;
66 }
67 if(!SSL_set_fd(context->ssl_handle, fd))
68 {
69 ERR_print_errors_fp(stderr);
70#if OPENSSL_VERSION_NUMBER < 0x1010005fL
71 ERR_remove_state(0);
72#endif
73 return false;
74 }
75 if(SSL_connect(context->ssl_handle) != 1)
76 {
77 ERR_print_errors_fp(stderr);
78#if OPENSSL_VERSION_NUMBER < 0x1010005fL
79 ERR_remove_state(0);
80#endif
81 }
82
83 if(context->ssl_handle)
84 {
85 cs_log_dbg(D_CLIENT, "%s: ssl established", client->reader->label);
86 return true;
87 }
88
89 return false;
90}
91#endif
92
93int32_t ghttp_client_init(struct s_client *cl)
94{
95 int32_t handle;
96 char *str = NULL;
97
98 ghttp_ignored_contexts = ll_create("ignored contexts");
99#ifdef WITH_SSL
100 ghttp_ssl_context = SSL_CTX_new(SSLv23_client_method());
101 if(ghttp_ssl_context == NULL)
102 {
103 ERR_print_errors_fp(stderr);
104#if OPENSSL_VERSION_NUMBER < 0x1010005fL
105 ERR_remove_state(0);
106#endif
107 }
108#endif
109
110 if(cl->reader->r_port == 0)
111 {
112 cl->reader->r_port = cl->reader->ghttp_use_ssl ? 443 : 80;
113 }
114
115 str = strstr(cl->reader->device, ".");
116 if(!str)
117 {
118 char host[128];
119 cs_strncpy(host, cl->reader->device, sizeof(host));
120 snprintf(cl->reader->device, sizeof(cl->reader->device), "%.115s.appspot.com", host);
121 }
122
123 cs_log("%s: init google cache client %s:%d (fd=%d)", cl->reader->label, cl->reader->device, cl->reader->r_port, cl->udp_fd);
124
125 if(cl->udp_fd)
126 {
127 network_tcp_connection_close(cl->reader, "re-init");
128 }
129
130 handle = network_tcp_connection_open(cl->reader);
131 if(handle < 0)
132 {
133 return -1;
134 }
135
136 cl->reader->tcp_connected = 2;
137 cl->reader->card_status = CARD_INSERTED;
138 cl->reader->last_g = cl->reader->last_s = time((time_t *)0);
139
140 cl->pfd = cl->udp_fd;
141 if(!cl->ghttp)
142 {
143 if(!cs_malloc(&(cl->ghttp), sizeof(s_ghttp)))
144 {
145 return -1;
146 }
147 memset(cl->ghttp, 0, sizeof(s_ghttp));
148 ((s_ghttp *)cl->ghttp)->post_contexts = ll_create("post contexts");
149 ((s_ghttp *)cl->ghttp)->ecm_q = ll_create("ecm queue");
150 }
151 else
152 {
153 ll_clear(((s_ghttp *)cl->ghttp)->ecm_q);
154 }
155
156 if(cl->reader->ghttp_use_ssl)
157 {
158#ifndef WITH_SSL
159 cs_log("%s: use_ssl set but no ssl support available, aborting...", cl->reader->label);
160 return -1;
161#endif
162#ifdef WITH_SSL
163 if(ghttp_ssl_context == NULL)
164 {
165 return -1;
166 }
167
168 if(_ssl_connect(cl, handle))
169 {
170 cl->crypted = 1;
171 }
172 else
173 {
174 network_tcp_connection_close(cl->reader, "ssl failed");
175 return -1;
176 }
177#endif
178 }
179
180 return 0;
181}
182
183static uint32_t javastring_hashcode(uint8_t *input, int32_t len)
184{
185 uint32_t h = 0;
186 while(/**input &&*/ len--)
187 {
188 h = 31 * h + *input++;
189 }
190 return h;
191}
192
193static int32_t ghttp_send_int(struct s_client *client, uint8_t *buf, int32_t l)
194{
195 cs_log_dbg(D_CLIENT, "%s: sending %d bytes", client->reader->label, l);
196 if(!client->pfd)
197 {
198 // disconnected? try reinit.
199 cs_log_dbg(D_CLIENT, "%s: disconnected?", client->reader->label);
200 ghttp_client_init(client);
201 }
202
203#ifdef WITH_SSL
204 s_ghttp *context = (s_ghttp *)client->ghttp;
205 if(client->reader->ghttp_use_ssl)
206 {
207 return SSL_write(context->ssl_handle, buf, l);
208 }
209#endif
210 return send(client->pfd, buf, l, 0);
211}
212
213static int32_t ghttp_send(struct s_client *client, uint8_t *buf, int32_t l)
214{
215 s_ghttp *context = (s_ghttp *)client->ghttp;
216 SAFE_MUTEX_LOCK(&context->conn_mutex);
217 int32_t ret = ghttp_send_int(client, buf, l);
218 SAFE_MUTEX_UNLOCK(&context->conn_mutex);
219 return ret;
220}
221
222static int32_t ghttp_recv_int(struct s_client *client, uint8_t *buf, int32_t l)
223{
224 int32_t n = -1;
225 s_ghttp *context = (s_ghttp *)client->ghttp;
226
227 if(!client->pfd)
228 {
229 ll_clear(context->ecm_q);
230 return -1;
231 }
232
233 if(client->reader->ghttp_use_ssl)
234 {
235#ifdef WITH_SSL
236 n = SSL_read(context->ssl_handle, buf, l);
237#endif
238 }
239 else
240 {
241 n = cs_recv(client->pfd, buf, l, 0);
242 }
243
244 if(n > 0)
245 {
246 cs_log_dbg(D_CLIENT, "%s: received %d bytes from %s", client->reader->label, n, remote_txt());
247 client->last = time((time_t *)0);
248
249 if(n > 400)
250 {
251 buf[n] = '\0';
252 cs_log_dbg(D_CLIENT, "%s: unexpected reply size %d - %s", client->reader->label, n, buf);
253 return -1; // assumes google error, disconnects
254 }
255 }
256
257 if(n < 5)
258 {
259 cs_log_dbg(D_CLIENT, "%s: read %d bytes, disconnecting", client->reader->label, n);
260 n = -1;
261 }
262 return n;
263}
264
265static int32_t ghttp_recv(struct s_client *client, uint8_t *buf, int32_t l)
266{
267 s_ghttp *context = (s_ghttp *)client->ghttp;
268 SAFE_MUTEX_LOCK(&context->conn_mutex);
269 int32_t ret = ghttp_recv_int(client, buf, l);
270 SAFE_MUTEX_UNLOCK(&context->conn_mutex);
271 return ret;
272}
273
274static bool _is_post_context(LLIST *ca_contexts, ECM_REQUEST *er, bool remove_data)
275{
276 s_ca_context *ctx;
277 s_ca_context *existing = NULL;
278
279 if(cs_malloc(&ctx, sizeof(s_ca_context)))
280 {
281 ctx->onid = er->onid;
282 ctx->tsid = er->tsid;
283 ctx->sid = er->srvid;
284 ctx->pid = 0;
285
286 existing = (s_ca_context *)ll_contains_data(ca_contexts, ctx, sizeof(s_ca_context));
287
288 if(remove_data)
289 {
290 ll_remove_data(ca_contexts, existing);
291 }
292 NULLFREE(ctx);
293 }
294 return existing != NULL;
295}
296
297static void _add_context(LLIST *ca_contexts, s_ca_context *context)
298{
299 if(!ll_contains_data(ca_contexts, context, sizeof(s_ca_context)))
300 {
301 ll_append(ca_contexts, context);
302 }
303 else
304 {
305 NULLFREE(context);
306 }
307
308 while(ll_count(ca_contexts) > 64)
309 {
310 ll_remove_first_data(ca_contexts);
311 }
312
313 cs_log_dbg(D_CLIENT, "ca contexts size %d", ll_count(ca_contexts));
314}
315
316static void _set_pid_status(LLIST *ca_contexts, uint16_t onid, uint16_t tsid, uint16_t sid, uint16_t pid)
317{
318 s_ca_context *ctx;
319 if(cs_malloc(&ctx, sizeof(s_ca_context)))
320 {
321 ctx->onid = onid;
322 ctx->tsid = tsid;
323 ctx->sid = sid;
324 ctx->pid = pid;
325 _add_context(ca_contexts, ctx);
326 }
327}
328
329static void _set_pids_status(LLIST *ca_contexts, uint16_t onid, uint16_t tsid, uint16_t sid, uint8_t *buf, int len)
330{
331 int8_t offs = 0;
332 uint16_t pid = 0;
333
334 while(offs < len)
335 {
336 pid = b2i(2, buf + offs);
337 offs += 2;
338 _set_pid_status(ca_contexts, onid, tsid, sid, pid);
339 }
340}
341
342static bool _swap_hosts(s_ghttp *context)
343{
344 if(!context->fallback_id)
345 {
346 return false;
347 }
348
349 uint8_t *tmp = context->host_id;
350 context->host_id = context->fallback_id;
351 context->fallback_id = tmp;
352 NULLFREE(context->session_id);
353 ll_clear(context->ecm_q);
354 ll_clear_data(ghttp_ignored_contexts);
355 return true;
356}
357
358static char *_get_header_substr(uint8_t *buf, const char *start, const char *end)
359{
360 char *data = strstr((char *)buf, start);
361 if(!data)
362 {
363 return NULL;
364 }
365
366 data += cs_strlen(start);
367 int len = strstr(data, end) - data;
368 if(len <= 0)
369 {
370 return NULL;
371 }
372
373 char tmp = data[len];
374 data[len] = '\0';
375 char *value = cs_strdup(data);
376 data[len] = tmp;
377 return value;
378}
379
380static int _get_int_header(uint8_t *buf, const char *start)
381{
382 char *data = strstr((char *)buf, start);
383 if(!data) { return -1; }
384 data += cs_strlen(start);
385 return atoi(data);
386}
387
388static char *_get_header(uint8_t *buf, const char *start)
389{
390 return _get_header_substr(buf, start, "\r\n");
391}
392
393static int32_t ghttp_recv_chk(struct s_client *client, uint8_t *dcw, int32_t *rc, uint8_t *buf, int32_t n)
394{
395 char *data;
396 char *hdrstr;
397 uint8_t *content;
398 int rcode, len, clen = 0;
399 s_ghttp *context = (s_ghttp *)client->ghttp;
400 ECM_REQUEST *er = NULL;
401
402 if(n < 5)
403 {
404 return -1;
405 }
406
407 data = strstr((char *)buf, "HTTP/1.1 ");
408 if(!data || ll_count(context->ecm_q) > 6)
409 {
410 cs_log_dbg(D_CLIENT, "%s: non http or otherwise corrupt response: %s", client->reader->label, buf);
411 cs_log_dump_dbg(D_CLIENT, buf, n, "%s: ", client->reader->label);
412 network_tcp_connection_close(client->reader, "receive error");
413 NULLFREE(context->session_id);
414 ll_clear(context->ecm_q);
415 return -1;
416 }
417
418 LL_ITER itr = ll_iter_create(context->ecm_q);
419 er = (ECM_REQUEST *)ll_iter_next(&itr);
420
421 rcode = _get_int_header(buf, "HTTP/1.1 ");
422 clen = _get_int_header(buf, "Content-Length: ");
423
424 content = (uint8_t *)(strstr(data, "\r\n\r\n") + 4);
425
426 hdrstr = _get_header_substr(buf, "ETag: \"", "\"\r\n");
427 if(hdrstr)
428 {
429 NULLFREE(context->host_id);
430 context->host_id = (uint8_t *)hdrstr;
431 cs_log_dbg(D_CLIENT, "%s: new name: %s", client->reader->label, context->host_id);
432
433 len = b64decode(context->host_id);
434 if(len == 0 || len >= 64)
435 {
436 NULLFREE(context->host_id);
437 }
438 else
439 {
440 cs_log_dbg(D_CLIENT, "%s: redirected...", client->reader->label);
441 NULLFREE(context->session_id);
442 ll_clear_data(ghttp_ignored_contexts);
443 ll_clear(context->ecm_q);
444 return -1;
445 }
446 }
447
448 hdrstr = _get_header_substr(buf, "ETag: W/\"", "\"\r\n");
449 if(hdrstr)
450 {
451 NULLFREE(context->fallback_id);
452 context->fallback_id = (uint8_t *)hdrstr;
453 cs_log_dbg(D_CLIENT, "%s: new fallback name: %s", client->reader->label, context->fallback_id);
454
455 len = b64decode(context->fallback_id);
456 if(len == 0 || len >= 64)
457 {
458 NULLFREE(context->fallback_id);
459 }
460 }
461
462 hdrstr = _get_header(buf, "Set-Cookie: GSSID=");
463 if(hdrstr)
464 {
465 NULLFREE(context->session_id);
466 context->session_id = (uint8_t *)hdrstr;
467 cs_log_dbg(D_CLIENT, "%s: set session_id to: %s", client->reader->label, context->session_id);
468 }
469
470 // buf[n] = '\0';
471 // cs_log_dump_dbg(D_TRACE, content, clen, "%s: reply\n%s", client->reader->label, buf);
472
473 if(rcode < 200 || rcode > 204)
474 {
475 cs_log_dbg(D_CLIENT, "%s: http error code %d", client->reader->label, rcode);
476 data = strstr((char *)buf, "Content-Type: application/octet-stream"); // if not octet-stream, google error. need reconnect?
477 if(data) // we have error info string in the post content
478 {
479 if(clen > 0)
480 {
481 content[clen] = '\0';
482 cs_log_dbg(D_CLIENT, "%s: http error message: %s", client->reader->label, content);
483 }
484 }
485
486 if(rcode == 503)
487 {
488 if(er && _is_post_context(context->post_contexts, er, false))
489 {
490 if(_swap_hosts(context))
491 {
492 cs_log_dbg(D_CLIENT, "%s: switching to fallback", client->reader->label);
493 }
494 else
495 {
496 cs_log_dbg(D_CLIENT, "%s: recv_chk got 503 despite post, trying reconnect", client->reader->label);
497 network_tcp_connection_close(client->reader, "reconnect");
498 ll_clear(context->ecm_q);
499 }
500 }
501 else
502 {
503 // on 503 cache timeout, retry with POST immediately (and switch to POST for subsequent)
504 if(er)
505 {
506 _set_pid_status(context->post_contexts, er->onid, er->tsid, er->srvid, 0);
507 cs_log_dbg(D_CLIENT, "%s: recv_chk got 503, trying direct post", client->reader->label);
508 _ghttp_post_ecmdata(client, er);
509 }
510 }
511 }
512 else if(rcode == 401)
513 {
514 NULLFREE(context->session_id);
515 if(er)
516 {
517 cs_log_dbg(D_CLIENT, "%s: session expired, trying direct post", client->reader->label);
518 _ghttp_post_ecmdata(client, er);
519 }
520 }
521 else if(rcode == 403)
522 {
523 client->reader->enable = 0;
524 network_tcp_connection_close(client->reader, "login failure");
525 ll_clear(context->ecm_q);
526 cs_log("%s: invalid username/password, disabling reader.", client->reader->label);
527 }
528
529 // not sure if this is needed on failure, copied from newcamd
530 *rc = 0;
531 memset(dcw, 0, 16);
532
533 return -1;
534 }
535
536 // successful http reply (200 ok or 204 no content)
537
538 hdrstr = _get_header(buf, "Pragma: context-ignore=");
539 if(hdrstr)
540 {
541 if(clen > 1)
542 {
543 cs_log_dump_dbg(D_CLIENT, content, clen, "%s: pmt ignore reply - %s (%d pids)",
544 client->reader->label, hdrstr, clen / 2);
545
546 uint32_t onid = 0, tsid = 0, sid = 0;
547
548 if(sscanf(hdrstr, "%4x-%4x-%4x", &onid, &tsid, &sid) == 3)
549 {
550 _set_pids_status(ghttp_ignored_contexts, onid, tsid, sid, content, clen);
551 }
552 NULLFREE(hdrstr);
553 return -1;
554 }
555 NULLFREE(hdrstr);
556 }
557
558 data = strstr((char *)buf, "Pragma: context-ignore-clear");
559 if(data)
560 {
561 cs_log_dbg(D_CLIENT, "%s: clearing local ignore list (size %d)",
562 client->reader->label, ll_count(ghttp_ignored_contexts));
563
564 ll_clear_data(ghttp_ignored_contexts);
565 }
566
567 // switch back to cache get after rapid ecm response (arbitrary atm), only effect is a slight bw save for client
568 if(!er || _is_post_context(context->post_contexts, er, false))
569 {
570 data = strstr((char *)buf, "Pragma: cached");
571 if(data || (client->cwlastresptime > 0 && client->cwlastresptime < 640))
572 {
573 cs_log_dbg(D_CLIENT, "%s: probably cached cw (%d ms), switching back to cache get for next req",
574 client->reader->label, client->cwlastresptime);
575
576 if(er)
577 {
578 _is_post_context(context->post_contexts, er, true);
579 }
580 }
581 }
582
583 if(clen == 16) // cw in content
584 {
585 memcpy(dcw, content, 16);
586 *rc = 1;
587 er = ll_remove_first(context->ecm_q);
588 if(!er)
589 {
590 return -1;
591 }
592
593 cs_log_dump_dbg(D_TRACE, dcw, 16, "%s: cw recv chk for idx %d", client->reader->label, er->idx);
594 return er->idx;
595 }
596 else
597 {
598 if(clen != 0)
599 {
600 cs_log_dump_dbg(D_CLIENT, content, clen, "%s: recv_chk fail, clen = %d", client->reader->label, clen);
601 }
602 }
603 return -1;
604}
605
606static char *_ghttp_basic_auth(struct s_client *client)
607{
608 uint8_t auth[64];
609 char *encauth = NULL;
610 int32_t ret;
611 s_ghttp *context = (s_ghttp *)client->ghttp;
612
613 if(!context->session_id && cs_strlen(client->reader->r_usr) > 0)
614 {
615 cs_log_dbg(D_CLIENT, "%s: username specified and no existing session, adding basic auth", client->reader->label);
616 ret = snprintf((char *)auth, sizeof(auth), "%s:%s", client->reader->r_usr, client->reader->r_pwd);
617 b64encode((char *)auth, ret, &encauth);
618 }
619 return encauth;
620}
621
622static int32_t _ghttp_http_get(struct s_client *client, uint32_t hash, int odd)
623{
624 uint8_t req[128];
625 char *encauth = NULL;
626 int32_t ret;
627 s_ghttp *context = (s_ghttp *)client->ghttp;
628
629 encauth = _ghttp_basic_auth(client);
630
631 if(encauth) // basic auth login
632 {
633 ret = snprintf((char *)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s\r\n\r\n",
634 odd ? 81 : 80, hash, context->host_id, encauth);
635
636 NULLFREE(encauth);
637 }
638 else
639 {
640 if(context->session_id) // session exists
641 {
642 ret = snprintf((char *)req, sizeof(req), "GET /api/c/%s/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n",
643 context->session_id, odd ? 81 : 80, hash, context->host_id);
644 }
645 else // no credentials configured, assume no session required
646 {
647 ret = snprintf((char *)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n",
648 odd ? 81 : 80, hash, context->host_id);
649 }
650 }
651
652 ret = ghttp_send(client, req, ret);
653
654 return ret;
655}
656
657static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST *er)
658{
659 uint8_t req[640];
660 uint8_t *end;
661 char *encauth = NULL;
662 int32_t ret;
663 s_ghttp *context = (s_ghttp *)client->ghttp;
664
665 encauth = _ghttp_basic_auth(client);
666
667 if(encauth) // basic auth login
668 {
669 ret = snprintf((char *)req, sizeof(req), "POST /api/e/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s\r\nContent-Length: %d\r\n\r\n",
670 er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, encauth, er->ecmlen);
671
672 NULLFREE(encauth);
673 }
674 else
675 {
676 if(context->session_id) // session exists
677 {
678 ret = snprintf((char *)req, sizeof(req), "POST /api/e/%s/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n",
679 context->session_id, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, er->ecmlen);
680 }
681 else // no credentials configured, assume no session required
682 {
683 ret = snprintf((char *)req, sizeof(req), "POST /api/e/%x/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nContent-Length: %d\r\n\r\n",
684 er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, context->host_id, er->ecmlen);
685 }
686 }
687 end = req + ret;
688 memcpy(end, er->ecm, er->ecmlen);
689
690 cs_log_dbg(D_CLIENT, "%s: sending full ecm - /api/e/%x/%x/%x/%x/%x/%x",
691 client->reader->label, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid);
692
693 ret = ghttp_send(client, req, ret + er->ecmlen);
694
695 return ret;
696}
697
698static bool _is_pid_ignored(ECM_REQUEST *er)
699{
700 s_ca_context *ignore;
701 if(cs_malloc(&ignore, sizeof(s_ca_context)))
702 {
703 ignore->onid = er->onid;
704 ignore->tsid = er->tsid;
705 ignore->sid = er->srvid;
706 ignore->pid = er->pid;
707
708 if(ll_contains_data(ghttp_ignored_contexts, ignore, sizeof(s_ca_context)))
709 {
710 NULLFREE(ignore);
711 return true;
712 }
713 else
714 {
715 NULLFREE(ignore);
716 }
717 }
718 return false;
719}
720
721static int32_t ghttp_send_ecm(struct s_client *client, ECM_REQUEST *er)
722{
723 uint32_t hash;
724 s_ghttp *context = (s_ghttp *)client->ghttp;
725
726 if(_is_pid_ignored(er))
727 {
728 cs_log_dbg(D_CLIENT, "%s: ca context found in ignore list, ecm blocked: %x-%x-%x pid %x",
729 client->reader->label, er->onid, er->tsid, er->srvid, er->pid);
730 return -1;
731 }
732
733 if(!context->host_id) { context->host_id = (uint8_t *)cs_strdup(client->reader->device); }
734
735 ll_append(context->ecm_q, er);
736 if(ll_count(context->ecm_q) > 1)
737 {
738 cs_log_dbg(D_CLIENT, "%s: %d simultaneous ecms...", client->reader->label, ll_count(context->ecm_q));
739 }
740
741 if(_is_post_context(context->post_contexts, er, false))
742 {
743 _ghttp_post_ecmdata(client, er);
744 }
745 else
746 {
747 hash = javastring_hashcode(er->ecm + 3, er->ecmlen - 3);
748 _ghttp_http_get(client, hash, er->ecm[0] == 0x81);
749 }
750
751 return 0;
752}
753
754static void ghttp_cleanup(struct s_client *client)
755{
756 s_ghttp *context = (s_ghttp *)client->ghttp;
757
758 ll_destroy_data(&ghttp_ignored_contexts);
759
760 if(context)
761 {
762 NULLFREE(context->session_id);
763 NULLFREE(context->host_id);
764 NULLFREE(context->fallback_id);
765 ll_destroy(&context->ecm_q);
766 ll_destroy_data(&context->post_contexts);
767
768#ifdef WITH_SSL
769 if(context->ssl_handle)
770 {
771 SSL_shutdown(context->ssl_handle);
772 SSL_free(context->ssl_handle);
773 }
774 SSL_CTX_free(ghttp_ssl_context);
775#endif
776 NULLFREE(context);
777 }
778}
779
780#ifdef HAVE_DVBAPI
781static int32_t ghttp_capmt_notify(struct s_client *client, struct demux_s *demux)
782{
783 uint8_t req[640], lenhdr[64] = "";
784 uint8_t *pids = NULL;
785 uint8_t *end;
786 char *encauth = NULL;
787 int32_t ret;
788 int8_t i, pids_len = 0, offs = 0;
789 s_ghttp *context = (s_ghttp *)client->ghttp;
790
791 if(!context)
792 {
793 return -1;
794 }
795
796 cs_log_dbg(D_CLIENT, "%s: capmt %x-%x-%x %d pids on adapter %d mask %x dmx index %d",
797 client->reader->label, demux->onid, demux->tsid, demux->program_number, demux->ECMpidcount,
798 demux->adapter_index, demux->ca_mask, demux->demux_index);
799
800 if(demux->ECMpidcount > 0)
801 {
802 if(cs_malloc(&pids, demux->ECMpidcount * 8))
803 {
804 pids_len = demux->ECMpidcount * 8;
805
806 for(i = 0; i < demux->ECMpidcount; i++)
807 {
808 i2b_buf(2, demux->ECMpids[i].ECM_PID, pids + offs);
809 i2b_buf(2, demux->ECMpids[i].CAID, pids + (offs += 2));
810 i2b_buf(4, demux->ECMpids[i].PROVID, pids + (offs += 2));
811 offs += 4;
812 }
813 snprintf((char *)lenhdr, sizeof(lenhdr), "\r\nContent-Length: %d", pids_len);
814 }
815 else
816 {
817 return -1;
818 }
819 }
820
821 if(!context->host_id)
822 {
823 context->host_id = (uint8_t *)cs_strdup(client->reader->device);
824 }
825
826 encauth = _ghttp_basic_auth(client);
827
828 if(encauth) // basic auth login
829 {
830 ret = snprintf((char *)req, sizeof(req), "%s /api/p/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s\r\nAuthorization: Basic %s%s\r\n\r\n",
831 ((pids_len > 0) ? "POST" : "GET"), demux->onid, demux->tsid, demux->program_number,
832 demux->ECMpidcount, demux->ens, context->host_id, encauth, lenhdr);
833
834 NULLFREE(encauth);
835 }
836 else
837 {
838 if(context->session_id) // session exists
839 {
840 ret = snprintf((char *)req, sizeof(req), "%s /api/p/%s/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s%s\r\n\r\n",
841 ((pids_len > 0) ? "POST" : "GET"), context->session_id, demux->onid, demux->tsid,
842 demux->program_number, demux->ECMpidcount, demux->ens, context->host_id, lenhdr);
843 }
844 else // no credentials configured, assume no session required
845 {
846 ret = snprintf((char *)req, sizeof(req), "%s /api/p/%x/%x/%x/%x/%x HTTP/1.1\r\nHost: %s%s\r\n\r\n",
847 ((pids_len > 0) ? "POST" : "GET"), demux->onid, demux->tsid, demux->program_number,
848 demux->ECMpidcount, demux->ens, context->host_id, lenhdr);
849 }
850 }
851 end = req + ret;
852 if(pids_len > 0)
853 {
854 memcpy(end, pids, pids_len);
855 cs_log_dbg(D_CLIENT, "%s: new unscrambling detected, switching to post", client->reader->label);
856 _set_pid_status(context->post_contexts, demux->onid, demux->tsid, demux->program_number, 0);
857 }
858 cs_log_dump_dbg(D_CLIENT, pids, pids_len, "%s: sending capmt ecm pids - %s /api/p/%x/%x/%x/%x/%x",
859 client->reader->label, (pids_len > 0) ? "POST" : "GET", demux->onid, demux->tsid,
860 demux->program_number, demux->ECMpidcount, demux->ens);
861
862 ghttp_send(client, req, ret + pids_len);
863
864 if(pids_len > 0)
865 {
866 NULLFREE(pids);
867 }
868
869 return 0;
870}
871#endif
872
873void module_ghttp(struct s_module *ph)
874{
875 ph->ptab.nports = 0;
876 // ph->ptab.ports[0].s_port = cfg.ghttp_port;
877 ph->desc = "ghttp";
878 ph->type = MOD_CONN_TCP;
879 // ph->listenertype = LIS_GHTTP;
880 ph->large_ecm_support = 1;
881 ph->recv = ghttp_recv;
882 ph->c_init = ghttp_client_init;
883 ph->c_recv_chk = ghttp_recv_chk;
884 ph->c_send_ecm = ghttp_send_ecm;
885 ph->cleanup = ghttp_cleanup;
886#ifdef HAVE_DVBAPI
887 ph->c_capmt = ghttp_capmt_notify;
888#endif
889 ph->num = R_GHTTP;
890}
891#endif
Note: See TracBrowser for help on using the repository browser.