blob: 573b8feb477107aba1e30cfa50ddbbc3f9806270 [file] [log] [blame]
/************************************************************************
* *
* Netcwmp/Opencwmp Project *
* A software client for enabling TR-069 in embedded devices (CPE). *
* *
* Copyright (C) 2013-2014 netcwmp.netcwmp group *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the *
* Free Software Foundation, Inc., 59 Temple Place, Suite 330, *
* Boston, MA 02111-1307 USA *
* *
* Copyright 2013-2014 Mr.x(Mr.x) <netcwmp@gmail.com> *
* *
***********************************************************************/
#include <cwmp/http.h>
#include <cwmp/event.h>
#include "cwmp_httpd.h"
#define MAX_CLIENT_NUMS 8
static char * AuthRealm = "cwmpd";
static char * AuthQop = "auth";
static char AuthOpaque[33] = {0};
static int AuthNonce = 0;
const char * RESPONSE_200 = "HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Length: 2\r\nContent-Type: text/xml; charset=\"utf-8\"\r\n\r\nOK";
const char * RESPONSE_400 = "HTTP/1.1 400 Bad request\r\nServer: CWMP-Agent\r\nConnection: close\r\nContent-Length: 5\r\n\r\nError";
//const char * RESPONSE_401 = "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Digest qop=\"%s\" nonce=\"%s\" opaque=\"%s\" realm=\"%s\"\r\nServer: TR069Agent\r\nContent-Length: 0\r\n\r\n";
const char * RESPONSE_401 = "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Digest realm=\"%s\", nonce=\"%s\", qop=\"%s\", opaque=\"%s\"\r\nServer: TR069Agent\r\nContent-Length: 0\r\n\r\n";
struct http_session_fd_t
{
//int fd;
time_t time;
http_socket_t * sock;
};
struct http_session_fd_t sessionfd[MAX_CLIENT_NUMS];
typedef enum
{
PDP_TYPE_UNINIT = -1,
PDP_TYPE_IPV4 = 0,
PDP_TYPE_IPV6,
PDP_TYPE_IPV4V6,
PDP_TYPE_UNDEFINE
}Pdp_Type;
void setnonblocking(int fd)
{
#ifdef WIN32
#else
int opts;
opts=fcntl(fd, F_GETFL);
if (opts < 0)
{
cwmp_log_error("setnonblocking fcntl GETFL failed: fd(%d)\n", fd);
return;
}
opts = opts | O_NONBLOCK;
if (fcntl(fd, F_SETFL, opts) < 0)
{
cwmp_log_error("setnonblocking fcntl SETFL failed: fd(%d)\n", fd);
return;
}
return;
#endif
}
int httpd_response_unauthorization(http_socket_t * sock)
{
char buffer[256];
char nonce[33];
FUNCTION_TRACE();
AuthNonce ++;
TRsnprintf(buffer, 256, "%d", AuthNonce);
cwmp_MD5(nonce, buffer, NULL);
nonce[32] = 0;
//TRsnprintf(buffer, 256, RESPONSE_401, AuthQop, nonce, AuthOpaque, AuthRealm);
TRsnprintf(buffer, 256, RESPONSE_401, AuthRealm, nonce, AuthQop, AuthOpaque);
return http_socket_write(sock, buffer, TRstrlen(buffer));
}
int httpd_response_ok(http_socket_t * sock)
{
FUNCTION_TRACE();
return http_socket_write(sock, RESPONSE_200, TRstrlen(RESPONSE_200));
}
int httpd_response_unkonw_error(http_socket_t * sock)
{
FUNCTION_TRACE();
return http_socket_write(sock, RESPONSE_400, TRstrlen(RESPONSE_400));
}
static int g_s_pdp_type = PDP_TYPE_UNINIT;
static int check_ipv6_addr()
{
char ipv6_addr[128] = {0};
sc_cfg_get("ipv6_br0_addr", ipv6_addr, sizeof(ipv6_addr));
if(strcmp(ipv6_addr, "") == 0)
{
cwmp_log_info("ipv6_addr is NULL");
return CWMP_ERROR;
}
return CWMP_OK;
}
int init_pdp_type(cwmp_t * cwmp)
{
char pdp_type[32] = {0};
int i = 0;
sc_cfg_get("pdp_type", pdp_type, sizeof(pdp_type));
if(strncmp(pdp_type, "IPv4v6", sizeof(pdp_type)) == 0)
{
while(1)
{
if(check_ipv6_addr() == CWMP_OK && http_get_host_by_url(cwmp->acs_url, AF_INET6) == CWMP_OK)
{
g_s_pdp_type = PDP_TYPE_IPV6;
return g_s_pdp_type;
}
if(http_get_host_by_url(cwmp->acs_url, AF_INET) == CWMP_OK)
{
g_s_pdp_type = PDP_TYPE_IPV4;
return g_s_pdp_type;
}
cwmp_log_info("gethostbyname return NULL");
sleep(1);
}
}
else if(strncmp(pdp_type, "IPv6", sizeof(pdp_type)) == 0)
{
while(check_ipv6_addr() != CWMP_OK)
{
cwmp_log_info("IPV6 addr return NULL");
sleep(1);
}
while(1)
{
if(http_get_host_by_url(cwmp->acs_url, AF_INET6) != CWMP_OK)
{
cwmp_log_info("gethostbyname return NULL");
sleep(1);
}
else
{
g_s_pdp_type = PDP_TYPE_IPV6;
cwmp_log_info("pdp_type:%s, s_pdp_type:%d", pdp_type, g_s_pdp_type);
return g_s_pdp_type;
}
}
// g_s_pdp_type = PDP_TYPE_UNDEFINE;
}
else if(strncmp(pdp_type, "IP", sizeof(pdp_type)) == 0)
{
g_s_pdp_type = PDP_TYPE_IPV4;
while(http_get_host_by_url(cwmp->acs_url, AF_INET) != CWMP_OK)
{
cwmp_log_info("gethostbyname return NULL");
sleep(1);
}
}
else
{
g_s_pdp_type = PDP_TYPE_UNDEFINE;
}
cwmp_log_info("pdp_type:%s, s_pdp_type:%d", pdp_type, g_s_pdp_type);
return g_s_pdp_type;
}
static int get_pdp_type()
{
return g_s_pdp_type;
}
int get_family_type()
{
int family = -1;
while(get_pdp_type() == PDP_TYPE_UNINIT){
cwmp_log_info("pdp type is not init, waiting");
sleep(1);
}
if(get_pdp_type() == PDP_TYPE_IPV6)
{
family = AF_INET6;
}
else if(get_pdp_type() == PDP_TYPE_IPV4)
{
family = AF_INET;
}
else
{
cwmp_log_error("unknown pdp type:%d", g_s_pdp_type);
}
return family;
}
int httpd_build_server(cwmp_t * cwmp)
{
http_socket_t * lsnsock;
pool_t * pool;
int rc;
int lsnfd, maxfd, nready;
int i;
int fd, newfd;
http_socket_t * s;
http_request_t * request;
char * auth;
time_t now;
fd_set readset, rdset;
struct timeval timeout;
int port;
char cpe_user[INI_BUFFERSIZE] = {0};
char cpe_pwd[INI_BUFFERSIZE] = {0};
FUNCTION_TRACE();
port = cwmp->httpd_port;
pool = pool_create(POOL_DEFAULT_SIZE);
if(!pool)
{
cwmp_log_error("pool create return null");
return CWMP_ERROR;
}
rc = http_socket_server(&lsnsock, port, 5, -1, pool);
if (rc != CWMP_OK)
{
cwmp_log_error("build httpd server faild. %s\n", strerror(errno));
exit(-1);
}
lsnfd = http_socket_get_fd(lsnsock);
for (i=0; i < MAX_CLIENT_NUMS; i++)
{
sessionfd[i].time = 0;
sessionfd[i].sock = NULL;
}
FD_ZERO(&readset);
FD_SET(lsnfd, &readset);
maxfd = lsnfd;
/*maxi = -1;*/
while (1)
{
FD_ZERO(&rdset);
rdset = readset;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
if ((nready = select(maxfd + 1, &rdset, NULL, NULL, &timeout)) <= 0)
{
sleep(1);
cwmp_log_info("select new connection timeout. no new request.\n");
now = time(NULL);
for (i=0; i<MAX_CLIENT_NUMS; i++)
{
//cwmp_log_debug("socket time: %d, timeout %d, fd is %d\n", sessionfd[i].time, now - sessionfd[i].time,
// sessionfd[i].sock == NULL? -1 : http_socket_get_fd(sessionfd[i].sock));
fd = http_socket_get_fd(sessionfd[i].sock);
if ((sessionfd[i].sock != NULL) && (now - sessionfd[i].time > 15))
{
cwmp_log_info("close a timeout socket. fd is %d.\n", fd);
FD_CLR(fd, &readset);
//http_socket_close(sessionfd[i].sock);
http_socket_destroy(sessionfd[i].sock);
sessionfd[i].time = 0;
sessionfd[i].sock = NULL;
}
}
continue;
}
cwmp_log_info("select info.....\n");
if (FD_ISSET(lsnfd, &rdset))
{
http_socket_t * newsock;
//FIXME
rc = http_socket_accept(lsnsock, &newsock);
if(CWMP_OK != rc)
{
cwmp_log_error("http_socket_accept return error");
continue;
}
newfd = http_socket_get_fd(newsock);
for (i=0; i<MAX_CLIENT_NUMS; i++)
{
if (sessionfd[i].sock == NULL)
{
sessionfd[i].sock = newsock;
sessionfd[i].time = time(NULL);
break;
}
}
if (i == MAX_CLIENT_NUMS)
{
//http_socket_close(newsock);
http_socket_destroy(newsock);
cwmp_log_error("too many ACS request connection");
continue;
}
FD_SET(newfd, &readset);
if (newfd > maxfd)
{
maxfd = newfd;
}
// newfd = -1;
if (--nready <= 0)
{
continue;
}
}
//readpool = pool_create(POOL_DEFAULT_SIZE);
cwmp_log_debug("nready is %d.\n", nready);
for (i=0; (i<MAX_CLIENT_NUMS) && (nready > 0) ; i++)
{
s = sessionfd[i].sock;
fd = http_socket_get_fd(s);
if ((fd != -1) && FD_ISSET(fd, &rdset))
{
nready--;
sessionfd[i].time = time(NULL);
pool_t * socket_pool = http_socket_get_pool(s);
if (NULL == socket_pool)
{
cwmp_log_error("http_socket_get_pool return null");
httpd_response_unkonw_error(s);
goto faild;
}
rc = http_request_create(&request, socket_pool);
if (CWMP_OK != rc)
{
cwmp_log_error("http_request_create rc:%d", rc);
httpd_response_unkonw_error(s);
goto faild;
}
socket_pool = http_socket_get_pool(s);
if (NULL == socket_pool)
{
cwmp_log_error("http_socket_get_pool return null");
httpd_response_unkonw_error(s);
goto faild;
}
rc = http_read_request(s, request, socket_pool);
if (rc <= 0)
{
cwmp_log_error("http_read_request rc:%d", rc);
httpd_response_unkonw_error(s);
goto faild;
}
if (request->method != HTTP_GET)
{
cwmp_log_error("request->method:%d (!= HTTP_GET)", request->method);
httpd_response_unkonw_error(s);
goto faild;
}
cwmp_log_info("cwmp->cpe_auth:%d", cwmp->cpe_auth);
if (cwmp->cpe_auth)
{
auth = http_get_variable(request->parser, "Authorization");
if (!auth)
{
httpd_response_unauthorization(s);
cwmp_log_debug("auth fail. auth=%s\n", auth);
goto faild;
}
// may be changed, so get new instead of get cwmp->cpe_user, cwmp->cpe_pwd
cwmp_conf_get("cwmp:cpe_username", cpe_user);
cwmp_conf_get("cwmp:cpe_password", cpe_pwd);
cwmp_log_debug("cpe username: %s, cpe password: %s\n", cpe_user, cpe_pwd);
if (http_check_digest_auth(AuthRealm, auth, cpe_user, cpe_pwd) != 0)
{
httpd_response_unauthorization(s);
goto faild;
}
}
httpd_response_ok(s);
//get a new request from acs
cwmp->new_request = CWMP_YES;
cwmp_log_debug("set cwmp new request to %d\n", cwmp->new_request);
cwmp_event_set_value(cwmp, INFORM_CONNECTIONREQUEST, 1, NULL, 0, 0, 0);
faild:
FD_CLR(fd, &readset);
sessionfd[i].time = 0;
sessionfd[i].sock = NULL;
http_socket_destroy(s);
}
}
}
}