927 lines
20 KiB
C
927 lines
20 KiB
C
#define MODULE_LOG_PREFIX "net"
|
|
|
|
#include "globals.h"
|
|
#include "oscam-client.h"
|
|
#include "oscam-failban.h"
|
|
#include "oscam-lock.h"
|
|
#include "oscam-net.h"
|
|
#include "oscam-string.h"
|
|
#include "oscam-time.h"
|
|
#include "oscam-work.h"
|
|
|
|
extern CS_MUTEX_LOCK gethostbyname_lock;
|
|
extern int32_t exit_oscam;
|
|
|
|
#ifndef IPV6SUPPORT
|
|
static int32_t inet_byteorder = 0;
|
|
|
|
static in_addr_t cs_inet_order(in_addr_t n)
|
|
{
|
|
if(!inet_byteorder)
|
|
{ inet_byteorder = (inet_addr("1.2.3.4") + 1 == inet_addr("1.2.3.5")) ? 1 : 2; }
|
|
switch(inet_byteorder)
|
|
{
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
n = ((n & 0xff000000) >> 24) |
|
|
((n & 0x00ff0000) >> 8) |
|
|
((n & 0x0000ff00) << 8) |
|
|
((n & 0x000000ff) << 24);
|
|
break;
|
|
}
|
|
return n;
|
|
}
|
|
#endif
|
|
|
|
char *cs_inet_ntoa(IN_ADDR_T addr)
|
|
{
|
|
#ifdef IPV6SUPPORT
|
|
static char buff[INET6_ADDRSTRLEN];
|
|
if(IN6_IS_ADDR_V4MAPPED(&addr) || IN6_IS_ADDR_V4COMPAT(&addr))
|
|
{
|
|
snprintf(buff, sizeof(buff), "%d.%d.%d.%d",
|
|
addr.s6_addr[12], addr.s6_addr[13], addr.s6_addr[14], addr.s6_addr[15]);
|
|
}
|
|
else
|
|
{
|
|
inet_ntop(AF_INET6, &(addr.s6_addr), buff, INET6_ADDRSTRLEN);
|
|
}
|
|
return buff;
|
|
#else
|
|
struct in_addr in;
|
|
in.s_addr = addr;
|
|
return (char *)inet_ntoa(in);
|
|
#endif
|
|
}
|
|
|
|
void cs_inet_addr(char *txt, IN_ADDR_T *out)
|
|
{
|
|
#ifdef IPV6SUPPORT
|
|
char buff[INET6_ADDRSTRLEN];
|
|
//trying as IPv6 address
|
|
if(inet_pton(AF_INET6, txt, out->s6_addr) == 0)
|
|
{
|
|
//now trying as mapped IPv4
|
|
snprintf(buff, sizeof(buff), "::ffff:%s", txt);
|
|
inet_pton(AF_INET6, buff, out->s6_addr);
|
|
}
|
|
#else
|
|
*out = inet_addr(txt);
|
|
#endif
|
|
}
|
|
|
|
void cs_resolve(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len)
|
|
{
|
|
#ifdef IPV6SUPPORT
|
|
cs_getIPv6fromHost(hostname, ip, sock, sa_len, 0);
|
|
#else
|
|
*ip = cs_getIPfromHost(hostname);
|
|
if(sa_len)
|
|
{ *sa_len = sizeof(*sock); }
|
|
#endif
|
|
}
|
|
|
|
#ifdef IPV6SUPPORT
|
|
void cs_resolve_v4(const char *hostname, IN_ADDR_T *ip, struct SOCKADDR *sock, socklen_t *sa_len)
|
|
{
|
|
cs_getIPv6fromHost(hostname, ip, sock, sa_len, 1);
|
|
}
|
|
#endif
|
|
|
|
#ifdef IPV6SUPPORT
|
|
int32_t cs_in6addr_equal(struct in6_addr *a1, struct in6_addr *a2)
|
|
{
|
|
return memcmp(a1, a2, 16) == 0;
|
|
}
|
|
|
|
int32_t cs_in6addr_lt(struct in6_addr *a, struct in6_addr *b)
|
|
{
|
|
int i;
|
|
for(i = 0; i < 4; i++)
|
|
{
|
|
if((i == 2) && ((IN6_IS_ADDR_V4COMPAT(a) && IN6_IS_ADDR_V4MAPPED(b)) ||
|
|
(IN6_IS_ADDR_V4COMPAT(b) && IN6_IS_ADDR_V4MAPPED(a))))
|
|
{ continue; } // skip comparing this part
|
|
|
|
if(a->s6_addr32[i] != b->s6_addr32[i])
|
|
{ return ntohl(a->s6_addr32[i]) < ntohl(b->s6_addr32[i]); }
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int32_t cs_in6addr_isnull(struct in6_addr *addr)
|
|
{
|
|
int i;
|
|
for(i = 0; i < 16; i++)
|
|
if(addr->s6_addr[i])
|
|
{ return 0; }
|
|
return 1;
|
|
}
|
|
|
|
void cs_in6addr_copy(struct in6_addr *dst, struct in6_addr *src)
|
|
{
|
|
memcpy(dst, src, 16);
|
|
}
|
|
|
|
void cs_in6addr_ipv4map(struct in6_addr *dst, in_addr_t src)
|
|
{
|
|
memset(dst->s6_addr, 0, 16);
|
|
dst->s6_addr[10] = 0xff;
|
|
dst->s6_addr[11] = 0xff;
|
|
memcpy(dst->s6_addr + 12, &src, 4);
|
|
}
|
|
#endif
|
|
|
|
IN_ADDR_T get_null_ip(void)
|
|
{
|
|
IN_ADDR_T ip;
|
|
#ifdef IPV6SUPPORT
|
|
cs_inet_addr("::", &ip);
|
|
#else
|
|
ip = 0;
|
|
#endif
|
|
return ip;
|
|
}
|
|
|
|
void set_null_ip(IN_ADDR_T *ip)
|
|
{
|
|
#ifdef IPV6SUPPORT
|
|
cs_inet_addr("::", ip);
|
|
#else
|
|
*ip = 0;
|
|
#endif
|
|
}
|
|
|
|
void set_localhost_ip(IN_ADDR_T *ip)
|
|
{
|
|
#ifdef IPV6SUPPORT
|
|
cs_inet_addr("::1", ip);
|
|
#else
|
|
cs_inet_addr("127.0.0.1", ip);
|
|
#endif
|
|
}
|
|
|
|
int32_t check_ip(struct s_ip *ip, IN_ADDR_T n)
|
|
{
|
|
struct s_ip *p_ip;
|
|
int32_t ok = 0;
|
|
#ifdef IPV6SUPPORT
|
|
for(p_ip = ip; (p_ip) && (!ok); p_ip = p_ip->next)
|
|
{
|
|
ok = cs_in6addr_lt(&n, &p_ip->ip[0]);
|
|
ok |= cs_in6addr_lt(&p_ip->ip[1], &n);
|
|
ok = !ok;
|
|
}
|
|
#else
|
|
for(p_ip = ip; (p_ip) && (!ok); p_ip = p_ip->next)
|
|
{ ok = ((cs_inet_order(n) >= cs_inet_order(p_ip->ip[0])) && (cs_inet_order(n) <= cs_inet_order(p_ip->ip[1]))); }
|
|
#endif
|
|
return ok;
|
|
}
|
|
|
|
/* Returns the ip from the given hostname. If gethostbyname is configured in the config file, a lock
|
|
will be held until the ip has been resolved. */
|
|
uint32_t cs_getIPfromHost(const char *hostname)
|
|
{
|
|
uint32_t result = 0;
|
|
// Resolve with gethostbyname:
|
|
if(cfg.resolve_gethostbyname)
|
|
{
|
|
cs_writelock(__func__, &gethostbyname_lock);
|
|
struct hostent *rht = gethostbyname(hostname);
|
|
if(!rht)
|
|
{ cs_log("can't resolve %s", hostname); }
|
|
else
|
|
{ result = ((struct in_addr *)rht->h_addr)->s_addr; }
|
|
cs_writeunlock(__func__, &gethostbyname_lock);
|
|
}
|
|
else // Resolve with getaddrinfo:
|
|
{
|
|
struct addrinfo hints, *res = NULL;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
|
|
int32_t err = getaddrinfo(hostname, NULL, &hints, &res);
|
|
if(err != 0 || !res || !res->ai_addr)
|
|
{
|
|
cs_log("can't resolve %s, error: %s", hostname, err ? gai_strerror(err) : "unknown");
|
|
}
|
|
else
|
|
{
|
|
result = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
|
|
}
|
|
if(res) { freeaddrinfo(res); }
|
|
}
|
|
return result;
|
|
}
|
|
|
|
#ifdef IPV6SUPPORT
|
|
void cs_getIPv6fromHost(const char *hostname, struct in6_addr *addr, struct sockaddr_storage *sa, socklen_t *sa_len, uint8_t forceV4)
|
|
{
|
|
uint32_t ipv4addr = 0;
|
|
uint8_t ipv6_found = 0;
|
|
struct addrinfo hints, *res = NULL;
|
|
memset(&hints, 0, sizeof(hints));
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if (forceV4)
|
|
{
|
|
hints.ai_family = AF_INET;
|
|
}
|
|
else
|
|
{
|
|
hints.ai_family = AF_UNSPEC;
|
|
}
|
|
|
|
hints.ai_protocol = IPPROTO_TCP;
|
|
int32_t err = getaddrinfo(hostname, NULL, &hints, &res);
|
|
if(err != 0 || !res || !res->ai_addr)
|
|
{
|
|
cs_log("can't resolve %s, error: %s", hostname, err ? gai_strerror(err) : "unknown");
|
|
}
|
|
else
|
|
{
|
|
while (res)
|
|
{
|
|
if ((!forceV4 && !ipv6_found) || (forceV4))
|
|
{
|
|
ipv4addr = ((struct sockaddr_in *)(res->ai_addr))->sin_addr.s_addr;
|
|
if(res->ai_family == AF_INET)
|
|
{ cs_in6addr_ipv4map(addr, ipv4addr); }
|
|
else
|
|
{
|
|
IP_ASSIGN(*addr, SIN_GET_ADDR(*res->ai_addr));
|
|
ipv6_found = 1;
|
|
}
|
|
if(sa)
|
|
{ memcpy(sa, res->ai_addr, res->ai_addrlen); }
|
|
if(sa_len)
|
|
{ *sa_len = res->ai_addrlen; }
|
|
|
|
res = res->ai_next;
|
|
}
|
|
else
|
|
{
|
|
res = res->ai_next;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(res)
|
|
{ freeaddrinfo(res); }
|
|
}
|
|
#endif
|
|
|
|
int set_socket_priority(int fd, int priority)
|
|
{
|
|
#if defined(IP_TOS) || defined(SO_PRIORITY)
|
|
if (priority == 0) { return -1; } // default value, therefore leave it untouched (IPP=0; DSCP=CS0)
|
|
|
|
int ret = 0;
|
|
int cos __attribute__ ((unused)) = 0;
|
|
int tos __attribute__ ((unused)) = 0x00;
|
|
|
|
switch(priority)
|
|
{
|
|
case 1: // IPP=1; DSCP=CS1
|
|
cos = 1;
|
|
tos = 0x20;
|
|
break;
|
|
|
|
case 2: // IPP=1; DSCP=AF11
|
|
cos = 1;
|
|
tos = 0x28;
|
|
break;
|
|
|
|
case 3: // IPP=1; DSCP=AF12
|
|
cos = 1;
|
|
tos = 0x30;
|
|
break;
|
|
|
|
case 4: // IPP=1; DSCP=AF13
|
|
cos = 1;
|
|
tos = 0x38;
|
|
break;
|
|
|
|
case 5: // IPP=2; DSCP=CS2
|
|
cos = 2;
|
|
tos = 0x40;
|
|
break;
|
|
|
|
case 6: // IPP=2; DSCP=AF21
|
|
cos = 2;
|
|
tos = 0x48;
|
|
break;
|
|
|
|
case 7: // IPP=2; DSCP=AF22
|
|
cos = 2;
|
|
tos = 0x50;
|
|
break;
|
|
|
|
case 8: // IPP=2; DSCP=AF23
|
|
cos = 2;
|
|
tos = 0x58;
|
|
break;
|
|
|
|
case 9: // IPP=3; DSCP=CS3
|
|
cos = 3;
|
|
tos = 0x60;
|
|
break;
|
|
|
|
case 10: // IPP=3; DSCP=AF31
|
|
cos = 3;
|
|
tos = 0x68;
|
|
break;
|
|
|
|
case 11: // IPP=3; DSCP=AF32
|
|
cos = 3;
|
|
tos = 0x70;
|
|
break;
|
|
|
|
case 12: // IPP=3; DSCP=AF33
|
|
cos = 3;
|
|
tos = 0x78;
|
|
break;
|
|
|
|
case 13: // IPP=4; DSCP=CS4
|
|
cos = 4;
|
|
tos = 0x80;
|
|
break;
|
|
|
|
case 14: // IPP=4; DSCP=AF41
|
|
cos = 4;
|
|
tos = 0x88;
|
|
break;
|
|
|
|
case 15: // IPP=4; DSCP=AF42
|
|
cos = 4;
|
|
tos = 0x90;
|
|
break;
|
|
|
|
case 16: // IPP=4; DSCP=AF43
|
|
cos = 4;
|
|
tos = 0x98;
|
|
break;
|
|
|
|
case 17: // IPP=5; DSCP=CS5
|
|
cos = 5;
|
|
tos = 0xa0;
|
|
break;
|
|
|
|
case 18: // IPP=5; DSCP=EF
|
|
cos = 5;
|
|
tos = 0xb8;
|
|
break;
|
|
|
|
case 19: // IPP=6; DSCP=CS6
|
|
cos = 6;
|
|
tos = 0xc0;
|
|
break;
|
|
|
|
case 20: // IPP=7; DSCP=CS7
|
|
cos = 7;
|
|
tos = 0xe0;
|
|
break;
|
|
}
|
|
|
|
#ifdef IP_TOS
|
|
if (setsockopt(fd, IPPROTO_IP, IP_TOS, (void *)&tos, sizeof(tos)) < 0)
|
|
{
|
|
cs_log("Setting IP_TOS failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
ret = ret ^ 0x01;
|
|
}
|
|
|
|
#if defined(IPV6SUPPORT) && defined(IPV6_TCLASS)
|
|
int32_t family = 0;
|
|
socklen_t length = sizeof(int);
|
|
#ifndef SO_DOMAIN
|
|
#define SO_DOMAIN 39
|
|
#endif
|
|
if (getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &family, &length) <0)
|
|
{
|
|
cs_log("getsockopt err - set_socket_priority()");
|
|
}
|
|
else
|
|
{
|
|
if (family == AF_INET6)
|
|
{
|
|
if (setsockopt(fd, IPPROTO_IPV6, IPV6_TCLASS, (void *)&tos, sizeof(tos)) < 0)
|
|
{
|
|
cs_log("Setting IPV6_TCLASS failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
ret = ret ^ 0x02;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef SO_PRIORITY
|
|
if (setsockopt(fd, SOL_SOCKET, SO_PRIORITY, (void *)&cos, sizeof(cos)) < 0)
|
|
{
|
|
cs_log("Setting SO_PRIORITY failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
else
|
|
{
|
|
ret = ret ^ 0x04;
|
|
}
|
|
#endif
|
|
|
|
return ret;
|
|
#else
|
|
(void)fd;
|
|
(void)priority;
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
void setTCPTimeouts(int32_t sock)
|
|
{
|
|
int32_t flag = 1;
|
|
// this is not only for a real keepalive but also to detect closed connections so it should not be configurable
|
|
if(setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &flag, sizeof(flag)) && errno != EBADF)
|
|
{
|
|
cs_log("Setting SO_KEEPALIVE failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
|
|
#if defined(TCP_KEEPIDLE) && defined(TCP_KEEPCNT) && defined(TCP_KEEPINTVL)
|
|
flag = 10;
|
|
|
|
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)
|
|
{
|
|
cs_log("Setting TCP_KEEPIDLE failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
|
|
flag = 3;
|
|
|
|
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
|
|
{
|
|
cs_log("Setting TCP_KEEPCNT failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
|
|
flag = 1;
|
|
|
|
if(setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &flag, sizeof(flag)) && errno != EBADF)
|
|
{
|
|
; // send a keepalive packet out every second (until answer has been received or TCP_KEEPCNT has been reached)
|
|
cs_log("Setting TCP_KEEPINTVL failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
#endif
|
|
|
|
struct timeval tv;
|
|
tv.tv_sec = 60;
|
|
tv.tv_usec = 0;
|
|
|
|
if(setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(struct timeval)) && errno != EBADF)
|
|
{
|
|
;
|
|
cs_log("Setting SO_SNDTIMEO failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
|
|
tv.tv_sec = 600;
|
|
tv.tv_usec = 0;
|
|
|
|
if(setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(struct timeval)) && errno != EBADF)
|
|
{
|
|
;
|
|
cs_log("Setting SO_RCVTIMEO failed, errno=%d, %s", errno, strerror(errno));
|
|
}
|
|
|
|
#if defined(TCP_USER_TIMEOUT)
|
|
int timeout = 60000; // RFC 5482 user timeout in milliseconds
|
|
setsockopt(sock, SOL_TCP, TCP_USER_TIMEOUT, (char *) &timeout, sizeof(timeout));
|
|
#endif
|
|
}
|
|
|
|
int set_nonblock(int32_t fd, bool nonblock)
|
|
{
|
|
int32_t flags = fcntl(fd, F_GETFL);
|
|
if (flags == -1)
|
|
return -1;
|
|
if (nonblock)
|
|
flags |= O_NONBLOCK;
|
|
else
|
|
flags &= (~O_NONBLOCK);
|
|
return fcntl(fd, F_SETFL, flags);
|
|
}
|
|
|
|
int8_t check_fd_for_data(int32_t fd)
|
|
{
|
|
int32_t rc;
|
|
struct pollfd pfd[1];
|
|
|
|
pfd[0].fd = fd;
|
|
pfd[0].events = (POLLIN | POLLPRI);
|
|
rc = poll(pfd, 1, 0);
|
|
|
|
if(rc == -1)
|
|
{ cs_log("check_fd_for_data(fd=%d) failed: (errno=%d %s)", fd, errno, strerror(errno)); }
|
|
|
|
if(rc == -1 || rc == 0)
|
|
{ return rc; }
|
|
|
|
if(pfd[0].revents & (POLLHUP | POLLNVAL | POLLERR))
|
|
{ return -2; }
|
|
|
|
return 1;
|
|
}
|
|
|
|
int32_t recv_from_udpipe(uint8_t *buf)
|
|
{
|
|
uint16_t n;
|
|
if(buf[0] != 'U')
|
|
{
|
|
cs_log("INTERNAL PIPE-ERROR");
|
|
cs_exit(1);
|
|
}
|
|
memcpy(&n, buf + 1, 2);
|
|
memmove(buf, buf + 3, n);
|
|
return n;
|
|
}
|
|
|
|
int32_t process_input(uint8_t *buf, int32_t buflen, int32_t timeout)
|
|
{
|
|
int32_t rc, i, pfdcount;
|
|
int64_t polltime, timeoutms;
|
|
struct pollfd pfd[2];
|
|
struct s_client *cl = cur_client();
|
|
|
|
struct timeb starttime;
|
|
struct timeb currenttime;
|
|
timeoutms = 1000 * timeout;
|
|
cs_ftime(&starttime);
|
|
polltime = timeoutms; // initial polltime = timeoutms
|
|
while(1)
|
|
{
|
|
pfdcount = 0;
|
|
if(cl->pfd)
|
|
{
|
|
pfd[pfdcount].fd = cl->pfd;
|
|
pfd[pfdcount++].events = POLLIN | POLLPRI;
|
|
}
|
|
int32_t p_rc = poll(pfd, pfdcount, polltime);
|
|
|
|
cs_ftime(¤ttime);
|
|
int64_t gone = comp_timeb(¤ttime, &starttime);
|
|
polltime = timeoutms - gone; // calculate polltime left
|
|
if(polltime < 0)
|
|
{
|
|
polltime = 0;
|
|
}
|
|
if(p_rc < 0)
|
|
{
|
|
if(errno == EINTR)
|
|
{ continue; }
|
|
else
|
|
{ return 0; }
|
|
}
|
|
|
|
if((p_rc == 0) && (timeout != 0) && (gone >= timeoutms)) // client maxidle reached? timeout = 0, idle disconnect disabled
|
|
{
|
|
rc = -9;
|
|
break;
|
|
}
|
|
|
|
for(i = 0; i < pfdcount && p_rc > 0; i++)
|
|
{
|
|
if(pfd[i].revents & POLLHUP) // POLLHUP is only valid in revents so it doesn't need to be set above in events
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if(!(pfd[i].revents & (POLLIN | POLLPRI)))
|
|
{ continue; }
|
|
|
|
if(pfd[i].fd == cl->pfd)
|
|
{ return get_module(cl)->recv(cl, buf, buflen); }
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static struct s_client *find_client_by_ip(IN_ADDR_T ip, in_port_t port)
|
|
{
|
|
struct s_client *cl;
|
|
for(cl = first_client; cl; cl = cl->next)
|
|
{
|
|
if(!cl->kill && IP_EQUAL(cl->ip, ip) && cl->port == port && (cl->typ == 'c' || cl->typ == 'm'))
|
|
{
|
|
return cl;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int32_t accept_connection(struct s_module *module, int8_t module_idx, int8_t port_idx)
|
|
{
|
|
struct SOCKADDR cad;
|
|
int32_t scad = sizeof(cad), n;
|
|
struct s_client *cl;
|
|
struct s_port *port = &module->ptab.ports[port_idx];
|
|
|
|
memset(&cad, 0, sizeof(struct SOCKADDR));
|
|
|
|
if(module->type == MOD_CONN_UDP)
|
|
{
|
|
uint8_t *buf;
|
|
if(!cs_malloc(&buf, 1024))
|
|
{ return -1; }
|
|
|
|
if((n = recvfrom(port->fd, buf + 3, 1024 - 3, 0, (struct sockaddr *)&cad, (socklen_t *)&scad)) > 0)
|
|
{
|
|
uint16_t rl;
|
|
cl = find_client_by_ip(SIN_GET_ADDR(cad), ntohs(SIN_GET_PORT(cad)));
|
|
rl = n;
|
|
buf[0] = 'U';
|
|
memcpy(buf + 1, &rl, 2);
|
|
|
|
if(cs_check_violation(SIN_GET_ADDR(cad), port->s_port))
|
|
{
|
|
NULLFREE(buf);
|
|
return 0;
|
|
}
|
|
|
|
cs_log_dbg(D_TRACE, "got %d bytes on port %d from ip %s:%d client %s",
|
|
n, port->s_port,
|
|
cs_inet_ntoa(SIN_GET_ADDR(cad)), SIN_GET_PORT(cad),
|
|
username(cl));
|
|
|
|
if(!cl)
|
|
{
|
|
cl = create_client(SIN_GET_ADDR(cad));
|
|
if(!cl) { return 0; }
|
|
|
|
cl->module_idx = module_idx;
|
|
cl->port_idx = port_idx;
|
|
cl->udp_fd = port->fd;
|
|
cl->udp_sa = cad;
|
|
cl->udp_sa_len = sizeof(cl->udp_sa);
|
|
|
|
cl->port = ntohs(SIN_GET_PORT(cad));
|
|
cl->typ = 'c';
|
|
|
|
add_job(cl, ACTION_CLIENT_INIT, NULL, 0);
|
|
}
|
|
add_job(cl, ACTION_CLIENT_UDP, buf, n + 3);
|
|
}
|
|
else
|
|
{ NULLFREE(buf); }
|
|
}
|
|
else // TCP
|
|
{
|
|
int32_t pfd3;
|
|
if((pfd3 = accept(port->fd, (struct sockaddr *)&cad, (socklen_t *)&scad)) > 0)
|
|
{
|
|
|
|
if(cs_check_violation(SIN_GET_ADDR(cad), port->s_port))
|
|
{
|
|
close(pfd3);
|
|
return 0;
|
|
}
|
|
|
|
cl = create_client(SIN_GET_ADDR(cad));
|
|
if(cl == NULL)
|
|
{
|
|
close(pfd3);
|
|
return 0;
|
|
}
|
|
|
|
int32_t flag = 1;
|
|
setsockopt(pfd3, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(flag));
|
|
setTCPTimeouts(pfd3);
|
|
|
|
cl->module_idx = module_idx;
|
|
cl->udp_fd = pfd3;
|
|
cl->port_idx = port_idx;
|
|
|
|
cl->pfd = pfd3;
|
|
cl->port = ntohs(SIN_GET_PORT(cad));
|
|
cl->typ = 'c';
|
|
|
|
add_job(cl, ACTION_CLIENT_INIT, NULL, 0);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void set_so_reuseport(int fd) {
|
|
#ifdef SO_REUSEPORT
|
|
// See: http://stackoverflow.com/questions/3261965/so-reuseport-on-linux
|
|
int32_t on = 1;
|
|
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, (void *)&on, sizeof(on));
|
|
#else
|
|
fd = fd; // Do nothing
|
|
#endif
|
|
}
|
|
|
|
int32_t start_listener(struct s_module *module, struct s_port *port)
|
|
{
|
|
int32_t ov = 1, timeout, is_udp, i;
|
|
char ptxt[2][45];
|
|
struct SOCKADDR sad; // structure to hold server's address
|
|
socklen_t sad_len;
|
|
|
|
ptxt[0][0] = ptxt[1][0] = '\0';
|
|
if(!port->s_port)
|
|
{
|
|
cs_log_dbg(D_TRACE, "%s: disabled", module->desc);
|
|
return 0;
|
|
}
|
|
is_udp = (module->type == MOD_CONN_UDP);
|
|
|
|
memset(&sad, 0 , sizeof(sad));
|
|
#ifdef IPV6SUPPORT
|
|
SIN_GET_FAMILY(sad) = AF_INET6;
|
|
SIN_GET_ADDR(sad) = in6addr_any;
|
|
sad_len = sizeof(struct sockaddr_in6);
|
|
#else
|
|
sad.sin_family = AF_INET;
|
|
sad_len = sizeof(struct sockaddr);
|
|
|
|
if(!module->s_ip)
|
|
{ module->s_ip = cfg.srvip; }
|
|
|
|
if(module->s_ip)
|
|
{
|
|
sad.sin_addr.s_addr = module->s_ip;
|
|
snprintf(ptxt[0], sizeof(ptxt[0]), ", ip=%s", inet_ntoa(sad.sin_addr));
|
|
}
|
|
else
|
|
{
|
|
sad.sin_addr.s_addr = INADDR_ANY;
|
|
}
|
|
#endif
|
|
|
|
timeout = cfg.bindwait;
|
|
port->fd = 0;
|
|
|
|
if(port->s_port > 0) // test for illegal value
|
|
{
|
|
SIN_GET_PORT(sad) = htons((uint16_t)port->s_port);
|
|
}
|
|
else
|
|
{
|
|
cs_log("%s: Bad port %d", module->desc, port->s_port);
|
|
return 0;
|
|
}
|
|
|
|
int s_type = (is_udp ? SOCK_DGRAM : SOCK_STREAM);
|
|
int s_proto = (is_udp ? IPPROTO_UDP : IPPROTO_TCP);
|
|
|
|
if((port->fd = socket(DEFAULT_AF, s_type, s_proto)) < 0)
|
|
{
|
|
cs_log("%s: Cannot create IPv6 socket (errno=%d: %s)", module->desc, errno, strerror(errno));
|
|
#ifdef IPV6SUPPORT
|
|
SIN_GET_FAMILY(sad) = AF_INET;
|
|
sad_len = sizeof(struct sockaddr_in);
|
|
cs_log("%s: Trying fallback to IPv4", module->desc);
|
|
if((port->fd = socket(AF_INET, s_type, s_proto)) < 0)
|
|
{
|
|
cs_log("%s: Cannot create IPv4 socket (errno=%d: %s)", module->desc, errno, strerror(errno));
|
|
return 0;
|
|
}
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
#ifdef IPV6SUPPORT
|
|
// azbox toolchain do not have this define
|
|
#ifndef IPV6_V6ONLY
|
|
#define IPV6_V6ONLY 26
|
|
#endif
|
|
else
|
|
{
|
|
// set the server socket option to listen on IPv4 and IPv6 simultaneously
|
|
int val = 0;
|
|
if(setsockopt(port->fd, IPPROTO_IPV6, IPV6_V6ONLY, (void *)&val, sizeof(val)) < 0)
|
|
{
|
|
cs_log("%s: setsockopt(IPV6_V6ONLY) failed (errno=%d: %s)", module->desc, errno, strerror(errno));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ov = 1;
|
|
if(setsockopt(port->fd, SOL_SOCKET, SO_REUSEADDR, (void *)&ov, sizeof(ov)) < 0)
|
|
{
|
|
cs_log("%s: setsockopt failed (errno=%d: %s)", module->desc, errno, strerror(errno));
|
|
close(port->fd);
|
|
port->fd = 0;
|
|
return 0;
|
|
}
|
|
|
|
set_so_reuseport(port->fd);
|
|
|
|
int prio_ret = set_socket_priority(port->fd, cfg.netprio);
|
|
if (prio_ret > -1) {
|
|
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" : "");
|
|
}
|
|
|
|
if(!is_udp)
|
|
{
|
|
int32_t keep_alive = 1;
|
|
setsockopt(port->fd, SOL_SOCKET, SO_KEEPALIVE, (void *)&keep_alive, sizeof(keep_alive));
|
|
}
|
|
|
|
while(timeout-- && !exit_oscam)
|
|
{
|
|
if(bind(port->fd, (struct sockaddr *)&sad, sad_len) < 0)
|
|
{
|
|
if(timeout)
|
|
{
|
|
cs_log("%s: Bind request failed (%s), waiting another %d seconds",
|
|
module->desc, strerror(errno), timeout);
|
|
cs_sleepms(1000);
|
|
}
|
|
else
|
|
{
|
|
cs_log("%s: Bind request failed (%s), giving up", module->desc, strerror(errno));
|
|
close(port->fd);
|
|
port->fd = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
timeout = 0;
|
|
}
|
|
}
|
|
|
|
if(!is_udp)
|
|
{
|
|
if(listen(port->fd, CS_QLEN) < 0)
|
|
{
|
|
cs_log("%s: Cannot start listen mode (errno=%d: %s)", module->desc, errno, strerror(errno));
|
|
close(port->fd);
|
|
port->fd = 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
cs_log("%s: initialized (fd=%d, port=%d%s%s)", module->desc, port->fd, port->s_port, ptxt[0], ptxt[1]);
|
|
|
|
for(i = 0; port->ncd && i < port->ncd->ncd_ftab.nfilts; i++)
|
|
{
|
|
int32_t j, pos = 0;
|
|
char buf[30 + (8 * port->ncd->ncd_ftab.filts[i].nprids)];
|
|
pos += snprintf(buf, sizeof(buf), "-> CAID: %04X PROVID: ", port->ncd->ncd_ftab.filts[i].caid);
|
|
|
|
for(j = 0; j < port->ncd->ncd_ftab.filts[i].nprids; j++)
|
|
{ pos += snprintf(buf + pos, sizeof(buf) - pos, "%06X, ", port->ncd->ncd_ftab.filts[i].prids[j]); }
|
|
|
|
if(pos > 2 && j > 0)
|
|
{ buf[pos - 2] = '\0'; }
|
|
|
|
cs_log("%s", buf);
|
|
}
|
|
|
|
return port->fd;
|
|
}
|
|
|
|
#ifdef __CYGWIN__
|
|
/**
|
|
* Workaround missing MSG_WAITALL implementation under Cygwin.
|
|
*/
|
|
ssize_t cygwin_recv(int sock, void *buf, int count, int tflags)
|
|
{
|
|
char *bp = buf;
|
|
int n = 0;
|
|
|
|
if ((n = recv(sock, bp, count, tflags)) < 0)
|
|
{
|
|
return(n);
|
|
}
|
|
|
|
if (n < count && (tflags & MSG_WAITALL))
|
|
{
|
|
cs_log_dbg(D_TRACE, "Cygwin socket read retry. Got %d expected %d", n, count);
|
|
|
|
int n2 = recv(sock, bp + n, count - n, tflags);
|
|
if (n2 < 0 || n + n2 != count)
|
|
{
|
|
cs_log_dbg(D_TRACE, "Cygwin socket read retry failed. Got %d", n2);
|
|
if (n2 < 0)
|
|
{
|
|
return(n2);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cs_log_dbg(D_TRACE, "Cygwin socket read retry success. Got %d - Total: %d", n2, n + n2);
|
|
}
|
|
|
|
n+= n2;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
#endif /* __CYGWIN__ */
|