1 | #include "globals.h"
|
---|
2 | #ifdef MODULE_GHTTP
|
---|
3 | #include "oscam-client.h"
|
---|
4 | #include "oscam-net.h"
|
---|
5 | #include "oscam-string.h"
|
---|
6 | #include "oscam-reader.h"
|
---|
7 |
|
---|
8 | typedef struct
|
---|
9 | {
|
---|
10 | uint16_t prev_sid;
|
---|
11 | ECM_REQUEST last_ecm;
|
---|
12 | int do_post_next;
|
---|
13 | uchar* session_id;
|
---|
14 | } s_ghttp;
|
---|
15 |
|
---|
16 | static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST* er);
|
---|
17 |
|
---|
18 | int32_t ghttp_client_init(struct s_client *cl)
|
---|
19 | {
|
---|
20 | int32_t handle;
|
---|
21 |
|
---|
22 | cs_log("%s: init cache client %s:%d (fd=%d)", cl->reader->label, cl->reader->device, cl->reader->r_port, cl->udp_fd);
|
---|
23 |
|
---|
24 | handle = network_tcp_connection_open(cl->reader);
|
---|
25 | if(handle < 0) return -1;
|
---|
26 |
|
---|
27 | cl->reader->tcp_connected = 2;
|
---|
28 | cl->reader->card_status = CARD_INSERTED;
|
---|
29 | cl->reader->last_g = cl->reader->last_s = time((time_t *)0);
|
---|
30 |
|
---|
31 | cl->pfd = cl->udp_fd;
|
---|
32 |
|
---|
33 | if(!cl->ghttp) {
|
---|
34 | if(!cs_malloc(&(cl->ghttp), sizeof (s_ghttp)))
|
---|
35 | return -1;
|
---|
36 | }
|
---|
37 | memset(cl->ghttp, 0, sizeof (s_ghttp));
|
---|
38 |
|
---|
39 | cs_debug_mask(D_CLIENT, "%s: last_s=%ld, last_g=%ld", cl->reader->label, cl->reader->last_s, cl->reader->last_g);
|
---|
40 |
|
---|
41 | return 0;
|
---|
42 | }
|
---|
43 |
|
---|
44 | uint32_t javastring_hashcode(uchar* input, int32_t len)
|
---|
45 | {
|
---|
46 | uint32_t h = 0;
|
---|
47 | while(/**input &&*/ len--) {
|
---|
48 | h = 31 * h + *input++;
|
---|
49 | }
|
---|
50 | return h;
|
---|
51 | }
|
---|
52 |
|
---|
53 | static int32_t ghttp_send(struct s_client * client, uchar *buf, int32_t l)
|
---|
54 | {
|
---|
55 | cs_debug_mask(D_CLIENT, "%s: sending %d bytes", client->reader->label, l);
|
---|
56 | if(!client->pfd) {
|
---|
57 | //disconnected? try reinit.
|
---|
58 | cs_debug_mask(D_CLIENT, "%s: disconnected?", client->reader->label);
|
---|
59 | ghttp_client_init(client);
|
---|
60 | }
|
---|
61 | return send(client->pfd, buf, l, 0);
|
---|
62 | }
|
---|
63 |
|
---|
64 | static int32_t ghttp_recv(struct s_client *client, uchar *buf, int32_t l)
|
---|
65 | {
|
---|
66 | int32_t n = -1;
|
---|
67 | if(!client->pfd) return (-1);
|
---|
68 |
|
---|
69 | if((n = recv(client->pfd, buf, l, 0)) > 0) {
|
---|
70 | cs_debug_mask(D_CLIENT, "%s: received %d bytes from %s", client->reader->label, n, remote_txt());
|
---|
71 | client->last = time((time_t *)0);
|
---|
72 |
|
---|
73 | if(n > 300) return -1; //assumes google error, disconnects
|
---|
74 | }
|
---|
75 | return n;
|
---|
76 | }
|
---|
77 |
|
---|
78 | static int32_t ghttp_recv_chk(struct s_client *client, uchar *dcw, int32_t *rc, uchar *buf, int32_t UNUSED(n))
|
---|
79 | {
|
---|
80 | char* data;
|
---|
81 | char* lenstr;
|
---|
82 | int rcode, len = 0;
|
---|
83 | s_ghttp* context = (s_ghttp*)client->ghttp;
|
---|
84 | ECM_REQUEST *er = &context->last_ecm;
|
---|
85 |
|
---|
86 | data = strstr((char*)buf, "HTTP/1.1");
|
---|
87 | if(!data) {
|
---|
88 | cs_debug_mask(D_CLIENT, "%s: non http or otherwise corrupt response/disconnect: %s", client->reader->label, buf);
|
---|
89 | network_tcp_connection_close(client->reader, "receive error or idle timeout");
|
---|
90 | return -1;
|
---|
91 | }
|
---|
92 | data = data + strlen("HTTP/1.1 ");
|
---|
93 | rcode = atoi(data);
|
---|
94 | if(rcode < 200 || rcode > 204) {
|
---|
95 | cs_debug_mask(D_CLIENT, "%s: http error code %d", client->reader->label, rcode);
|
---|
96 | data = strstr(data, "Content-Type: application/octet-stream"); // if not octet-stream, google error. need reconnect?
|
---|
97 | if(data) // we have error info string in data
|
---|
98 | {
|
---|
99 | lenstr = strstr((char*)buf, "Content-Length: ");
|
---|
100 | if(lenstr) {
|
---|
101 | lenstr = lenstr + strlen("Content-Length: ");
|
---|
102 | len = atoi(lenstr);
|
---|
103 | }
|
---|
104 | data = strstr(data, "\r\n\r\n") + 4;
|
---|
105 | if(data) {
|
---|
106 | data[len] = '\0';
|
---|
107 | cs_debug_mask(D_CLIENT, "%s: http error message: %s", client->reader->label, data);
|
---|
108 | }
|
---|
109 | }
|
---|
110 | if(rcode == 503) {
|
---|
111 | context->prev_sid = 0;
|
---|
112 | if(context->do_post_next) {
|
---|
113 | cs_debug_mask(D_CLIENT, "%s: recv_chk got 503 despite post, trying reconnect", client->reader->label);
|
---|
114 | network_tcp_connection_close(client->reader, "timeout");
|
---|
115 | return -1;
|
---|
116 | } else {
|
---|
117 | // on 503 timeout, switch to POST
|
---|
118 | context->do_post_next = 1;
|
---|
119 | cs_debug_mask(D_CLIENT, "%s: recv_chk got 503, trying direct post", client->reader->label);
|
---|
120 | _ghttp_post_ecmdata(client, er);
|
---|
121 |
|
---|
122 | *rc = 0;
|
---|
123 | memset(dcw, 0, 16);
|
---|
124 | return -1;
|
---|
125 | }
|
---|
126 | } else if(rcode == 401) {
|
---|
127 | cs_debug_mask(D_CLIENT, "%s: session expired, trying direct post", client->reader->label);
|
---|
128 | context->do_post_next = 1;
|
---|
129 | NULLFREE(context->session_id);
|
---|
130 | _ghttp_post_ecmdata(client, er);
|
---|
131 |
|
---|
132 | *rc = 0;
|
---|
133 | memset(dcw, 0, 16);
|
---|
134 | return -1;
|
---|
135 | }
|
---|
136 | return -1;
|
---|
137 | }
|
---|
138 |
|
---|
139 | // switch back to cache get after rapid ecm response (arbitrary atm), only effect is a slight bw save for client
|
---|
140 | if(context->do_post_next && context->last_ecm.srvid == context->prev_sid) {
|
---|
141 | if(client->cwlastresptime > 0 && client->cwlastresptime < 800) {
|
---|
142 | cs_debug_mask(D_CLIENT, "%s: prev resp time for same sid was %d ms, switching back to cache get for next req", client->reader->label, client->cwlastresptime);
|
---|
143 | context->do_post_next = 0;
|
---|
144 | }
|
---|
145 | }
|
---|
146 |
|
---|
147 | data = strstr((char*)buf, "Set-Cookie: GSSID=");
|
---|
148 | if(data) {
|
---|
149 | data += strlen("Set-Cookie: GSSID=");
|
---|
150 | NULLFREE(context->session_id);
|
---|
151 | if(cs_malloc(&context->session_id, 7)) { // todo dont assume session id of length 6
|
---|
152 | strncpy((char*)context->session_id, data, 6);
|
---|
153 | context->session_id[6] = '\0';
|
---|
154 | cs_debug_mask(D_CLIENT, "%s: set session_id to: %s", client->reader->label, context->session_id);
|
---|
155 | }
|
---|
156 | }
|
---|
157 |
|
---|
158 | data = strstr((char*)buf, "Content-Length: 16");
|
---|
159 | if(data) {
|
---|
160 | data = strstr((char*)buf, "\r\n\r\n");
|
---|
161 | data += 4;
|
---|
162 | memcpy(dcw, data, 16);
|
---|
163 | *rc = 1;
|
---|
164 | char tmp_dbg[33];
|
---|
165 | cs_debug_mask(D_CLIENT, "%s: recv chk - %s", client->reader->label, cs_hexdump(0, dcw, 16, tmp_dbg, sizeof (tmp_dbg)));
|
---|
166 | return client->reader->msg_idx;
|
---|
167 | } else {
|
---|
168 | cs_debug_mask(D_CLIENT, "%s: recv_chk fail!", client->reader->label);
|
---|
169 | }
|
---|
170 | return -1;
|
---|
171 | }
|
---|
172 |
|
---|
173 | static int32_t _ghttp_http_get(struct s_client *client, uint32_t hash, int odd)
|
---|
174 | {
|
---|
175 | uchar req[128], auth[64];
|
---|
176 | char* encauth = NULL;
|
---|
177 | int32_t ret;
|
---|
178 | s_ghttp* context = (s_ghttp*)client->ghttp;
|
---|
179 |
|
---|
180 | if(!context->session_id && strlen(client->reader->r_usr) > 0) {
|
---|
181 | cs_debug_mask(D_CLIENT, "%s: username specified and no existing session, adding basic auth", client->reader->label);
|
---|
182 | ret = snprintf((char*)auth, sizeof(auth), "%s:%s", client->reader->r_usr, client->reader->r_pwd);
|
---|
183 | ret = b64encode((char*)auth, ret, &encauth);
|
---|
184 | }
|
---|
185 |
|
---|
186 | if(encauth) { // basic auth login
|
---|
187 | 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", odd ? 81 : 80, hash, client->reader->device, encauth);
|
---|
188 | } else {
|
---|
189 | if(context->session_id) { // session exists
|
---|
190 | ret = snprintf((char*)req, sizeof(req), "GET /api/c/%s/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n", context->session_id, odd ? 81 : 80, hash, client->reader->device);
|
---|
191 | } else { // no credentials configured, assume no session required
|
---|
192 | ret = snprintf((char*)req, sizeof(req), "GET /api/c/%d/%x HTTP/1.1\r\nHost: %s\r\n\r\n", odd ? 81 : 80, hash, client->reader->device);
|
---|
193 | }
|
---|
194 | }
|
---|
195 |
|
---|
196 | ret = ghttp_send(client, req, ret);
|
---|
197 |
|
---|
198 | return ret;
|
---|
199 | }
|
---|
200 |
|
---|
201 | static int32_t _ghttp_post_ecmdata(struct s_client *client, ECM_REQUEST* er)
|
---|
202 | {
|
---|
203 | uchar req[640], auth[64];
|
---|
204 | uchar* end;
|
---|
205 | char* encauth = NULL;
|
---|
206 | int32_t ret;
|
---|
207 | s_ghttp* context = (s_ghttp*)client->ghttp;
|
---|
208 |
|
---|
209 | if(!context->session_id && strlen(client->reader->r_usr) > 0) {
|
---|
210 | cs_debug_mask(D_CLIENT, "%s: username specified and no existing session, adding basic auth", client->reader->label);
|
---|
211 | ret = snprintf((char*)auth, sizeof(auth), "%s:%s", client->reader->r_usr, client->reader->r_pwd);
|
---|
212 | ret = b64encode((char*)auth, ret, &encauth);
|
---|
213 | }
|
---|
214 |
|
---|
215 | if(encauth) { // basic auth login
|
---|
216 | 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", er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, client->reader->device, encauth, er->ecmlen);
|
---|
217 | } else {
|
---|
218 | if(context->session_id) { // session exists
|
---|
219 | 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", context->session_id, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, client->reader->device, er->ecmlen);
|
---|
220 | } else { // no credentials configured, assume no session required
|
---|
221 | 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", er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid, client->reader->device, er->ecmlen);
|
---|
222 | }
|
---|
223 | }
|
---|
224 | end = req + ret;
|
---|
225 | memcpy(end, er->ecm, er->ecmlen);
|
---|
226 |
|
---|
227 | cs_debug_mask(D_CLIENT, "%s: sending full ecm - /api/e/%x/%x/%x/%x/%x/%x", client->reader->label, er->onid, er->tsid, er->pid, er->srvid, er->caid, er->prid);
|
---|
228 |
|
---|
229 | ret = ghttp_send(client, req, ret + er->ecmlen);
|
---|
230 |
|
---|
231 | return ret;
|
---|
232 | }
|
---|
233 |
|
---|
234 | static int32_t ghttp_send_ecm(struct s_client *client, ECM_REQUEST *er, uchar *UNUSED(buf))
|
---|
235 | {
|
---|
236 | uint32_t hash;
|
---|
237 | // int32_t ret;
|
---|
238 | s_ghttp* context = (s_ghttp*)client->ghttp;
|
---|
239 |
|
---|
240 | context->prev_sid = context->last_ecm.srvid;
|
---|
241 | if(context->prev_sid != er->srvid) {
|
---|
242 | cs_debug_mask(D_CLIENT, "%s: zap detected? prev %x, current %x", client->reader->label, context->prev_sid, er->srvid);
|
---|
243 | context->do_post_next = 1;
|
---|
244 | }
|
---|
245 |
|
---|
246 | client->reader->msg_idx = er->idx;
|
---|
247 | if(context->do_post_next) {
|
---|
248 | _ghttp_post_ecmdata(client, er);
|
---|
249 | } else {
|
---|
250 | hash = javastring_hashcode(er->ecm + 3, er->ecmlen - 3);
|
---|
251 | _ghttp_http_get(client, hash, er->ecm[0] == 0x81);
|
---|
252 | }
|
---|
253 |
|
---|
254 | context->last_ecm = *er; //struct copy
|
---|
255 |
|
---|
256 | return 0;
|
---|
257 | }
|
---|
258 |
|
---|
259 | void module_ghttp(struct s_module *ph)
|
---|
260 | {
|
---|
261 | static PTAB ptab;
|
---|
262 | // ptab.ports[0].s_port = cfg.ghttp_port;
|
---|
263 | ph->ptab = &ptab;
|
---|
264 | ph->ptab->nports = 0;
|
---|
265 |
|
---|
266 | ph->desc = "ghttp";
|
---|
267 | ph->type = MOD_CONN_TCP;
|
---|
268 | // ph->listenertype = LIS_GHTTP;
|
---|
269 | ph->recv = ghttp_recv;
|
---|
270 | ph->c_init = ghttp_client_init;
|
---|
271 | ph->c_recv_chk = ghttp_recv_chk;
|
---|
272 | ph->c_send_ecm = ghttp_send_ecm;
|
---|
273 | ph->num = R_GHTTP;
|
---|
274 | }
|
---|
275 | #endif
|
---|