blob: c8bd383838e9a7973d1f88a0b553651ee6925c09 [file] [log] [blame]
#include <pthread.h>
#include "tr069.h"
#include "tr069_uci.h"
#include "tr069_service.h"
#define closesocket(a) close(a)
int tr069_server_stun_fd;
int tr069_client_stun_fd;
int tr069_accept_stun_fd;
void *tcp_direct_connect(void *data);
void *stun_task(void * data);
void enable_stun(void);
struct stun_hdr {
unsigned short int type;
unsigned short int len;
unsigned int id[4];
unsigned char payload[0];
}__attribute__((packed));
struct stun_attr_hdr {
unsigned short int type;
unsigned short int len;
unsigned char payload[0];
}__attribute__((packed));
struct stun_attr_mapped_addr {
unsigned char unused;
unsigned char family;
unsigned short int port;
unsigned int address;
}__attribute__((packed));
static unsigned char buf[BUF_SIZE];
static unsigned char * p_stun_msg;
static unsigned short int msglen = 0;
#define RESET_MSG(ADDR) do { \
msglen = 0; \
p_stun_msg = ADDR; \
} while (0)
#define GET_MSG_LENGTH() msglen
#define SET_U16_VALUE(VALUE) do { \
*(p_stun_msg + msglen + 1) = ((VALUE) & 0xff); \
*(p_stun_msg + msglen + 0) = (((VALUE) >> 8) & 0xff); \
msglen += 2; \
} while (0)
#define SET_U32_VALUE(VALUE) do { \
SET_U16_VALUE((unsigned short int)(((VALUE) >> 16) && 0xffff)); \
SET_U16_VALUE((unsigned short int)((VALUE) && 0xffff)); \
} while (0)
#define SET_STRING_VALUE(STRING) do { \
msglen += sprintf((char *)(p_stun_msg + msglen), "%s", STRING); \
} while (0)
#define CONNECTION_REQUEST_BINDING_STRING "dslforum.org/TR-111 "
#define DEFAULT_STUN_SERVER "testacs.friendly-tech.com"
//#define DEFAULT_STUN_SERVER "stun.stunprotocol.org"
#define MAPPED_ADDRESS 0x0001
#define RESPONSE_ADDRESS 0x0002
#define CHANGE_REQUEST 0x0003
#define SOURCE_ADDRESS 0x0004
#define CHANGED_ADDRESS 0x0005
#define USERNAME 0x0006
#define PASSWORD 0x0007
#define MESSAGE_INTEGRITY 0x0008
#define ERROR_CODE 0x0009
#define UNKNOWN_ATTRIBUTES 0x000a
#define REFLECTED_FROM 0x000b
#define CONNECTION_REQUEST_BINDING 0xc001
#define BINDING_CHANGE 0xc002
#define BINDING_REQUEST 0x0001
#define BINDING_RESPONSE 0x0101
#define BINDING_ERR_RESP 0x0111
#define SHARED_SECRET_REQ 0x0002
#define SHARED_SECRET_RESP 0x0102
#define SHARED_SECRET_ERR_RESP 0x0112
static int sent_stun_result(struct tr069_task_command * cmd, int result)
{
struct tr069_task_command * msg = NULL;
if (result < 0 && cmd->payload[2] < 5) {
TR069_DEBUG("%s: Sent stun state to ACS failed, try resent", __func__);
msg = tr069_malloc(sizeof(struct tr069_task_command) + sizeof(int) * 3);
if (msg) {
unsigned int * p = msg->payload;
memset(msg, 0, sizeof(struct tr069_task_command));
msg->command_id = TR069_COMMAND_STUN_CHANGE;
msg->msg = (unsigned int)p;
p[0] = cmd->payload[0];
p[1] = cmd->payload[1];
p[2] = cmd->payload[2] + 1;
send_tr069_command(msg);
}
}
return 0;
}
static int handle_binding_response(const unsigned char * response, int total)
{
struct stun_attr_hdr * attr;
unsigned short int type, length;
TR069_DEBUG("Total remain %d", total);
while (total > 0) {
attr = (struct stun_attr_hdr *)response;
type = htons(attr->type);
length = htons(attr->len);
total -= 4;
response += 4;
switch (type) {
case MAPPED_ADDRESS: {
struct stun_attr_mapped_addr * mapped = (struct stun_attr_mapped_addr *)&attr->payload[0];
static struct in_addr addr_backup = {0};
struct in_addr addr_in;
addr_in.s_addr = mapped->address;
TR069_DEBUG("Handle mapped address: port %d, address %s", mapped->port, inet_ntoa(addr_in));
if (memcmp(&addr_backup, &addr_in, sizeof(struct in_addr))) {
struct tr069_task_command * msg;
TR069_DEBUG("Handle new puplic addreess");
addr_backup = addr_in;
// Send command to tr069 thread
msg = tr069_malloc(sizeof(struct tr069_task_command) + sizeof(int) * 3);
if (msg) {
unsigned int * p = msg->payload;
memset(msg, 0, sizeof(struct tr069_task_command) + sizeof(int) * 3);
msg->command_id = TR069_COMMAND_STUN_CHANGE;
msg->tsk_cmd_cb = sent_stun_result;
msg->msg = (unsigned int)p;
p[0] = addr_backup.s_addr;
p[1] = htons(mapped->port);
p[2] = 0; // Trace re-send cnt
send_tr069_command(msg);
} else {
TR069_ERR("Cannot malloc memory!(%s:%d)", __func__, __LINE__);
}
char * ip_buf = tr069_malloc(16);
memset(ip_buf,0,16);
tr069_get_host_ip("ccinet0",ip_buf);
inet_aton(ip_buf,&addr_backup);
tr069_free(ip_buf);
}
break;
}
default:
TR069_DEBUG("Handle unkonwn attribute 0x%04x", type);
break;
}
response += length;
total -= length;
TR069_DEBUG("After process remain %d", total);
}
return 0;
}
#define PRINT_BUF_SIZE 65
static char print_buf[PRINT_BUF_SIZE];
static void print_string(const char * string)
{
int len = strlen(string);
int printed = 0;
while (printed != len) {
if ((len - printed) > (PRINT_BUF_SIZE - 1)) {
memcpy(print_buf, string + printed, (PRINT_BUF_SIZE - 1));
printed += (PRINT_BUF_SIZE - 1);
print_buf[PRINT_BUF_SIZE - 1] = '\0';
} else {
sprintf(print_buf, "%s", string + printed);
printed = len;
}
TR069_DEBUG("[stun-value]%s", print_buf);
}
}
static char * get_line(char ** buf)
{
char * string = NULL;
char * start;
if (buf == NULL) return NULL;
string = *buf;
if (string == NULL) return NULL;
while ((*string == '\r') || (*string == '\n')) {
// Skipped special char from the beginning
string++;
}
if (*string == '\0') {
// String reach end
*buf = NULL;
return NULL;
}
start = string;
while ((*string != '\r') && (*string != '\n') && (*string != '\0')) {
string++;
}
if (*string != '\0') {
*string++ = '\0'; // Mark one line end
*buf = string;
} else {
*buf = string;
}
return start;
}
static int handle_udp_request(char * request, int total, int udptcp)
{
int ret = 200;
char * string = request;
char * line = NULL;
//char * token = NULL;
const char * method = NULL;
print_string(string);
if (udptcp == 0) {
if (!strncmp(string, "GET", 3)) {
goto send_cmd;
}
}
if ((string[total - 1] != '\n') || (string[total - 2] != '\r') ||
(string[total - 3] != '\n' || (string[total - 4] != '\r'))) {
TR069_ERR("HTTP request not complete");
ret = 401;
goto done;
}
if (!strncmp(string, "GET", 3)) {
method = "GET";
} else if (!strncmp(string, "POST", 4)) {
method = "POST";
} else {
TR069_ERR("Unknow method");
ret = 401;
goto done;
}
if (cpe.conn_req_username[0] == '\0') {
TR069_ERR("Not have user name");
ret = 200;
goto send_cmd;
}
while (NULL != (line = get_line(&string))) {
#define AUTHOR_HEAD "Authorization: Digest "
TR069_DEBUG("Get line: %s", line);
if (!strncmp(line, AUTHOR_HEAD, strlen(AUTHOR_HEAD))) {
struct digestdata *digest = NULL;
line += strlen(AUTHOR_HEAD);
digest_input(line, &digest);
if (digest) {
ret = digest_authenticate(cpe.conn_req_username, cpe.conn_req_password, method, digest);
if (ret == 0) {
ret = 200;
goto send_cmd;
}
clear_digestdata(digest);
digest = NULL;
}
break;
}
}
// Here should be no digest message or digest failed
ret = 401;
goto done;
// Send message to tr069 main task, establish ACS connection
send_cmd:
{
struct tr069_task_command * msg =
tr069_malloc(sizeof(struct tr069_task_command));
if (msg) {
memset(msg, 0, sizeof(struct tr069_task_command));
msg->command_id = TR069_COMMAND_REQUEST_CONNECT;
send_tr069_command(msg);
}
}
done:
return ret;
}
void *tcp_direct_connect(void *data)
{
UNUSED(data);
tr069_client_stun_fd = usock(USOCK_UNIX, TR069_INNER_STUN_CONN_SOCKET, NULL);
if (tr069_client_stun_fd < 0) {
TR069_ERR("%s,Failed to connect to server\n",__FUNCTION__);
return NULL;
}
TR069_INFO("%s,client fd %d", __FUNCTION__,tr069_client_stun_fd);
struct sockaddr_in ip = {0};
int fd = -1;
int ret;
int optval;
char * buf = NULL;
for (;;) {
struct sockaddr_in in = {0};
int client;
int size = sizeof(in);
//OSATaskSleep(20 * 1000 / 5);
if (fd < 0) {
TR069_INFO("[tcp]Try build tcp socket..");
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (fd < 0) {
TR069_ERR("[tcp]Build tcp socket faield..");
continue;
}
bzero(&ip, sizeof(ip));
ip.sin_family = AF_INET;
ip.sin_port = htons(8080);
ip.sin_addr.s_addr=htonl(INADDR_ANY);
ret = bind(fd, (const struct sockaddr *)&ip, sizeof(struct sockaddr));
if (ret < 0) {
TR069_ERR("[tcp]Bind to port failed..");
closesocket(fd);
fd = -1;
continue;
}
TR069_INFO("[tcp]bind success..");
ret = listen(fd, 2);
if (ret < 0) {
TR069_ERR("[tcp]Listen port failed..");
closesocket(fd);
fd = -1;
continue;
}
TR069_INFO("[tcp]listen success..");
}
client = accept(fd, (struct sockaddr *)&in, (socklen_t *)&size);
if (client < 0) {
TR069_ERR("[tcp]Accept bad socket handle..");
continue;
}
TR069_DEBUG("[tcp]Accept success..");
optval = 1;
ret = setsockopt(client, IPPROTO_TCP, TCP_NODELAY, &optval, sizeof(int));
optval = 8 * 1000;
struct timeval timeout={8,0};
ret = setsockopt(client, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval));
if (ret < 0) {
TR069_ERR("build_new_socket set tcp recv timeout failed(%d)", ret);
}
#undef BUFFER_SIZE
#define BUFFER_SIZE 2048
buf = tr069_malloc(BUFFER_SIZE+1);
if (buf == NULL) {
TR069_ERR("[tcp]malloc failed");
goto done;
}
memset(buf,0,BUFFER_SIZE+1);
ret = recv(client, buf, BUFFER_SIZE, 0);
if (ret >= 0) {
print_string(buf);
ret = handle_udp_request(buf, ret, 1);
memset(buf, 0, BUFFER_SIZE+1);
if (401 == ret) {
#undef HTTP_RESPONSE
#define HTTP_RESPONSE "HTTP/1.1 401 Unauthorized\r\nContent-Length: 0\r\n"\
"WWW-Authenticate: Digest realm=\"%s\", nonce=\"%08x\", qop=\"auth\", algorithm=MD5\r\n\r\n"
ret = snprintf(buf, BUFFER_SIZE, HTTP_RESPONSE, "tr069", (unsigned int)buf);
} else {
#undef HTTP_RESPONSE
#define HTTP_RESPONSE "HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n"
ret = snprintf(buf, BUFFER_SIZE, "%s", HTTP_RESPONSE);
}
send(client, buf, ret, 0);
}
tr069_free(buf);
buf = NULL;
done:
closesocket(client);
client = -1;
}
}
#define ADJUEST_SOCKET_TIMEOUT 1
#define MAX_STUN_MSG_SIZE (sizeof(void *))
#define MAX_STUN_MSGQ_SIZE 16
struct stun_command {
int command_id;
unsigned int payload[0];
};
void enable_stun(void)
{
struct stun_command * command = tr069_malloc(sizeof(struct stun_command));
if (!command) {
TR069_ERR("%s: failed", __func__);
return;
}
command->command_id = 1;
write(tr069_client_stun_fd,(void *)&command,MAX_STUN_MSG_SIZE);
return;
}
static pthread_t s_tid_StunTcpTask;
void *stun_task(void * data)
{
UNUSED(data);
struct hostent * host_entry;
struct stun_command * cmd = NULL;
int ret;
int fd = -1;
struct sockaddr_in ip = {0};
struct stun_hdr * hdr;
struct timeval timeout_value ={0,0};
//int stun_timeout = 20, sent_stun_cmd_state = 2,
int sent_stun_cmd_state = 2;
int wait_connect_timeout = cpe.stun_max_period ;
int connected_fd = -1;
unlink(TR069_INNER_STUN_CONN_SOCKET);
tr069_server_stun_fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, TR069_INNER_STUN_CONN_SOCKET, NULL);
if (tr069_server_stun_fd < 0) {
TR069_ERR("usock failed\n");
return NULL;
}
ret = 0;
ret = pthread_create(&s_tid_StunTcpTask, NULL, tcp_direct_connect, NULL);
if(ret < 0){
TR069_ERR("Failed to create stun_tcp thread, ret:%d",ret);
return NULL;
}
while(1) {
connected_fd = accept(tr069_server_stun_fd, NULL, 0);
if (connected_fd < 0) {
TR069_ERR("accept socket failed\n");
usleep(1000);
}
else
break;
}
tr069_accept_stun_fd = connected_fd;
// Task main loop
for (;;) {
if (!cpe.stun_enable) {
int len = read(tr069_accept_stun_fd,(void *)&cmd,MAX_STUN_MSG_SIZE);
TR069_DEBUG("STUN re-enable...[%d]",len);
if (cmd) tr069_free(cmd);
cmd = NULL;
} else {
// Clear message if have
//unsigned int timeout = 0;
read(tr069_accept_stun_fd,(void *)&cmd,MAX_STUN_MSG_SIZE);
if (cmd) tr069_free(cmd);
cmd = NULL;
}
if (fd < 0) {
fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd < 0) {
TR069_ERR("Create socket failed(%d)", fd);
continue;
}
timeout_value.tv_sec = wait_connect_timeout;
timeout_value.tv_usec = 0;
//timeout_value={wait_connect_timeout,0};
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_value, sizeof(struct timeval));
if (ret < 0) {
TR069_ERR("Set socket recv timeout failed(%d)", ret);
}
}
#ifdef ADJUEST_SOCKET_TIMEOUT
if (fd > 0 && wait_connect_timeout != cpe.stun_max_period) {
wait_connect_timeout = cpe.stun_max_period ;
timeout_value.tv_sec = wait_connect_timeout;
timeout_value.tv_usec = 0;
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_value, sizeof(struct timeval));
if (ret < 0) {
TR069_ERR("Set socket recv timeout failed(%d)", ret);
}
}
#else
if (fd > 0 && wait_connect_timeout != cpe.stun_max_period / 2) {
stun_timeout = wait_connect_timeout = cpe.stun_max_period / 2;
timeout_value.tv_sec = wait_connect_timeout;
timeout_value.tv_usec = 0;
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_value, sizeof(struct timeval));
if (ret < 0) {
TR069_ERR("Set socket recv timeout failed(%d)", ret);
}
}
#endif
// Do DNS if need
if (ip.sin_port == 0) {
struct sockaddr_in6 addr;
host_entry = gethostbyname(DEFAULT_STUN_SERVER);
if (host_entry == 0) {
TR069_ERR("DNS failed!");
sleep(4);
continue;
}
TR069_DEBUG("Get IP %d.%d.%d.%d", host_entry->h_addr_list[0][0] & 0xff,
host_entry->h_addr_list[0][1] & 0xff,
host_entry->h_addr_list[0][2] & 0xff,
host_entry->h_addr_list[0][3] & 0xff);
ip.sin_addr = * (struct in_addr *) host_entry->h_addr_list[0];
ip.sin_family = AF_INET;
ip.sin_port = htons(3478);
memset(&addr, 0, sizeof(addr));
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(234);
ret = bind(fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_in));
if (ret < 0) {
TR069_ERR("Bind port failed(%d)",ret);
memset(&ip, 0, sizeof(ip));
continue;
}
}
// Build and send STUN message
hdr = (struct stun_hdr *)buf;
hdr->type = htons(BINDING_REQUEST);
hdr->id[0] = 0xdf;
RESET_MSG(&hdr->payload[0]);
SET_U16_VALUE(CHANGE_REQUEST);
SET_U16_VALUE(4);
SET_U32_VALUE(0);
SET_U16_VALUE(CONNECTION_REQUEST_BINDING);
SET_U16_VALUE(((unsigned short int)strlen(CONNECTION_REQUEST_BINDING_STRING)));
SET_STRING_VALUE(CONNECTION_REQUEST_BINDING_STRING);
hdr->len = htons(GET_MSG_LENGTH());
TR069_DEBUG("Send request to STUN server");
ret = sendto(fd, buf, msglen + sizeof(struct stun_hdr), 0, (const struct sockaddr *)&ip, sizeof(ip));
if (ret <= 0) {
TR069_ERR("Socket send failed(%d)", ret);
sleep(4);
// If STUN request send failed, we continue into recv process
// The recv process can help us delay and send request again
}
sent_stun_cmd_state = 2;
#ifdef ADJUEST_SOCKET_TIMEOUT
/*
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &stun_timeout, sizeof(int));
if (ret < 0) {
TR069_ERR("Set socket recv timeout failed(%d)", ret);
}
*/
#endif
// 1) Recieve STUN binding response after our send request
// 2) Wait the solicited request from ACS. The wait timeout is 'stun_max_period', it also have delay function
while (sent_stun_cmd_state) {
memset(buf, 0, BUF_SIZE);
ret = recv(fd, buf, BUF_SIZE-1, 0);
TR069_DEBUG("Socket recv %d", ret);
do {
struct stun_hdr * response = (struct stun_hdr *)buf;
if (ret < 0) break;
if ((unsigned int)ret < sizeof(struct stun_hdr)) {
TR069_ERR("Recv STUN message too short(%d)", ret);
break;
}
switch (htons(response->type)) {
case BINDING_REQUEST:
TR069_DEBUG("Handle binding request...");
break;
case BINDING_RESPONSE:
TR069_DEBUG("Handle binding response...");
if (htons(response->len) + sizeof(struct stun_hdr) < (unsigned int)ret) {
TR069_ERR("Recv STUN message not complete");
break;
}
handle_binding_response(&response->payload[0], (int)htons(response->len));
break;
case BINDING_ERR_RESP:
TR069_DEBUG("Handle binding error response...");
break;
case SHARED_SECRET_REQ:
TR069_DEBUG("Handle shared secret request...");
break;
case SHARED_SECRET_RESP:
TR069_DEBUG("Handle shared secret response...");
break;
case SHARED_SECRET_ERR_RESP:
TR069_DEBUG("Handle shared secret error response...");
break;
default:
// Here is UDP connection request
TR069_DEBUG("Handle ACS UDP connection request...");
handle_udp_request((char *)buf, ret, 0);
break;
}
} while (0);
#ifdef ADJUEST_SOCKET_TIMEOUT
timeout_value.tv_sec = wait_connect_timeout;
timeout_value.tv_usec = 0;
ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout_value, sizeof(struct timeval));
if (ret < 0) {
TR069_ERR("Set socket recv timeout failed(%d)", ret);
}
#endif
sent_stun_cmd_state--;
}
}
}