1 | #define MODULE_LOG_PREFIX "net"
|
---|
2 |
|
---|
3 | #include "globals.h"
|
---|
4 | #include "oscam-client.h"
|
---|
5 | #include "oscam-failban.h"
|
---|
6 | #include "oscam-lock.h"
|
---|
7 | #include "oscam-net.h"
|
---|
8 | #include "oscam-string.h"
|
---|
9 | #include "oscam-time.h"
|
---|
10 | #include "oscam-work.h"
|
---|
11 |
|
---|
12 | extern CS_MUTEX_LOCK gethostbyname_lock;
|
---|
13 | extern int32_t exit_oscam;
|
---|
14 |
|
---|
15 | #ifndef IPV6SUPPORT
|
---|
16 | static int32_t inet_byteorder = 0;
|
---|
17 |
|
---|
18 | static in_addr_t cs_inet_order(in_addr_t n)
|
---|
19 | {
|
---|
20 | if(!inet_byteorder)
|
---|
21 | { inet_byteorder = (inet_addr("1.2.3.4") + 1 == inet_addr("1.2.3.5")) ? 1 : 2; }
|
---|
22 | switch(inet_byteorder)
|
---|
23 | {
|
---|
24 | case 1:
|
---|
25 | break;
|
---|
26 | case 2:
|
---|
27 | n = ((n & 0xff000000) >> 24) |
|
---|
28 | ((n & 0x00ff0000) >> 8) |
|
---|
29 | ((n & 0x0000ff00) << 8) |
|
---|
30 | ((n & 0x000000ff) << 24);
|
---|
31 | break;
|
---|
32 | }
|
---|
33 | return n;
|
---|
34 | }
|
---|
35 | #endif
|
---|
36 |
|
---|
37 | char *cs_inet_ntoa(IN_ADDR_T addr)
|
---|
38 | {
|
---|
39 | #ifdef IPV6SUPPORT
|
---|
40 | static char buff[INET6_ADDRSTRLEN];
|
---|
41 | if(IN6_IS_ADDR_V4MAPPED(&addr) || IN6_IS_ADDR_V4COMPAT(&addr))
|
---|
42 | {
|
---|
43 | snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
|
---|
44 | addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15]);
|
---|
45 | }
|
---|
46 | else
|
---|
47 | {
|
---|
48 | inet_ntop(AF_INET6, &(addr.s6_addr), buff, INET6_ADDRSTRLEN);
|
---|
49 | }
|
---|
50 | return buff;
|
---|
51 | #else
|
---|
52 | struct in_addr in;
|
---|
53 | in.s_addr = addr;
|
---|
54 | return (char *)inet_ntoa(in);
|
---|
55 | #endif
|
---|
56 | }
|
---|
57 |
|
---|
58 | void cs_inet_addr(char *txt, IN_ADDR_T *out)
|
---|
59 | {
|
---|
60 | #ifdef IPV6SUPPORT
|
---|
61 | char buff[INET6_ADDRSTRLEN];
|
---|
62 | //trying as IPv6 address
|
---|
63 | if(inet_pton(AF_INET6, txt, out->s6_addr) == 0)
|
---|
64 | {
|
---|
65 | //now trying as mapped IPv4
|
---|
66 | snprintf(buff, sizeof(buff), "::ffff:%s", txt);
|
---|
67 | inet_pton(AF_INET6, buff, out->s6_addr);
|
---|
68 | }
|
---|
69 | #else
|
---|
70 | *out = inet_addr(txt);
|
---|
71 | #endif
|
---|
72 | }
|
---|
73 |
|
---|
74 | void cs_resolve(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len)
|
---|
75 | {
|
---|
76 | #ifdef IPV6SUPPORT
|
---|
77 | cs_getIPv6fromHost(hostname, ip, sock, sa_len);
|
---|
78 | #else
|
---|
79 | *ip = cs_getIPfromHost(hostname);
|
---|
80 | if(sa_len)
|
---|
81 | { *sa_len = sizeof(*sock); }
|
---|
82 | #endif
|
---|
83 | }
|
---|
84 |
|
---|
85 | #ifdef IPV6SUPPORT
|
---|
86 | int32_t cs_in6addr_equal(struct in6_addr *a1, struct in6_addr *a2)
|
---|
87 | {
|
---|
88 | return memcmp(a1, a2, 16) == 0;
|
---|
89 | }
|
---|
90 |
|
---|
91 | int32_t cs_in6addr_lt(struct in6_addr *a, struct in6_addr *b)
|
---|
92 | {
|
---|
93 | int i;
|
---|
94 | for(i = 0; i < 4; i++)
|
---|
95 | {
|
---|
96 | if((i == 2) && ((IN6_IS_ADDR_V4COMPAT(a) && IN6_IS_ADDR_V4MAPPED(b)) ||
|
---|
97 | (IN6_IS_ADDR_V4COMPAT(b) && IN6_IS_ADDR_V4MAPPED(a))))
|
---|
98 | { continue; } // skip comparing this part
|
---|
99 |
|
---|
100 | if(a->s6_addr32[i] != b->s6_addr32[i])
|
---|
101 | { return ntohl(a->s6_addr32[i]) < ntohl(b->s6_addr32[i]); }
|
---|
102 | }
|
---|
103 |
|
---|
104 | return 0;
|
---|
105 | }
|
---|
106 |
|
---|
107 | int32_t cs_in6addr_isnull(struct in6_addr *addr)
|
---|
108 | {
|
---|
109 | int i;
|
---|
110 | for(i = 0; i < 16; i++)
|
---|
111 | if(addr->s6_addr[i])
|
---|
112 | { return 0; }
|
---|
113 | return 1;
|
---|
114 | }
|
---|
115 |
|
---|
116 | void cs_in6addr_copy(struct in6_addr *dst, struct in6_addr *src)
|
---|
117 | {
|
---|
118 | memcpy(dst, src, 16);
|
---|
119 | }
|
---|
120 |
|
---|
121 | void cs_in6addr_ipv4map(struct in6_addr *dst, in_addr_t src)
|
---|
122 | {
|
---|
123 | memset(dst->s6_addr, 0, 16);
|
---|
124 | dst->s6_addr[10] = 0xff;
|
---|
125 | dst->s6_addr[11] = 0xff;
|
---|
126 | memcpy(dst->s6_addr + 12, &src, 4);
|
---|
127 | }
|
---|
128 | #endif
|
---|
129 |
|
---|
130 | IN_ADDR_T get_null_ip(void)
|
---|
131 | {
|
---|
132 | IN_ADDR_T ip;
|
---|
133 | #ifdef IPV6SUPPORT
|
---|
134 | cs_inet_addr("::", &ip);
|
---|
135 | #else
|
---|
136 | ip = 0;
|
---|
137 | #endif
|
---|
138 | return ip;
|
---|
139 | }
|
---|
140 |
|
---|
141 | void set_null_ip(IN_ADDR_T *ip)
|
---|
142 | {
|
---|
143 | #ifdef IPV6SUPPORT
|
---|
144 | cs_inet_addr("::", ip);
|
---|
145 | #else
|
---|
146 | *ip = 0;
|
---|
147 | #endif
|
---|
148 | }
|
---|
149 |
|
---|
150 | void set_localhost_ip(IN_ADDR_T *ip)
|
---|
151 | {
|
---|
152 | #ifdef IPV6SUPPORT
|
---|
153 | cs_inet_addr("::1", ip);
|
---|
154 | #else
|
---|
155 | cs_inet_addr("127.0.0.1", ip);
|
---|
156 | #endif
|
---|
157 | }
|
---|
158 |
|
---|
159 | int32_t check_ip(struct s_ip *ip, IN_ADDR_T n)
|
---|
160 | {
|
---|
161 | struct s_ip *p_ip;
|
---|
162 | int32_t ok = 0;
|
---|
163 | #ifdef IPV6SUPPORT
|
---|
164 | for(p_ip = ip; (p_ip) && (!ok); p_ip = p_ip->next)
|
---|
165 | {
|
---|
166 | ok = cs_in6addr_lt(&n, &p_ip->ip[0]);
|
---|
167 | ok |= cs_in6addr_lt(&p_ip->ip[1], &n);
|
---|
168 | ok = !ok;
|
---|
169 | }
|
---|
170 | #else
|
---|
171 | for(p_ip = ip; (p_ip) && (!ok); p_ip = p_ip->next)
|
---|
172 | { ok = ((cs_inet_order(n) >= cs_inet_order(p_ip->ip[0])) && (cs_inet_order(n) <= cs_inet_order(p_ip->ip[1]))); }
|
---|
173 | #endif
|
---|
174 | return ok;
|
---|
175 | }
|
---|
176 |
|
---|
177 | /* Returns the ip from the given hostname. If gethostbyname is configured in the config file, a lock
|
---|
178 | will be held until the ip has been resolved. */
|
---|
179 | uint32_t cs_getIPfromHost(const char *hostname)
|
---|
180 | {
|
---|
181 | uint32_t result = 0;
|
---|
182 | // Resolve with gethostbyname:
|
---|
183 | if(cfg.resolve_gethostbyname)
|
---|
184 | {
|
---|
185 | cs_writelock(__func__, &gethostbyname_lock);
|
---|
186 | struct hostent *rht = gethostbyname(hostname);
|
---|
187 | if(!rht)
|
---|
188 | { cs_log("can't resolve %s", hostname); }
|
---|
189 | else
|
---|
190 | { result = ((struct in_addr *)rht->h_addr)->s_addr; }
|
---|
191 | cs_writeunlock(__func__, &gethostbyname_lock);
|
---|
192 | }
|
---|
193 | else // Resolve with getaddrinfo:
|
---|
194 | {
|
---|
195 | struct addrinfo hints, *res = NULL;
|
---|
196 | memset(&hints, 0, sizeof(hints));
|
---|
197 | hints.ai_socktype = SOCK_STREAM;
|
---|
198 | hints.ai_family = AF_INET;
|
---|
199 | hints.ai_protocol = IPPROTO_TCP;
|
---|
200 |
|
---|
201 | int32_t err = getaddrinfo(hostname, NULL, &hints, &res);
|
---|
202 | if(err != 0 || !res || !res->ai_addr)
|
---|
203 | {
|
---|
204 | cs_log("can't resolve %s, error: %s", hostname, err ? gai_strerror(err) : "unknown");
|
---|
205 | }
|
---|
206 | else
|
---|
207 | {
|
---|
208 | result = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
|
---|
209 | }
|
---|
210 | if(res) { freeaddrinfo(res); }
|
---|
211 | }
|
---|
212 | return result;
|
---|
213 | }
|
---|
214 |
|
---|
215 | #ifdef IPV6SUPPORT
|
---|
216 | void cs_getIPv6fromHost(const char *hostname, struct in6_addr *addr, struct sockaddr_storage *sa, socklen_t *sa_len)
|
---|
217 | {
|
---|
218 | uint32_t ipv4addr = 0;
|
---|
219 | struct addrinfo hints, *res = NULL;
|
---|
220 | memset(&hints, 0, sizeof(hints));
|
---|
221 | hints.ai_socktype = SOCK_STREAM;
|
---|
222 | hints.ai_family = AF_UNSPEC;
|
---|
223 | hints.ai_protocol = IPPROTO_TCP;
|
---|
224 | int32_t err = getaddrinfo(hostname, NULL, &hints, &res);
|
---|
225 | if(err != 0 || !res || !res->ai_addr)
|
---|
226 | {
|
---|
227 | cs_log("can't resolve %s, error: %s", hostname, err ? gai_strerror(err) : "unknown");
|
---|
228 | }
|
---|
229 | else
|
---|
230 | {
|
---|
231 | ipv4addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
|
---|
232 | if(res->ai_family == AF_INET)
|
---|
233 | { cs_in6addr_ipv4map(addr, ipv4addr); }
|
---|
234 | else
|
---|
235 | { IP_ASSIGN(*addr, SIN_GET_ADDR(*res->ai_addr)); }
|
---|
236 | if(sa)
|
---|
237 | { memcpy(sa, res->ai_addr, res->ai_addrlen); }
|
---|
238 | if(sa_len)
|
---|
239 | { *sa_len = res->ai_addrlen; }
|
---|
240 | }
|
---|
241 | if(res)
|
---|
242 | { freeaddrinfo(res); }
|
---|
243 | }
|
---|
244 | #endif
|
---|
245 |
|
---|
246 | int set_socket_priority(int fd, int priority)
|
---|
247 | {
|
---|
248 | #if defined(IP_TOS) || defined(SO_PRIORITY)
|
---|
249 | if (priority == 0) { return -1; } // default value, therefore leave it untouched (IPP=0; DSCP=CS0)
|
---|
250 |
|
---|
251 | int ret = 0;
|
---|
252 | int cos __attribute__ ((unused)) = 0;
|
---|
253 | int tos __attribute__ ((unused)) = 0x00;
|
---|
254 |
|
---|
255 | switch(priority)
|
---|
256 | {
|
---|
257 | case 1: // IPP=1; DSCP=CS1
|
---|
258 | cos = 1;
|
---|
259 | tos = 0x20;
|
---|
260 | break;
|
---|
261 |
|
---|
262 | case 2: // IPP=1; DSCP=AF11
|
---|
263 | cos = 1;
|
---|
264 | tos = 0x28;
|
---|
265 | break;
|
---|
266 |
|
---|
267 | case 3: // IPP=1; DSCP=AF12
|
---|
268 | cos = 1;
|
---|
269 | tos = 0x30;
|
---|
270 | break;
|
---|
271 |
|
---|
272 | case 4: // IPP=1; DSCP=AF13
|
---|
273 | cos = 1;
|
---|
274 | tos = 0x38;
|
---|
275 | break;
|
---|
276 |
|
---|
277 | case 5: // IPP=2; DSCP=CS2
|
---|
278 | cos = 2;
|
---|
279 | tos = 0x40;
|
---|
280 | break;
|
---|
281 |
|
---|
282 | case 6: // IPP=2; DSCP=AF21
|
---|
283 | cos = 2;
|
---|
284 | tos = 0x48;
|
---|
285 | break;
|
---|
286 |
|
---|
287 | case 7: // IPP=2; DSCP=AF22
|
---|
288 | cos = 2;
|
---|
289 | tos = 0x50;
|
---|
290 | break;
|
---|
291 |
|
---|
292 | case 8: // IPP=2; DSCP=AF23
|
---|
293 | cos = 2;
|
---|
294 | tos = 0x58;
|
---|
295 | break;
|
---|
296 |
|
---|
297 | case 9: // IPP=3; DSCP=CS3
|
---|
298 | cos = 3;
|
---|
299 | tos = 0x60;
|
---|
300 | break;
|
---|
301 |
|
---|
302 | case 10: // IPP=3; DSCP=AF31
|
---|
303 | cos = 3;
|
---|
304 | tos = 0x68;
|
---|
305 | break;
|
---|
306 |
|
---|
307 | case 11: // IPP=3; DSCP=AF32
|
---|
308 | cos = 3;
|
---|
309 | tos = 0x70;
|
---|
310 | break;
|
---|
311 |
|
---|
312 | case 12: // IPP=3; DSCP=AF33
|
---|
313 | cos = 3;
|
---|
314 | tos = 0x78;
|
---|
315 | break;
|
---|
316 |
|
---|
317 | case 13: // IPP=4; DSCP=CS4
|
---|
318 | cos = 4;
|
---|
319 | tos = 0x80;
|
---|
320 | break;
|
---|
321 |
|
---|
322 | case 14: // IPP=4; DSCP=AF41
|
---|
323 | cos = 4;
|
---|
324 | tos = 0x88;
|
---|
325 | break;
|
---|
326 |
|
---|
327 | case 15: // IPP=4; DSCP=AF42
|
---|
328 | cos = 4;
|
---|
329 | tos = 0x90;
|
---|
330 | break;
|
---|
331 |
|
---|
332 | case 16: // IPP=4; DSCP=AF43
|
---|
333 | cos = 4;
|
---|
334 | tos = 0x98;
|
---|
335 | break;
|
---|
336 |
|
---|
337 | case 17: // IPP=5; DSCP=CS5
|
---|
338 | cos = 5;
|
---|
339 | tos = 0xa0;
|
---|
340 | break;
|
---|
341 |
|
---|
342 | case 18: // IPP=5; DSCP=EF
|
---|
343 | cos = 5;
|
---|
344 | tos = 0xb8;
|
---|
345 | break;
|
---|
346 |
|
---|
347 | case 19: // IPP=6; DSCP=CS6
|
---|
348 | cos = 6;
|
---|
349 | tos = 0xc0;
|
---|
350 | break;
|
---|
351 |
|
---|
352 | case 20: // IPP=7; DSCP=CS7
|
---|
353 | cos = 7;
|
---|
354 | tos = 0xe0;
|
---|
355 | break;
|
---|
356 | }
|
---|
357 |
|
---|
358 | #ifdef IP_TOS
|
---|
359 | if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) < 0)
|
---|
360 | {
|
---|
361 | cs_log("Setting IP_TOS failed, errno=%d, %s", errno, strerror(errno));
|
---|
362 | }
|
---|
363 | else
|
---|
364 | {
|
---|
365 | ret = ret ^ 0x01;
|
---|
366 | }
|
---|
367 |
|
---|
368 | #if defined(IPV6SUPPORT) && defined(IPV6_TCLASS)
|
---|
369 | if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, (void *)&tos, sizeof(tos)) < 0)
|
---|
370 | {
|
---|
371 | cs_log("Setting IPV6_TCLASS failed, errno=%d, %s", errno, strerror(errno));
|
---|
372 | }
|
---|
373 | else
|
---|
374 | {
|
---|
375 | ret = ret ^ 0x02;
|
---|
376 | }
|
---|
377 | #endif
|
---|
378 | #endif
|
---|
379 |
|
---|
380 | #ifdef SO_PRIORITY
|
---|
381 | if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void *)&cos, sizeof(cos)) < 0)
|
---|
382 | {
|
---|
383 | cs_log("Setting SO_PRIORITY failed, errno=%d, %s", errno, strerror(errno));
|
---|
384 | }
|
---|
385 | else
|
---|
386 | {
|
---|
387 | ret = ret ^ 0x04;
|
---|
388 | }
|
---|
389 | #endif
|
---|
390 |
|
---|
391 | return ret;
|
---|
392 | #else
|
---|
393 | (void)fd;
|
---|
394 | (void)priority;
|
---|
395 | return -1;
|
---|
396 | #endif
|
---|
397 | }
|
---|
398 |
|
---|
399 | void setTCPTimeouts(int32_t sock)
|
---|
400 | {
|
---|
401 | int32_t flag = 1;
|
---|
402 | // this is not only for a real keepalive but also to detect closed connections so it should not be configurable
|
---|
403 | if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) && errno != EBADF)
|
---|
404 | {
|
---|
405 | cs_log("Setting SO_KEEPALIVE failed, errno=%d, %s", errno, strerror(errno));
|
---|
406 | }
|
---|
407 |
|
---|
408 | #if defined(TCP_KEEPIDLE) && defined(TCP_KEEPCNT) && defined(TCP_KEEPINTVL)
|
---|
409 | flag = 10;
|
---|
410 |
|
---|
411 | if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &flag, sizeof(flag)) && errno != EBADF) // send first keepalive packet after 10 seconds of last package received (keepalive packets included)
|
---|
412 | {
|
---|
413 | cs_log("Setting TCP_KEEPIDLE failed, errno=%d, %s", errno, strerror(errno));
|
---|
414 | }
|
---|
415 |
|
---|
416 | flag = 3;
|
---|
417 |
|
---|
418 | if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &flag, sizeof(flag)) && errno != EBADF) // send up to 3 keepalive packets out (in interval TCP_KEEPINTVL), then disconnect if no response
|
---|
419 | {
|
---|
420 | cs_log("Setting TCP_KEEPCNT failed, errno=%d, %s", errno, strerror(errno));
|
---|
421 | }
|
---|
422 |
|
---|
423 | flag = 1;
|
---|
424 |
|
---|
425 | if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &flag, sizeof(flag)) && errno != EBADF)
|
---|
426 | {
|
---|
427 | ; // send a keepalive packet out every second (until answer has been received or TCP_KEEPCNT has been reached)
|
---|
428 | cs_log("Setting TCP_KEEPINTVL failed, errno=%d, %s", errno, strerror(errno));
|
---|
429 | }
|
---|
430 | #endif
|
---|
431 |
|
---|
432 | struct timeval tv;
|
---|
433 | tv.tv_sec = 60;
|
---|
434 | tv.tv_usec = 0;
|
---|
435 |
|
---|
436 | if(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval)) && errno != EBADF)
|
---|
437 | {
|
---|
438 | ;
|
---|
439 | cs_log("Setting SO_SNDTIMEO failed, errno=%d, %s", errno, strerror(errno));
|
---|
440 | }
|
---|
441 |
|
---|
442 | tv.tv_sec = 600;
|
---|
443 | tv.tv_usec = 0;
|
---|
444 |
|
---|
445 | if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)) && errno != EBADF)
|
---|
446 | {
|
---|
447 | ;
|
---|
448 | cs_log("Setting SO_RCVTIMEO failed, errno=%d, %s", errno, strerror(errno));
|
---|
449 | }
|
---|
450 |
|
---|
451 | #if defined(TCP_USER_TIMEOUT)
|
---|
452 | int timeout = 60000; // RFC 5482 user timeout in milliseconds
|
---|
453 | setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, (char *) &timeout, sizeof(timeout));
|
---|
454 | #endif
|
---|
455 | }
|
---|
456 |
|
---|
457 | int set_nonblock(int32_t fd, bool nonblock)
|
---|
458 | {
|
---|
459 | int32_t flags = fcntl(fd, F_GETFL);
|
---|
460 | if (flags == -1)
|
---|
461 | return -1;
|
---|
462 | if (nonblock)
|
---|
463 | flags |= O_NONBLOCK;
|
---|
464 | else
|
---|
465 | flags &= (~O_NONBLOCK);
|
---|
466 | return fcntl(fd, F_SETFL, flags);
|
---|
467 | }
|
---|
468 |
|
---|
469 | int8_t check_fd_for_data(int32_t fd)
|
---|
470 | {
|
---|
471 | int32_t rc;
|
---|
472 | struct pollfd pfd[1];
|
---|
473 |
|
---|
474 | pfd[0].fd = fd;
|
---|
475 | pfd[0].events = (POLLIN | POLLPRI);
|
---|
476 | rc = poll(pfd, 1, 0);
|
---|
477 |
|
---|
478 | if(rc == -1)
|
---|
479 | { cs_log("check_fd_for_data(fd=%d) failed: (errno=%d %s)", fd, errno, strerror(errno)); }
|
---|
480 |
|
---|
481 | if(rc == -1 || rc == 0)
|
---|
482 | { return rc; }
|
---|
483 |
|
---|
484 | if(pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR))
|
---|
485 | { return -2; }
|
---|
486 |
|
---|
487 | return 1;
|
---|
488 | }
|
---|
489 |
|
---|
490 | int32_t recv_from_udpipe(uint8_t *buf)
|
---|
491 | {
|
---|
492 | uint16_t n;
|
---|
493 | if(buf[0] != 'U')
|
---|
494 | {
|
---|
495 | cs_log("INTERNAL PIPE-ERROR");
|
---|
496 | cs_exit(1);
|
---|
497 | }
|
---|
498 | memcpy(&n, buf + 1, 2);
|
---|
499 | memmove(buf, buf + 3, n);
|
---|
500 | return n;
|
---|
501 | }
|
---|
502 |
|
---|
503 | int32_t process_input(uint8_t *buf, int32_t buflen, int32_t timeout)
|
---|
504 | {
|
---|
505 | int32_t rc, i, pfdcount;
|
---|
506 | int64_t polltime, timeoutms;
|
---|
507 | struct pollfd pfd[2];
|
---|
508 | struct s_client *cl = cur_client();
|
---|
509 |
|
---|
510 | struct timeb starttime;
|
---|
511 | struct timeb currenttime;
|
---|
512 | timeoutms = 1000 * timeout;
|
---|
513 | cs_ftime(&starttime);
|
---|
514 | polltime = timeoutms; // initial polltime = timeoutms
|
---|
515 | while(1)
|
---|
516 | {
|
---|
517 | pfdcount = 0;
|
---|
518 | if(cl->pfd)
|
---|
519 | {
|
---|
520 | pfd[pfdcount].fd = cl->pfd;
|
---|
521 | pfd[pfdcount++].events = POLLIN | POLLPRI;
|
---|
522 | }
|
---|
523 | int32_t p_rc = poll(pfd, pfdcount, polltime);
|
---|
524 |
|
---|
525 | cs_ftime(¤ttime);
|
---|
526 | int64_t gone = comp_timeb(¤ttime, &starttime);
|
---|
527 | polltime = timeoutms - gone; // calculate polltime left
|
---|
528 | if(polltime < 0)
|
---|
529 | {
|
---|
530 | polltime = 0;
|
---|
531 | }
|
---|
532 | if(p_rc < 0)
|
---|
533 | {
|
---|
534 | if(errno == EINTR)
|
---|
535 | { continue; }
|
---|
536 | else
|
---|
537 | { return 0; }
|
---|
538 | }
|
---|
539 |
|
---|
540 | if((p_rc == 0) && (timeout != 0) && (gone >= timeoutms)) // client maxidle reached? timeout = 0, idle disconnect disabled
|
---|
541 | {
|
---|
542 | rc = -9;
|
---|
543 | break;
|
---|
544 | }
|
---|
545 |
|
---|
546 | for(i = 0; i < pfdcount && p_rc > 0; i++)
|
---|
547 | {
|
---|
548 | if(pfd[i].revents & POLLHUP) // POLLHUP is only valid in revents so it doesn't need to be set above in events
|
---|
549 | {
|
---|
550 | return 0;
|
---|
551 | }
|
---|
552 |
|
---|
553 | if(!(pfd[i].revents & (POLLIN | POLLPRI)))
|
---|
554 | { continue; }
|
---|
555 |
|
---|
556 | if(pfd[i].fd == cl->pfd)
|
---|
557 | { return get_module(cl)->recv(cl, buf, buflen); }
|
---|
558 | }
|
---|
559 | }
|
---|
560 | return rc;
|
---|
561 | }
|
---|
562 |
|
---|
563 | static struct s_client *find_client_by_ip(IN_ADDR_T ip, in_port_t port)
|
---|
564 | {
|
---|
565 | struct s_client *cl;
|
---|
566 | for(cl = first_client; cl; cl = cl->next)
|
---|
567 | {
|
---|
568 | if(!cl->kill && IP_EQUAL(cl->ip, ip) && cl->port == port && (cl->typ == 'c' || cl->typ == 'm'))
|
---|
569 | {
|
---|
570 | return cl;
|
---|
571 | }
|
---|
572 | }
|
---|
573 | return NULL;
|
---|
574 | }
|
---|
575 |
|
---|
576 | int32_t accept_connection(struct s_module *module, int8_t module_idx, int8_t port_idx)
|
---|
577 | {
|
---|
578 | struct SOCKADDR cad;
|
---|
579 | int32_t scad = sizeof(cad), n;
|
---|
580 | struct s_client *cl;
|
---|
581 | struct s_port *port = &module->ptab.ports[port_idx];
|
---|
582 |
|
---|
583 | memset(&cad, 0, sizeof(struct SOCKADDR));
|
---|
584 |
|
---|
585 | if(module->type == MOD_CONN_UDP)
|
---|
586 | {
|
---|
587 | uint8_t *buf;
|
---|
588 | if(!cs_malloc(&buf, 1024))
|
---|
589 | { return -1; }
|
---|
590 |
|
---|
591 | if((n = recvfrom(port->fd, buf + 3, 1024 - 3, 0, (struct sockaddr *)&cad, (socklen_t *)&scad)) > 0)
|
---|
592 | {
|
---|
593 | uint16_t rl;
|
---|
594 | cl = find_client_by_ip(SIN_GET_ADDR(cad), ntohs(SIN_GET_PORT(cad)));
|
---|
595 | rl = n;
|
---|
596 | buf[0] = 'U';
|
---|
597 | memcpy(buf + 1, &rl, 2);
|
---|
598 |
|
---|
599 | if(cs_check_violation(SIN_GET_ADDR(cad), port->s_port))
|
---|
600 | {
|
---|
601 | NULLFREE(buf);
|
---|
602 | return 0;
|
---|
603 | }
|
---|
604 |
|
---|
605 | cs_log_dbg(D_TRACE, "got %d bytes on port %d from ip %s:%d client %s",
|
---|
606 | n, port->s_port,
|
---|
607 | cs_inet_ntoa(SIN_GET_ADDR(cad)), SIN_GET_PORT(cad),
|
---|
608 | username(cl));
|
---|
609 |
|
---|
610 | if(!cl)
|
---|
611 | {
|
---|
612 | cl = create_client(SIN_GET_ADDR(cad));
|
---|
613 | if(!cl) { return 0; }
|
---|
614 |
|
---|
615 | cl->module_idx = module_idx;
|
---|
616 | cl->port_idx = port_idx;
|
---|
617 | cl->udp_fd = port->fd;
|
---|
618 | cl->udp_sa = cad;
|
---|
619 | cl->udp_sa_len = sizeof(cl->udp_sa);
|
---|
620 |
|
---|
621 | cl->port = ntohs(SIN_GET_PORT(cad));
|
---|
622 | cl->typ = 'c';
|
---|
623 |
|
---|
624 | add_job(cl, ACTION_CLIENT_INIT, NULL, 0);
|
---|
625 | }
|
---|
626 | add_job(cl, ACTION_CLIENT_UDP, buf, n + 3);
|
---|
627 | }
|
---|
628 | else
|
---|
629 | { NULLFREE(buf); }
|
---|
630 | }
|
---|
631 | else // TCP
|
---|
632 | {
|
---|
633 | int32_t pfd3;
|
---|
634 | if((pfd3 = accept(port->fd, (struct sockaddr *)&cad, (socklen_t *)&scad)) > 0)
|
---|
635 | {
|
---|
636 |
|
---|
637 | if(cs_check_violation(SIN_GET_ADDR(cad), port->s_port))
|
---|
638 | {
|
---|
639 | close(pfd3);
|
---|
640 | return 0;
|
---|
641 | }
|
---|
642 |
|
---|
643 | cl = create_client(SIN_GET_ADDR(cad));
|
---|
644 | if(cl == NULL)
|
---|
645 | {
|
---|
646 | close(pfd3);
|
---|
647 | return 0;
|
---|
648 | }
|
---|
649 |
|
---|
650 | int32_t flag = 1;
|
---|
651 | setsockopt(pfd3, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
|
---|
652 | setTCPTimeouts(pfd3);
|
---|
653 |
|
---|
654 | cl->module_idx = module_idx;
|
---|
655 | cl->udp_fd = pfd3;
|
---|
656 | cl->port_idx = port_idx;
|
---|
657 |
|
---|
658 | cl->pfd = pfd3;
|
---|
659 | cl->port = ntohs(SIN_GET_PORT(cad));
|
---|
660 | cl->typ = 'c';
|
---|
661 |
|
---|
662 | add_job(cl, ACTION_CLIENT_INIT, NULL, 0);
|
---|
663 | }
|
---|
664 | }
|
---|
665 | return 0;
|
---|
666 | }
|
---|
667 |
|
---|
668 | void set_so_reuseport(int fd) {
|
---|
669 | #ifdef SO_REUSEPORT
|
---|
670 | // See: http://stackoverflow.com/questions/3261965/so-reuseport-on-linux
|
---|
671 | int32_t on = 1;
|
---|
672 | setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on));
|
---|
673 | #else
|
---|
674 | fd = fd; // Do nothing
|
---|
675 | #endif
|
---|
676 | }
|
---|
677 |
|
---|
678 | int32_t start_listener(struct s_module *module, struct s_port *port)
|
---|
679 | {
|
---|
680 | int32_t ov = 1, timeout, is_udp, i;
|
---|
681 | char ptxt[2][45];
|
---|
682 | struct SOCKADDR sad; // structure to hold server's address
|
---|
683 | socklen_t sad_len;
|
---|
684 |
|
---|
685 | ptxt[0][0] = ptxt[1][0] = '\0';
|
---|
686 | if(!port->s_port)
|
---|
687 | {
|
---|
688 | cs_log_dbg(D_TRACE, "%s: disabled", module->desc);
|
---|
689 | return 0;
|
---|
690 | }
|
---|
691 | is_udp = (module->type == MOD_CONN_UDP);
|
---|
692 |
|
---|
693 | memset(&sad, 0 , sizeof(sad));
|
---|
694 | #ifdef IPV6SUPPORT
|
---|
695 | SIN_GET_FAMILY(sad) = AF_INET6;
|
---|
696 | SIN_GET_ADDR(sad) = in6addr_any;
|
---|
697 | sad_len = sizeof(struct sockaddr_in6);
|
---|
698 | #else
|
---|
699 | sad.sin_family = AF_INET;
|
---|
700 | sad_len = sizeof(struct sockaddr);
|
---|
701 |
|
---|
702 | if(!module->s_ip)
|
---|
703 | { module->s_ip = cfg.srvip; }
|
---|
704 |
|
---|
705 | if(module->s_ip)
|
---|
706 | {
|
---|
707 | sad.sin_addr.s_addr = module->s_ip;
|
---|
708 | snprintf(ptxt[0], sizeof(ptxt[0]), ", ip=%s", inet_ntoa(sad.sin_addr));
|
---|
709 | }
|
---|
710 | else
|
---|
711 | {
|
---|
712 | sad.sin_addr.s_addr = INADDR_ANY;
|
---|
713 | }
|
---|
714 | #endif
|
---|
715 |
|
---|
716 | timeout = cfg.bindwait;
|
---|
717 | port->fd = 0;
|
---|
718 |
|
---|
719 | if(port->s_port > 0) // test for illegal value
|
---|
720 | {
|
---|
721 | SIN_GET_PORT(sad) = htons((uint16_t)port->s_port);
|
---|
722 | }
|
---|
723 | else
|
---|
724 | {
|
---|
725 | cs_log("%s: Bad port %d", module->desc, port->s_port);
|
---|
726 | return 0;
|
---|
727 | }
|
---|
728 |
|
---|
729 | int s_type = (is_udp ? SOCK_DGRAM : SOCK_STREAM);
|
---|
730 | int s_proto = (is_udp ? IPPROTO_UDP : IPPROTO_TCP);
|
---|
731 |
|
---|
732 | if((port->fd = socket(DEFAULT_AF, s_type, s_proto)) < 0)
|
---|
733 | {
|
---|
734 | cs_log("%s: Cannot create socket (errno=%d: %s)", module->desc, errno, strerror(errno));
|
---|
735 | #ifdef IPV6SUPPORT
|
---|
736 | cs_log("%s: Trying fallback to IPv4", module->desc);
|
---|
737 | if((port->fd = socket(AF_INET, s_type, s_proto)) < 0)
|
---|
738 | {
|
---|
739 | cs_log("%s: Cannot create socket (errno=%d: %s)", module->desc, errno, strerror(errno));
|
---|
740 | return 0;
|
---|
741 | }
|
---|
742 | #else
|
---|
743 | return 0;
|
---|
744 | #endif
|
---|
745 | }
|
---|
746 |
|
---|
747 | #ifdef IPV6SUPPORT
|
---|
748 | // azbox toolchain do not have this define
|
---|
749 | #ifndef IPV6_V6ONLY
|
---|
750 | #define IPV6_V6ONLY 26
|
---|
751 | #endif
|
---|
752 | // set the server socket option to listen on IPv4 and IPv6 simultaneously
|
---|
753 | int val = 0;
|
---|
754 | if(setsockopt(port->fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val, sizeof(val)) < 0)
|
---|
755 | {
|
---|
756 | cs_log("%s: setsockopt(IPV6_V6ONLY) failed (errno=%d: %s)", module->desc, errno, strerror(errno));
|
---|
757 | }
|
---|
758 | #endif
|
---|
759 |
|
---|
760 | ov = 1;
|
---|
761 | if(setsockopt(port->fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ov, sizeof(ov)) < 0)
|
---|
762 | {
|
---|
763 | cs_log("%s: setsockopt failed (errno=%d: %s)", module->desc, errno, strerror(errno));
|
---|
764 | close(port->fd);
|
---|
765 | port->fd = 0;
|
---|
766 | return 0;
|
---|
767 | }
|
---|
768 |
|
---|
769 | set_so_reuseport(port->fd);
|
---|
770 |
|
---|
771 | int prio_ret = set_socket_priority(port->fd, cfg.netprio);
|
---|
772 | if (prio_ret > -1) {
|
---|
773 | snprintf(ptxt[1], sizeof(ptxt[1]), ", prio=%d [%s%s%s ]", cfg.netprio, prio_ret&0x04 ? " SO_PRIORITY" : "", prio_ret&0x01 ? " IP_TOS" : "", prio_ret&0x02 ? " IPV6_TCLASS" : "");
|
---|
774 | }
|
---|
775 |
|
---|
776 | if(!is_udp)
|
---|
777 | {
|
---|
778 | int32_t keep_alive = 1;
|
---|
779 | setsockopt(port->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive));
|
---|
780 | }
|
---|
781 |
|
---|
782 | while(timeout-- && !exit_oscam)
|
---|
783 | {
|
---|
784 | if(bind(port->fd, (struct sockaddr *)&sad, sad_len) < 0)
|
---|
785 | {
|
---|
786 | if(timeout)
|
---|
787 | {
|
---|
788 | cs_log("%s: Bind request failed (%s), waiting another %d seconds",
|
---|
789 | module->desc, strerror(errno), timeout);
|
---|
790 | cs_sleepms(1000);
|
---|
791 | }
|
---|
792 | else
|
---|
793 | {
|
---|
794 | cs_log("%s: Bind request failed (%s), giving up", module->desc, strerror(errno));
|
---|
795 | close(port->fd);
|
---|
796 | port->fd = 0;
|
---|
797 | return 0;
|
---|
798 | }
|
---|
799 | }
|
---|
800 | else
|
---|
801 | {
|
---|
802 | timeout = 0;
|
---|
803 | }
|
---|
804 | }
|
---|
805 |
|
---|
806 | if(!is_udp)
|
---|
807 | {
|
---|
808 | if(listen(port->fd, CS_QLEN) < 0)
|
---|
809 | {
|
---|
810 | cs_log("%s: Cannot start listen mode (errno=%d: %s)", module->desc, errno, strerror(errno));
|
---|
811 | close(port->fd);
|
---|
812 | port->fd = 0;
|
---|
813 | return 0;
|
---|
814 | }
|
---|
815 | }
|
---|
816 |
|
---|
817 | cs_log("%s: initialized (fd=%d, port=%d%s%s)", module->desc, port->fd, port->s_port, ptxt[0], ptxt[1]);
|
---|
818 |
|
---|
819 | for(i = 0; port->ncd && i < port->ncd->ncd_ftab.nfilts; i++)
|
---|
820 | {
|
---|
821 | int32_t j, pos = 0;
|
---|
822 | char buf[30 + (8 * port->ncd->ncd_ftab.filts[i].nprids)];
|
---|
823 | pos += snprintf(buf, sizeof(buf), "-> CAID: %04X PROVID: ", port->ncd->ncd_ftab.filts[i].caid);
|
---|
824 |
|
---|
825 | for(j = 0; j < port->ncd->ncd_ftab.filts[i].nprids; j++)
|
---|
826 | { pos += snprintf(buf + pos, sizeof(buf) - pos, "%06X, ", port->ncd->ncd_ftab.filts[i].prids[j]); }
|
---|
827 |
|
---|
828 | if(pos > 2 && j > 0)
|
---|
829 | { buf[pos - 2] = '\0'; }
|
---|
830 |
|
---|
831 | cs_log("%s", buf);
|
---|
832 | }
|
---|
833 |
|
---|
834 | return port->fd;
|
---|
835 | }
|
---|
836 |
|
---|
837 | #ifdef __CYGWIN__
|
---|
838 | /**
|
---|
839 | * Workaround missing MSG_WAITALL implementation under Cygwin.
|
---|
840 | */
|
---|
841 | ssize_t cygwin_recv(int sock, void *buf, int count, int tflags)
|
---|
842 | {
|
---|
843 | char *bp = buf;
|
---|
844 | int n = 0;
|
---|
845 |
|
---|
846 | if ((n = recv(sock, bp, count, tflags)) < 0)
|
---|
847 | {
|
---|
848 | return(n);
|
---|
849 | }
|
---|
850 |
|
---|
851 | if (n < count && (tflags & MSG_WAITALL))
|
---|
852 | {
|
---|
853 | cs_log_dbg(D_TRACE, "Cygwin socket read retry. Got %d expected %d", n, count);
|
---|
854 |
|
---|
855 | int n2 = recv(sock, bp + n, count - n, tflags);
|
---|
856 | if (n2 < 0 || n + n2 != count)
|
---|
857 | {
|
---|
858 | cs_log_dbg(D_TRACE, "Cygwin socket read retry failed. Got %d", n2);
|
---|
859 | if (n2 < 0)
|
---|
860 | {
|
---|
861 | return(n2);
|
---|
862 | }
|
---|
863 | }
|
---|
864 | else
|
---|
865 | {
|
---|
866 | cs_log_dbg(D_TRACE, "Cygwin socket read retry success. Got %d - Total: %d", n2, n + n2);
|
---|
867 | }
|
---|
868 |
|
---|
869 | n+= n2;
|
---|
870 | }
|
---|
871 |
|
---|
872 | return n;
|
---|
873 | }
|
---|
874 | #endif /* __CYGWIN__ */
|
---|