blob: e4aed5e741928f3ec01acc698392e0c407c41ebe [file] [log] [blame]
/************************************************************************
* Id: http.c *
* *
* TR069 Project: A TR069 library in C *
* Copyright (C) 2013-2014 netcwmp.netcwmp group *
* *
* *
* Email: netcwmp ( & ) gmail dot com *
* *
***********************************************************************/
#include "cwmp/http.h"
#include "cwmp/log.h"
#include "cwmp_private.h"
#include <cwmp/md5.h>
#include <errno.h>
#include <netdb.h>
#include <sys/socket.h>
struct http_sockaddr_t
{
/** IPv6 sockaddr structure */
struct sockaddr_in6 sin6;
};
int http_read_body_chunked(http_socket_t * sock);
/*********************************************************************/
const char base[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
static char* base64_encode(const char* data, int data_len);
static char *base64_decode(const char* data, int data_len);
static char find_pos(char ch);
/* */
static char *base64_encode(const char* data, int data_len)
{
//int data_len = strlen(data);
int prepare = 0;
int ret_len;
int temp = 0;
char *ret = NULL;
char *f = NULL;
int tmp = 0;
char changed[4];
int i = 0;
ret_len = data_len / 3;
temp = data_len % 3;
if (temp > 0)
{
ret_len += 1;
}
ret_len = ret_len*4 + 1;
ret = (char *)malloc(ret_len);
if ( ret == NULL)
{
printf("No enough memory.\n");
return NULL;
}
memset(ret, 0, ret_len);
f = ret;
while (tmp < data_len)
{
temp = 0;
prepare = 0;
memset(changed, '\0', 4);
while (temp < 3)
{
//printf("tmp = %d\n", tmp);
if (tmp >= data_len)
{
break;
}
prepare = ((prepare << 8) | (data[tmp] & 0xFF));
tmp++;
temp++;
}
prepare = (prepare<<((3-temp)*8));
//printf("before for : temp = %d, prepare = %d\n", temp, prepare);
for (i = 0; i < 4 ;i++ )
{
if (temp < i)
{
changed[i] = 0x40;
}
else
{
changed[i] = (prepare>>((3-i)*6)) & 0x3F;
}
*f = base[changed[i]];
//printf("%.2X", changed[i]);
f++;
}
}
*f = '\0';
return ret;
}
/* */
static char find_pos(char ch)
{
char *ptr = (char*)strrchr(base, ch);//the last position (the only) in base[]
return (ptr - base);
}
/* */
static char *base64_decode(const char *data, int data_len)
{
int ret_len = (data_len / 4) * 3;
int equal_count = 0;
char *ret = NULL;
char *f = NULL;
int tmp = 0;
int temp = 0;
char need[3];
int prepare = 0;
int i = 0;
if (*(data + data_len - 1) == '=')
{
equal_count += 1;
}
if (*(data + data_len - 2) == '=')
{
equal_count += 1;
}
if (*(data + data_len - 3) == '=')
{//seems impossible
equal_count += 1;
}
switch (equal_count)
{
case 0:
ret_len += 4;//3 + 1 [1 for NULL]
break;
case 1:
ret_len += 4;//Ceil((6*3)/8)+1
break;
case 2:
ret_len += 3;//Ceil((6*2)/8)+1
break;
case 3:
ret_len += 2;//Ceil((6*1)/8)+1
break;
}
ret = (char *)malloc(ret_len);
if (ret == NULL)
{
printf("No enough memory.\n");
return NULL;
}
memset(ret, 0, ret_len);
f = ret;
while (tmp < (data_len - equal_count))
{
temp = 0;
prepare = 0;
memset(need, 0, sizeof(need));
while (temp < 4)
{
if (tmp >= (data_len - equal_count))
{
break;
}
prepare = (prepare << 6) | (find_pos(data[tmp]));
temp++;
tmp++;
}
prepare = prepare << ((4-temp) * 6);
for (i=0; i<3 ;i++ )
{
if (i == temp)
{
break;
}
*f = (char)((prepare>>((2-i)*8)) & 0xFF);
f++;
}
}
*f = '\0';
return ret;
}
/*********************************************************************/
char * http_get_variable(http_parser_t * parser, const char *name)
{
int i;
for (i=0; i<parser->count && i<MAX_HEADERS; i++)
{
if (TRstrcasecmp(parser->header[i]->name, name) == 0)
{
return parser->header[i]->value;
}
}
return NULL;
}
void http_set_variable(http_parser_t *parser, const char *name, const char *value, pool_t * pool)
{
key_value_t *var;
//FUNCTION_TRACE();
if (name == NULL || value == NULL)
return;
var = (key_value_t *)pool_pcalloc(pool, sizeof(key_value_t));
if (var == NULL)
{
return;
}
var->name = pool_pstrdup_lower(pool, name);
var->value = pool_pstrdup(pool, value);
if (parser->count >= MAX_HEADERS)
{
return;
}
parser->header[parser->count++] = var;
}
int http_connect(http_socket_t * sock, const char * url)
{
return 0;
}
int http_dest_create(http_dest_t ** dest, const char * url, pool_t * pool)
{
http_dest_t * d = (http_dest_t*)pool_pcalloc(pool, sizeof(http_dest_t));
if(NULL == d)
{
cwmp_log_error("d pool_pcalloc return null");
return CWMP_ERROR;
}
// cwmp_uint32_t length = TRstrlen(url);
cwmp_log_info("url:%s", url);
http_parse_url(d, url);
d->url = pool_pstrdup(pool, url);
if(NULL == d->url)
{
cwmp_log_error("d->url pool_pstrdup is null, url:%s", url);
return CWMP_ERROR;
}
// d->url = (char *)pool_pcalloc(pool, length+1);
// strncpy(d->url, url, length);
cwmp_log_debug("dest create url is %s", d->url);
*dest = d;
return CWMP_OK;
}
void http_sockaddr_set(http_sockaddr_t * addr, int family, int port, const char * hostname)
{
FUNCTION_TRACE();
if (hostname)
{
cwmp_log_info("host name is:%s", hostname);
struct sockaddr_in sa;
char *ptr,**pptr;
char str[256];
struct hostent *host = gethostbyname2(hostname, family);
if(!host){
cwmp_log_error("Get IP address error, host name:%s!", hostname);
return;
}
switch(host->h_addrtype)
{
case AF_INET:
pptr=host->h_addr_list;
inet_ntop(host->h_addrtype, *pptr, str, sizeof(str));
cwmp_log_info("ip is:%s", str);
((struct sockaddr_in *)&(addr->sin6))->sin_addr.s_addr = inet_addr(str);
((struct sockaddr_in*)&(addr->sin6))->sin_family = AF_INET;
((struct sockaddr_in*)&(addr->sin6))->sin_port = htons((unsigned short)port);
break;
case AF_INET6:
pptr=host->h_addr_list;
inet_ntop(host->h_addrtype, *pptr, str, sizeof(str));
cwmp_log_info("ip6 is:%s", str);
{
int ret = inet_pton(AF_INET6, str, (void*)&addr->sin6.sin6_addr);
if(ret <= 0)//cov
{
cwmp_log_error("http_sockaddr_set inet_pton6 error!\n");
}
}
cwmp_log_info("ip6 is:%s", str);
addr->sin6.sin6_port = htons((unsigned short)port);
addr->sin6.sin6_family = AF_INET6;
break;
default:
cwmp_log_error("unknown address type\n");
break;
}
}
else
{
cwmp_log_error("null hostname\n");
if(family == AF_INET6)
{
addr->sin6.sin6_port = htons((unsigned short)(port&0xFFFF));
addr->sin6.sin6_family = family;
}
else if(family == AF_INET)
{
((struct sockaddr_in *)&(addr->sin6))->sin_port = htons((unsigned short)(port&0xFFFF));
((struct sockaddr_in *)&(addr->sin6))->sin_family = family;
}
else
{
cwmp_log_error("unknown family type:%d", family);
}
}
}
int http_socket_calloc(http_socket_t **news, pool_t * pool)
{
(*news) = (http_socket_t *)pool_pcalloc(pool, sizeof(http_socket_t));
if ((*news) == NULL)
{
cwmp_log_error("socket create pool pcalloc null.\n");
return CWMP_ERROR;
}
(*news)->addr = (http_sockaddr_t*)pool_pcalloc(pool, sizeof(http_sockaddr_t));
if ((*news)->addr == NULL)
{
(*news) = NULL;
cwmp_log_error("http_sockaddr_t pool pcalloc null.\n");
return CWMP_ERROR;
}
(*news)->sockdes = -1;
(*news)->timeout = -1;
(*news)->pool = pool;
pool_cleanup_add(pool, (pool_cleanup_handler)http_socket_close, (*news));
return CWMP_OK;
}
int http_socket_create(http_socket_t **news, int family, int type, int protocol, pool_t * pool)
{
int stat;
stat = http_socket_calloc(news, pool);
if (stat == CWMP_ERROR)
{
return CWMP_ERROR;
}
(*news)->sockdes = socket(family, type, protocol);
#if HAVE_IPV6
if ((*news)->sockdes == -1)
{
cwmp_log_error(" have ipv6");
family = AF_INET;
(*news)->sockdes = socket(family, type, protocol);
}
#endif
if ((*news)->sockdes == -1)
{
cwmp_log_error("sockdes is -1.\n");
return - errno;
}
(*news)->type = type;
(*news)->protocol = protocol;
http_sockaddr_set((*news)->addr,family, 0, NULL);
(*news)->timeout = -1;
return CWMP_OK;
}
int http_socket_server (http_socket_t **news, int port, int backlog, int timeout, pool_t * pool)
{
int i;
http_socket_t * sock;
int rc;
int family = get_family_type();
if(family == -1)
{
cwmp_log_error("family error:%d.", family);
return CWMP_ERROR;
}
rc = http_socket_create(&sock, family, SOCK_STREAM, 0, pool);
if (rc != CWMP_OK)
{
cwmp_log_error("http_socket_create faild. %s", strerror(errno));
return CWMP_ERROR;
}
i = 1;
if (setsockopt (sock->sockdes, SOL_SOCKET, SO_REUSEADDR, (void *)&i, sizeof(i)) == -1)
{
cwmp_log_error ("http_socket_server: setsockopt SO_REUSEADDR: %sock", strerror (errno));
}
http_sockaddr_set(sock->addr, family, port, NULL);
if(family == AF_INET)
{
rc = bind (sock->sockdes, (struct sockaddr *)sock->addr, sizeof (struct sockaddr_in));
}
else if(family == AF_INET6)
{
rc = bind (sock->sockdes, (struct sockaddr *)sock->addr, sizeof (struct sockaddr_in6));
}
if (rc == -1)
{
cwmp_log_error ("http_socket_server: bind fail: %s", strerror (errno));
http_socket_close (sock);
return CWMP_ERROR;
}
if (listen (sock->sockdes, (unsigned)backlog) == -1)
{
http_socket_close (sock);
cwmp_log_error ("http_socket_server: listen fail: %s", strerror (errno));
return CWMP_ERROR;
}
*news = sock;
return CWMP_OK;
}
int http_socket_connect(http_socket_t * sock, int family, const char * host, int port)
{
int rc = 0;
http_sockaddr_set(sock->addr, family, port, host);
if(family == AF_INET)
{
rc = connect(sock->sockdes, (const struct sockaddr *)&sock->addr->sin6,
sizeof(struct sockaddr_in));
}
else if(family == AF_INET6)
{
rc = connect(sock->sockdes, (const struct sockaddr *)&sock->addr->sin6,
sizeof(struct sockaddr_in6));
}
else
{
cwmp_log_error("undefine pdp type. %d", family);
return CWMP_ERROR;
}
if (rc == -1)
{
cwmp_log_error("connect error no:%d(%s)\n", errno, strerror(errno));
return CWMP_ERROR;
}
return CWMP_OK;
}
int http_socket_accept(http_socket_t *sock, http_socket_t ** news)
{
struct sockaddr addr4 = {0}; //cov
struct sockaddr_in6 addr6 = {0}; //cov
size_t len;
pool_t * pool;
int rc, s;
cwmp_log_debug("TRACE: socket_tcp_accept\n");
int family = get_family_type();
if(family == AF_INET)
{
len = sizeof(addr4);
s = accept (sock->sockdes, &addr4, &len);
}
else if(family == AF_INET6)
{
len = sizeof(addr6);
s = accept (sock->sockdes, &addr6, &len);
}
else
{
cwmp_log_error("undefine family type. %d", family);
return CWMP_ERROR;
}
if (s == -1)
{
cwmp_log_error("accept return -1");
return CWMP_ERROR;
}
pool = pool_create(POOL_DEFAULT_SIZE);
if(NULL == pool)
{
cwmp_log_error("pool create return null, size: POOL_DEFAULT_SIZE");
close(s);
return CWMP_ERROR;
}
rc = http_socket_calloc(news, pool);
if (rc != CWMP_OK)
{
cwmp_log_error("http_socket_calloc return ERROR;");
close(s);
return CWMP_ERROR;
}
(*news)->sockdes = s;
if(family == AF_INET)
{
memcpy(&(*news)->addr->sin6, &addr4, sizeof(struct sockaddr_in));
}
else if(family == AF_INET6)
{
memcpy(&(*news)->addr->sin6, &addr6, sizeof(struct sockaddr_in6));
}
return CWMP_OK;
}
void http_socket_close(http_socket_t * sock)
{
FUNCTION_TRACE();
if (sock)
{
if (sock->sockdes != -1)
{
#ifdef WIN32
closesocket(sock->sockdes);
#else
close(sock->sockdes);
#endif
sock->sockdes = -1;
}
}
}
void http_socket_destroy(http_socket_t * sock)
{
pool_t * pool;
pool = sock->pool;
pool_destroy(pool);
}
int http_socket_get_fd(http_socket_t * sock)
{
if (sock)
return sock->sockdes;
else
return -1;
}
pool_t * http_socket_get_pool(http_socket_t * sock)
{
if(sock && sock->pool)
{
return sock->pool;
}
else
{
return NULL;
}
}
int http_socket_read (http_socket_t * sock, char *buf, int bufsize)
{
int res = 0;
if(sock->use_ssl)
{
#ifdef USE_CWMP_OPENSSL
do
{
res = SSL_read(sock->ssl, buf, bufsize);
}
while (res == -1 && errno == EINTR);
#endif
return res;
}
else
{
do
{
res = recv (sock->sockdes, buf, bufsize, 0);
}
while (res == -1 && errno == EINTR);
return res;
}
}
int http_socket_write (http_socket_t * sock, const char *buf, int bufsize)
{
int res = 0;
if(sock->use_ssl)
{
cwmp_log_debug("http socket ssl write buffer: %s, length: %d", buf, bufsize);
#ifdef USE_CWMP_OPENSSL
do
{
res = SSL_write (sock->ssl, buf, bufsize);
}
while (res == -1 && errno == EINTR);
#endif
return res;
}
else
{
cwmp_log_debug("http socket write buffer fd:%d, length:%d, [\n%s\n]", sock->sockdes, bufsize, buf);
do
{
res = send (sock->sockdes, buf, bufsize, 0);
}while (res == -1 && errno == EINTR);
return res;
}
}
void http_socket_set_sendtimeout(http_socket_t * sock, int timeout)
{
struct timeval to;
to.tv_sec = timeout;
to.tv_usec = 0;
sock->timeout = timeout;
if(0 != setsockopt(sock->sockdes, SOL_SOCKET, SO_SNDTIMEO,
(char *) &to,
sizeof(to)))
{
cwmp_log_error("setsockopt send timeout fail!");
}
}
void http_socket_set_recvtimeout(http_socket_t * sock, int timeout)
{
struct timeval to;
to.tv_sec = timeout;
to.tv_usec = 0;
sock->timeout = timeout;
if(0 != setsockopt(sock->sockdes, SOL_SOCKET, SO_RCVTIMEO,
(char *) &to,
sizeof(to)))
{
cwmp_log_error("setsockopt recv timeout fail!");
}
}
int http_socket_set_writefunction(http_socket_t * sock, http_write_callback_pt callback, void * calldata)
{
if(!sock)
{
return CWMP_ERROR;
}
sock->write_callback = callback;
sock->write_calldata = calldata;
return CWMP_OK;
}
int http_request_create(http_request_t ** request , pool_t * pool)
{
http_request_t * req;
req = (http_request_t*)pool_pcalloc(pool, sizeof(http_request_t));
if(NULL == req)
{
cwmp_log_error("req pool pcalloc return fail");
return CWMP_ERROR;
}
req->parser = (http_parser_t*)pool_pcalloc(pool, sizeof(http_parser_t));
if(NULL == req->parser)
{
cwmp_log_error("req pool pcalloc return fail");
return CWMP_ERROR;
}
*request = req;
return CWMP_OK;
}
int http_response_create(http_response_t ** response, pool_t * pool)
{
http_response_t * res;
res = (http_response_t*)pool_pcalloc(pool, sizeof(http_response_t));
if(NULL == res)
{
cwmp_log_error("res pool_pcalloc return NULL");
*response = res;
return CWMP_ERROR;
}
res->parser = (http_parser_t*)pool_pcalloc(pool, sizeof(http_parser_t));
if(NULL == res->parser)
{
cwmp_log_error("res->parser pool_pcalloc return NULL");
*response = res;
return CWMP_ERROR;
}
*response = res;
return CWMP_OK;
}
int http_parse_cookie(const char * cookie, char * dest_cookie)
{
char data[MIN_BUFFER_LEN+1] = {0};
char * s ;
char buffer[128];
char * end;
FUNCTION_TRACE();
if (!cookie)
return CWMP_ERROR;
for (s = (char*)cookie; isspace(*s); s++);
TRstrncpy(dest_cookie, s, MIN_BUFFER_LEN);
return CWMP_OK;
}
void http_parse_key_value(char ** from, char *to, int len, int shift)
{
int n;
char fmt[20];
char *p = *from + shift;
*from = p;
if (*p == '"')//notice that '"' is not two " ,but ' and " and ',Jeff Sun - Jul.24.2005
{
TRsnprintf(fmt, sizeof(fmt), "%%%d[^\"]%%n", len - 1);
p++;
}
else
{
TRsnprintf(fmt, sizeof(fmt), "%%%d[^ \t,]%%n", len - 1);
}
if (sscanf(p, fmt, to, &n))
{
if(n > strlen(p))
{
cwmp_log_info("n is out of string len");
return;
}
p += n;
*from = p;
}
}
int http_parse_url(http_dest_t * dest, const char * url)
{
char *d;
const char *p, *q;
const char * uri;
int i;
/* allocate struct url */
//char urlbuf[1024] = {0};
//strncpy(urlbuf, url, strlen(url));
FUNCTION_TRACE();
cwmp_log_info("url:%s", url);
uri = url;
/* scheme name */
if ((p = strstr(url, ":/")))
{
TRsnprintf(dest->scheme, URL_SCHEME_LEN+1,
"%.*s", (int)(p - uri), uri);
uri = ++p;
/*
* Only one slash: no host, leave slash as part of document
* Two slashes: host follows, strip slashes
*/
if (uri[1] == '/')
uri = (p += 2);
}
else
{
p = uri;
}
if (!*uri || *uri == '/' || *uri == '.')
goto nohost;
p = strpbrk(uri, "/@");
if (p && *p == '@')
{
/* username */
for (q = uri, i = 0; (*q != ':') && (*q != '@'); q++)
if (i < URL_USER_LEN)
{
dest->user[i++] = *q;
}
/* password */
if (*q == ':')
for (q++, i = 0; (*q != ':') && (*q != '@'); q++)
if (i < URL_PWD_LEN)
{
dest->password[i++] = *q;
}
p++;
}
else
{
p = uri;
}
/* hostname */
//#ifdef INET6
if (*p == '[' && (q = strchr(p + 1, ']')) != NULL &&
(*++q == '\0' || *q == '/' || *q == ':'))
{
if ((i = q - p - 2) > MAX_HOST_NAME_LEN)
{
i = MAX_HOST_NAME_LEN;
}
memset(dest->host, 0, sizeof(dest->host));
memcpy(dest->host, ++p, i);
p = q;
}
else
//#endif
{
memset(dest->host, 0, MAX_HOST_NAME_LEN+1);
for (i = 0; *p && (*p != '/') && (*p != ':'); p++)
if (i < MAX_HOST_NAME_LEN)
{
dest->host[i++] = *p;
}
}
/* port */
if(strncmp(url, "https:", 6) == 0)
{
dest->port = 443;
}
else
{
dest->port = 80;
}
if (*p == ':')
{
dest->port = 0;
for (q = ++p; *q && (*q != '/'); q++)
if (isdigit(*q))
dest->port = dest->port * 10 + (*q - '0');
else
{
/* invalid port */
goto outoff;
}
p = q;
}
nohost:
/* document */
if (!*p)
p = "/";
if (TRstrcasecmp(dest->scheme, "http") == 0 ||
TRstrcasecmp(dest->scheme, "https") == 0)
{
const char hexnums[] = "0123456789abcdef";
d = dest->uri;
while (*p != '\0')
{
if (!isspace(*p))
{
*d++ = *p++;
}
else
{
*d++ = '%';
*d++ = hexnums[((unsigned int)*p) >> 4];
*d++ = hexnums[((unsigned int)*p) & 0xf];
p++;
}
}
*d = '\0';
}
else
{
//strncpy(d, p, MAX_URI_LEN);
}
cwmp_log_debug(
"scheme: [%s]\n"
"user: [%s]\n"
"password: [%s]\n"
"host: [%s]\n"
"port: [%d]\n"
"uri: [%s]\n",
dest->scheme, dest->user, dest->password,
dest->host, dest->port, dest->uri);
return CWMP_OK;
outoff:
cwmp_log_error("parse url error.\n");
return CWMP_ERROR;
}
int http_get_host_type_by_url(char *url)
{
http_dest_t d= {0};
http_parse_url(&d, url);
cwmp_log_info("url:%s, host:%s", url, d.host);
struct hostent *host = NULL;
host = gethostbyname2(d.host, AF_INET6);
cwmp_log_info("AF_INET6:%p", host);
if(host)
{
return AF_INET6;
}
host = gethostbyname2(d.host, AF_INET);
cwmp_log_info("AF_INET4:%p", host);
if(host)
{
return AF_INET;
}
return -1;
}
int http_get_host_by_url(char *url, int family)
{
http_dest_t d= {0};
http_parse_url(&d, url);
cwmp_log_info("url:%s, host:%s", url, d.host);
struct hostent *host = NULL;
host = gethostbyname2(d.host, family);
if(host)
{
return CWMP_OK;
}
return CWMP_ERROR;
}
static int http_split_headers(char *data, unsigned long len, char **line)
{
int lines = 0;
unsigned long i;
//FUNCTION_TRACE();
line[lines] = data;
for (i = 0; i < len && lines < MAX_HEADERS; i++)
{
if (data[i] == '\r')
{
data[i] = '\0';
}
if (data[i] == '\n')
{
lines++;
data[i] = '\0';
if (lines >= MAX_HEADERS)
{
return MAX_HEADERS;
}
if (i + 1 < len)
{
if (data[i + 1] == '\n' || data[i + 1] == '\r')
{
break;
}
line[lines] = &data[i + 1];
}
}
}
i++;
while (i < len && data[i] == '\n') i++;
return lines;
}
static void http_parse_headers(http_parser_t * parser, char **line, int lines, pool_t * pool)
{
int i,l;
int whitespace, where, slen;
char *name = NULL;
char *value = NULL;
//FUNCTION_TRACE();
/* parse the name: value lines. */
for (l = 1; l < lines; l++)
{
where = 0;
whitespace = 0;
name = line[l];
value = NULL;
slen = strlen(line[l]);
for (i = 0; i < slen; i++)
{
if (line[l][i] == ':')
{
whitespace = 1;
line[l][i] = '\0';
}
else
{
if (whitespace)
{
whitespace = 0;
while (i < slen && line[l][i] == ' ')
i++;
if (i < slen)
value = &line[l][i];
break;
}
}
}
if (name != NULL && value != NULL)
{
http_set_variable(parser, name, value, pool);
name = NULL;
value = NULL;
}
}
}
int http_read_line(http_socket_t * sock, char * buffer, int max)
{
char c;
int i=0;
while (i < max)
{
if ( http_socket_read(sock, &c, 1) <= 0 )
{
cwmp_log_error("recv, CANNOT READ 1 char");
return CWMP_ERROR;
};
buffer[i++]=c;
if (c=='\r') // GOT CR
{
if ( http_socket_read(sock, &c, 1) < 0 )
{
cwmp_log_error("http_socket_read fail");
return CWMP_ERROR;
};
buffer[i++]=c;
break ;
}
}
if (i >= max)
{
cwmp_log_error("i(%d) > max(%d)", i, max);
cwmp_log_info("alread read info:%s", buffer);
return CWMP_ERROR;
}
buffer[i] = 0;
return i;
}
int http_read_header(http_socket_t * sock, cwmp_chunk_t * header, pool_t * pool)
{
char buffer[1024];
int rc, bytes;
FUNCTION_TRACE();
bytes = 0;
for (;;)
{
rc = http_read_line(sock, buffer, 1023);
if (rc < 0)
{
cwmp_log_error("http_read_line rc:%d", rc);
return rc;
}
buffer[rc] = 0;
//cwmp_log_debug("%s", buffer);
cwmp_chunk_write_string(header, buffer, rc, pool);
bytes += rc;
if (buffer[0] == '\r' && buffer[1] == '\n')
{
break;
}
}
return bytes;
}
int http_read_body(http_socket_t * sock, int max)//, cwmp_chunk_t * body, pool_t * pool)
{
int bytes = 0;
int len;
char buffer[512];
int read_len = 0;
FUNCTION_TRACE();
cwmp_log_info("max len:%d", max);
cwmp_log_info("-------------------------");
while (bytes < max)
{
read_len = (max-bytes) >= 512 ? 512 : (max-bytes);
len = http_socket_read(sock, buffer, read_len);
if ( len < 0 )
{
cwmp_log_error("recv, CANNOT READ 512 chars");
return CWMP_ERROR;
}
else if (len == 0)
{
return bytes;
}
//memcpy(b, buffer, len);
if(sock->write_callback)
{
(*sock->write_callback)(buffer, 1, len, sock->write_calldata);
}
else
{
cwmp_log_info("%s", buffer);
}
bytes += len;
}
cwmp_log_info("-------------------------");
cwmp_log_info("read len:%d", bytes);
return bytes;
}
int http_read_request(http_socket_t * sock, http_request_t * request, pool_t * pool)
{
int rc;
cwmp_chunk_t * header;
char *line[MAX_HEADERS]; /* limited to 64 lines, should be more than enough */
int lines, len;
size_t bytes;
char *req_type = NULL;
char *uri = NULL;
char *version = NULL;
int whitespace, wheres, slen;
int i;
http_parser_t * parser;
char data[2048];
FUNCTION_TRACE();
bytes = 0;
parser = request->parser;
rc = cwmp_chunk_create(&header, pool);
if(CWMP_OK != rc)
{
cwmp_log_error("cwmp_chunk_create fail:%d", rc);
return CWMP_ERROR;
}
rc = http_read_header(sock, header, pool);
if (rc <= 0)
{
cwmp_log_error("http_read_header return rc:%d", rc);
return rc;
}
len = cwmp_chunk_copy(data, header, 2047);
cwmp_log_debug("http read request: %s\n", data);
bytes += len;
lines = http_split_headers(data, len, line);
wheres = 0;
whitespace = 0;
slen = strlen(line[0]);
req_type = line[0];
for (i = 0; i < slen; i++)
{
if (line[0][i] == ' ')
{
whitespace = 1;
line[0][i] = '\0';
}
else
{
/* we're just past the whitespace boundry */
if (whitespace)
{
whitespace = 0;
wheres++;
switch (wheres)
{
case 1:
uri = &line[0][i];
break;
case 2:
version = &line[0][i];
break;
}
}
}
}
if (TRstrcasecmp("GET", req_type) == 0)
{
request->method = HTTP_GET;
}
else if (TRstrcasecmp("POST", req_type) == 0)
{
request->method = HTTP_POST;
}
else if (TRstrcasecmp("HEAD", req_type) == 0)
{
request->method = HTTP_HEAD;
}
else
{
request->method = HTTP_UNKNOWN;
}
http_parse_headers(parser, line, lines, pool);
return bytes;
#if 0
cwmp_chunk_t header;
cwmp_chunk_t body;
int rc;
char *tmp;
char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */
int i;
int lines;
char *req_type = NULL;
char *uri = NULL;
char *version = NULL;
int whitespace, where, slen;
rc = http_read_header(sock, &header);
if (rc <= 0)
{
return CWMP_ERROR;
}
lines = http_split_headers(data, len, line);
where = 0;
whitespace = 0;
slen = strlen(line[0]);
req_type = line[0];
for (i = 0; i < slen; i++)
{
if (line[0][i] == ' ')
{
whitespace = 1;
line[0][i] = '\0';
}
else
{
/* we're just past the whitespace boundry */
if (whitespace)
{
whitespace = 0;
where++;
switch (where)
{
case 1:
uri = &line[0][i];
break;
case 2:
version = &line[0][i];
break;
}
}
}
}
http_parse_headers(request->parser,
#endif
}
int http_parse_request(http_request_t * request, char *data, unsigned long len)
{
char *line[MAX_HEADERS]; /* limited to 32 lines, should be more than enough */
int i;
int lines;
char *req_type = NULL;
char *uri = NULL;
char *version = NULL;
int whitespace, where, slen;
if (data == NULL)
return 0;
/* make a local copy of the data, including 0 terminator */
//data = (char *)malloc(len+1);
//if (data == NULL) return 0;
//memcpy(data, http_data, len);
//data[len] = 0;
lines = http_split_headers(data, len, line);
/* parse the first line special
** the format is:
** REQ_TYPE URI VERSION
** eg:
** GET /index.html HTTP/1.0
*/
where = 0;
whitespace = 0;
slen = strlen(line[0]);
req_type = line[0];
for (i = 0; i < slen; i++)
{
if (line[0][i] == ' ')
{
whitespace = 1;
line[0][i] = '\0';
}
else
{
/* we're just past the whitespace boundry */
if (whitespace)
{
whitespace = 0;
where++;
switch (where)
{
case 1:
uri = &line[0][i];
break;
case 2:
version = &line[0][i];
break;
}
}
}
}
#if 0
if (strcasecmp("GET", req_type) == 0)
{
parser->req_type = httpp_req_get;
}
else if (strcasecmp("POST", req_type) == 0)
{
parser->req_type = httpp_req_post;
}
else if (strcasecmp("HEAD", req_type) == 0)
{
parser->req_type = httpp_req_head;
}
else if (strcasecmp("SOURCE", req_type) == 0)
{
parser->req_type = httpp_req_source;
}
else if (strcasecmp("PLAY", req_type) == 0)
{
parser->req_type = httpp_req_play;
}
else if (strcasecmp("STATS", req_type) == 0)
{
parser->req_type = httpp_req_stats;
}
else
{
parser->req_type = httpp_req_unknown;
}
if (uri != NULL && strlen(uri) > 0)
{
char *query;
if ((query = strchr(uri, '?')) != NULL)
{
http_set_variable(parser, HTTPP_VAR_RAWURI, uri);
*query = 0;
query++;
parse_query(parser, query);
}
parser->uri = strdup(uri);
}
else
{
free(data);
return 0;
}
if ((version != NULL) && ((tmp = strchr(version, '/')) != NULL))
{
tmp[0] = '\0';
if ((strlen(version) > 0) && (strlen(&tmp[1]) > 0))
{
http_set_variable(parser, HTTPP_VAR_PROTOCOL, version);
http_set_variable(parser, HTTPP_VAR_VERSION, &tmp[1]);
}
else
{
free(data);
return 0;
}
}
else
{
free(data);
return 0;
}
if (parser->req_type != httpp_req_none && parser->req_type != httpp_req_unknown)
{
switch (parser->req_type)
{
case httpp_req_get:
http_set_variable(parser, HTTPP_VAR_REQ_TYPE, "GET");
break;
case httpp_req_post:
http_set_variable(parser, HTTPP_VAR_REQ_TYPE, "POST");
break;
case httpp_req_head:
http_set_variable(parser, HTTPP_VAR_REQ_TYPE, "HEAD");
break;
case httpp_req_source:
http_set_variable(parser, HTTPP_VAR_REQ_TYPE, "SOURCE");
break;
case httpp_req_play:
http_set_variable(parser, HTTPP_VAR_REQ_TYPE, "PLAY");
break;
case httpp_req_stats:
http_set_variable(parser, HTTPP_VAR_REQ_TYPE, "STATS");
break;
default:
break;
}
}
else
{
free(data);
return 0;
}
if (parser->uri != NULL)
{
http_set_variable(parser, HTTPP_VAR_URI, parser->uri);
}
else
{
free(data);
return 0;
}
parse_headers(parser, line, lines);
free(data);
#endif
return 1;
}
int http_read_response(http_socket_t * sock, http_response_t * response, pool_t * pool)
{
char *line[MAX_HEADERS];
int lines, slen,i, whitespace=0, where=0,code;
char *version=NULL, *resp_code=NULL, *message=NULL;
cwmp_chunk_t *header;
//cwmp_chunk_t body;
int rc;
size_t len;
char * data;
char * ctxlen;
size_t cont_len;
char * transfer_encoding;
char * content_type;
int chunk_flag = 0;
int type_flag = 0;
FUNCTION_TRACE();
rc = cwmp_chunk_create(&header, pool);
if (CWMP_OK != rc)
{
cwmp_log_error("cwmp_chunk_create fail, rc = %d", rc);
return CWMP_ERROR;
}
rc = http_read_header(sock, header, pool);
if (rc <= 0)
{
cwmp_log_error("http read header fail, rc = %d", rc);
return CWMP_ERROR;
}
len = cwmp_chunk_length(header);
data = pool_pcalloc(pool, len + 1);
if(NULL == data)
{
cwmp_log_error("data pool_pcalloc return null");
return CWMP_ERROR;
}
cwmp_chunk_copy(data, header, len);
data[len] = 0;
cwmp_log_debug("http read header length: %d, [\n%s\n]", len, data);
lines = http_split_headers(data, len, line);
/* In this case, the first line contains:
* VERSION RESPONSE_CODE MESSAGE, such as HTTP/1.0 200 OK
*/
slen = strlen(line[0]);
version = line[0];
for (i=0; i < slen; i++)
{
if (line[0][i] == ' ')
{
line[0][i] = 0;
whitespace = 1;
}
else if (whitespace)
{
whitespace = 0;
where++;
if (where == 1)
{
resp_code = &line[0][i];
}
else
{
message = &line[0][i];
break;
}
}
}
//if (version == NULL || resp_code == NULL || message == NULL)
if (version == NULL || resp_code == NULL)
{
cwmp_log_error("version:%p, resp_code:%p, message:%p", version, resp_code, message);
return CWMP_ERROR;
}
http_set_variable(response->parser, HTTPP_VAR_ERROR_CODE, resp_code, pool);
code = TRatoi(resp_code);
response->status = code;
if (code < 200 || code >= 300)
{
cwmp_log_info("response code:%s->%d", resp_code, code);
http_set_variable(response->parser, HTTPP_VAR_ERROR_MESSAGE, message, pool);
}
//http_set_variable(response->parser, HTTPP_VAR_URI, uri);
http_set_variable(response->parser, HTTPP_VAR_REQ_TYPE, "NONE", pool);
http_parse_headers(response->parser, line, lines, pool);
ctxlen = http_get_variable(response->parser, "Content-Length");
cwmp_log_info("ctrlen:%s", ctxlen);
cont_len = 0;
if (ctxlen)
{
cont_len = TRatoi(ctxlen);
}
#if 0
rc = http_read_body(sock, cont_len);//, &body, pool);
if (rc < 0 || code != 200)
{
cwmp_log_info("Http read response rc is:%d, code is (%d)\n", rc, code);
}
#else
transfer_encoding = http_get_variable(response->parser, "Transfer-Encoding");
if(transfer_encoding)
{
if(TRstrcmp(transfer_encoding, "chunked") == 0)
{
cwmp_log_info("chunked message");
chunk_flag = 1;
}
}
content_type = http_get_variable(response->parser, "Content-Disposition");
if(content_type)
{
cwmp_log_info("Content-Type message:%s", content_type);
type_flag = 1;
}
if(chunk_flag == 1 && type_flag == 1)
{
rc = http_read_body_chunked(sock);
if (rc < 0 || code != 200)
{
cwmp_log_info("Http read chunked response rc is:%d, code is (%d)\n", rc, code);
}
return code;
}
else
{
if(cont_len <= 0 || cont_len > 0x7FFFFFFF)
{
cwmp_log_error("cont len is invalid:%d", cont_len);
return code;
}
rc = http_read_body(sock, cont_len);//, &body, pool);
if (rc < 0 || code != 200)
{
cwmp_log_info("Http read response rc is:%d, code is (%d)\n", rc, code);
}
}
#endif
return code;
}
//#define http_set_variable(header, name, value) http_set_var( &header, name, value)
char * http_method(int method)
{
switch (method)
{
case HTTP_POST:
return "POST";
case HTTP_PUT:
return "PUT";
default:
return "GET";
};
// return "GET";
}
/* calculate H(A1) as per spec */
void http_digest_calc_ha1(
const char *pszAlg,
const char *pszUserName,
const char *pszRealm,
const char *pszPassword,
const char *pszNonce,
const char *pszCNonce,
char *SessionKey)
{
cwmp_MD5_CTX Md5Ctx;
char HA1[HASHLEN];
cwmp_MD5Init(&Md5Ctx);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)pszUserName, strlen(pszUserName));
cwmp_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)pszRealm, strlen(pszRealm));
cwmp_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)pszPassword, strlen(pszPassword));
cwmp_MD5Final((unsigned char *)HA1, &Md5Ctx);
if (TRstrcasecmp(pszAlg, "md5-sess") == 0)
{
cwmp_log_info("md5-sess enter");
cwmp_MD5Init(&Md5Ctx);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)HA1, HASHLEN);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)pszNonce, strlen(pszNonce));
cwmp_MD5Update(&Md5Ctx, (unsigned char *)":", 1);
cwmp_MD5Update(&Md5Ctx, (unsigned char *)pszCNonce, strlen(pszCNonce));
cwmp_MD5Final((unsigned char *)HA1, &Md5Ctx);
};
convert_to_hex(HA1, SessionKey);
};
/***********************************************************************
***********************************************************************/
#if 1
int http_check_digest_auth(const char * auth_realm, const char * auth, char * cpe_user, char * cpe_pwd)
{
char data[512] = {0};
char * s ;
// char buffer[128];
char realm[256] = {0};
char user[256] = {0}; /*CDRouter will test largest size ConnectionRequest Username*/
char uri[256] = {0};//uri[32768]
char cnonce[33] = {0};
char nonce[33] = {0};
char qop[16] = {0};
char nc[16] = {0};
char response[128] = {0};
// char method[16] = {0};
// char resp[33] = {0};
char ha1[HASHHEXLEN+1];
char ha2[HASHHEXLEN+1];
char validResponse[HASHHEXLEN+1];
char * end;
if (!auth)
{
cwmp_log_info("auth is %s", auth);
return -1;
}
for (s = (char*)auth; isspace(*s); s++);
strncpy(data, s, 511);
s = data;
if (TRstrncasecmp(s, "digest", 6) != 0)
{
cwmp_log_info("s is %s, not begin with[digest]", s);
return -1;
}
for (s += 6; isspace(*s); s++);
end = s + strlen(s);
// memset(buffer, 0, 128);
while (s<end && NULL != s)
{
if (!strncmp(s, "username=", 9))
{
http_parse_key_value(&s, user, sizeof(user), 9);
}
else if (! strncmp(s, "nonce=", 6))
{
http_parse_key_value(&s, nonce, sizeof(nonce), 6);
}
else if (! strncmp(s, "response=", 9))
{
http_parse_key_value(&s, response, sizeof(response), 9);
}
else if (! strncmp(s, "uri=", 4))
{
http_parse_key_value(&s, uri, sizeof(uri), 4);
}
else if (! strncmp(s, "qop=", 4))
{
http_parse_key_value(&s, qop, sizeof(qop), 4);
}
else if (! strncmp(s, "cnonce=", 7))
{
http_parse_key_value(&s, cnonce, sizeof(cnonce), 7);
}
else if (! strncmp(s, "nc=", 3))
{
http_parse_key_value(&s, nc, sizeof(nc), 3);
}
else if (! strncmp(s, "realm=", 6))
{
http_parse_key_value(&s, realm, sizeof(nc), 6);
}
s++;
}
cwmp_log_info("user[%s], nonce[%s], response[%s], uri[%s], qop[%s], cnonce[%s], nc[%s]\n",
user, nonce, response, uri, qop, cnonce, nc);
if (TRstrcmp(cpe_user, user) != 0)
{
cwmp_log_info("cpe_user [%s] != user:[%s]\n", cpe_user, user);
return -1;
}
http_digest_calc_ha1("MD5", cpe_user, realm, cpe_pwd, nonce, cnonce, ha1);
cwmp_log_info("ha1:%s", ha1);
cwmp_MD5(ha2, "GET", ":", uri, NULL);
cwmp_MD5(validResponse, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, ":", ha2, NULL);
if (TRstrcasecmp(validResponse, response) == 0)
{
cwmp_log_info("auth ok. [%s] [%s]\n", validResponse, response);
return 0;
}
else
{
cwmp_log_info("auth fail. ha1:[%s], ha2:[%s], validResponse:[%s] response:[%s]\n", ha1, ha2, validResponse, response);
return -1;
}
return 0;
}
#else
int http_check_digest_auth(const char * auth_realm, const char * auth, char * cpe_user, char * cpe_pwd)
{
char data[512] = {0};
char * s ;
// char buffer[128];
char realm[256] = {0};
char user[256] = {0}; /*CDRouter will test largest size ConnectionRequest Username*/
char uri[256] = {0};//uri[32768]
char cnonce[33] = {0};
char nonce[33] = {0};
char qop[16] = {0};
char nc[16] = {0};
char pwd[256] = {0};
char response[128] = {0};
// char method[16] = {0};
// char resp[33] = {0};
int auth_type = HTTP_NONE_AUTH;
char ha1[HASHHEXLEN+1];
char ha2[HASHHEXLEN+1];
char validResponse[HASHHEXLEN+1];
char basic_content[256+1] = {0};
char *decoded_basic_content = NULL;
char * end;
if (!auth)
{
cwmp_log_info("auth is %s", auth);
return -1;
}
for (s = (char*)auth; isspace(*s); s++);
strncpy(data, s, 511);
s = data;
if (TRstrncasecmp(s, "digest", 6) == 0)
{
cwmp_log_error("s is:%s, return", s);
for (s += 6; isspace(*s); s++);
end = s + strlen(s);
auth_type = HTTP_DIGEST_AUTH;
goto auth_digest;
}
else if(TRstrncasecmp(s, "Basic", 5) == 0)
{
cwmp_log_error("s is:%s, return", s);
for (s += 5; isspace(*s); s++);
end = s + strlen(s);
auth_type = HTTP_BASIC_AUTH;
goto auth_basic;
}
else
{
cwmp_log_error("s is:%s, return", s);
return -1;
}
auth_digest:
// memset(buffer, 0, 128);
while (s<end && NULL != s)
{
if (!strncmp(s, "username=", 9))
{
http_parse_key_value(&s, user, sizeof(user), 9);
}
else if (! strncmp(s, "nonce=", 6))
{
http_parse_key_value(&s, nonce, sizeof(nonce), 6);
}
else if (! strncmp(s, "response=", 9))
{
http_parse_key_value(&s, response, sizeof(response), 9);
}
else if (! strncmp(s, "uri=", 4))
{
http_parse_key_value(&s, uri, sizeof(uri), 4);
}
else if (! strncmp(s, "qop=", 4))
{
http_parse_key_value(&s, qop, sizeof(qop), 4);
}
else if (! strncmp(s, "cnonce=", 7))
{
http_parse_key_value(&s, cnonce, sizeof(cnonce), 7);
}
else if (! strncmp(s, "nc=", 3))
{
http_parse_key_value(&s, nc, sizeof(nc), 3);
}
else if (! strncmp(s, "realm=", 6))
{
http_parse_key_value(&s, realm, sizeof(nc), 6);
}
s++;
}
cwmp_log_info("user[%s], nonce[%s], response[%s], uri[%s], qop[%s], cnonce[%s], nc[%s]\n",
user, nonce, response, uri, qop, cnonce, nc);
if (TRstrcmp(cpe_user, user) != 0)
{
cwmp_log_info("cpe_user [%s] != user:[%s]\n", cpe_user, user);
return -1;
}
http_digest_calc_ha1("MD5", cpe_user, realm, cpe_pwd, nonce, cnonce, ha1);
cwmp_log_info("ha1:%s", ha1);
cwmp_MD5(ha2, "GET", ":", uri, NULL);
cwmp_MD5(validResponse, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, ":", ha2, NULL);
if (TRstrcasecmp(validResponse, response) == 0)
{
cwmp_log_info("auth ok. [%s] [%s]\n", validResponse, response);
return 0;
}
else
{
cwmp_log_info("auth fail. ha1:[%s], ha2:[%s], validResponse:[%s] response:[%s]\n", ha1, ha2, validResponse, response);
return -1;
}
return 0;
auth_basic:
TRstrncpy(basic_content, s, sizeof(s)-1);
decoded_basic_content = base64_decode(basic_content, strlen(basic_content));
if(NULL == decoded_basic_content)
{
cwmp_log_error("decode basic auth content[%s] fail!", basic_content);
return -1;
}
sscanf(decoded_basic_content, "%256s:%256s", user, pwd);
cwmp_log_info("src:[%s], decoded:[%s], slipted:user:[%s], passwd:[%s]", basic_content, decoded_basic_content, user, pwd);
free(decoded_basic_content);
if (TRstrcmp(cpe_user, user) != 0 || TRstrcmp(cpe_pwd, pwd) != 0)
{
cwmp_log_info("cpe_user:[%s],user:[%s] cpe_pwd:[%s],pwd:[%s]\n", cpe_user, user, cpe_pwd, pwd);
return -1;
}
return 0;
}
#endif
int http_calc_digest_response(const char * user, const char * pwd,
const char * realm,
const char * nonce,
const char * uri,
const char * cnonce,
const char * nc,
const char * qop,
char * response)
{
char ha1[HASHHEXLEN+1];
char ha2[HASHHEXLEN+1];
char valid_response[HASHHEXLEN+1];
char valid_response_t[HASHHEXLEN+1];
http_digest_calc_ha1("MD5", user, realm, pwd, nonce, cnonce, ha1);
cwmp_MD5(ha2, "POST", ":", uri, NULL);
cwmp_MD5(valid_response_t, ha1, ":", nonce, ":", nc, ":", cnonce, ":", qop, ":", ha2, NULL);
cwmp_MD5(valid_response, ha1, ":", nonce, ":", ha2, NULL);
TRstrncpy(response, valid_response_t, HASHHEXLEN);
cwmp_log_info("ha1:%s, ha2:%s, response:%s, valid_response_t:%s", ha1, ha2, response, valid_response_t);
return CWMP_OK;
}
int http_parse_digest_auth(const char * auth, http_digest_auth_t * digest_auth, int *auth_type)
{
char data[512] = {0};
char * s ;
// char buffer[128];
char * end;
char user[256] = {0}; /*CDRouter will test largest size ConnectionRequest Username*/
char uri[256] = {0};//uri[32768]
char nonce[33] = {0};
char cnonce[33] = {0};
char realm[128] = {0};
char qop[16] = {0};
char nc[16] = {0};
char response[128] = {0};
FUNCTION_TRACE();
if (!auth)
{
return CWMP_ERROR;
}
for (s = (char*)auth; isspace(*s); s++);
strncpy(data, s, 511);
s = data;
if (TRstrncasecmp(s, "digest", 6) == 0)
{
cwmp_log_error("s is:%s, return", s);
for (s += 6; isspace(*s); s++);
end = s + strlen(s);
*auth_type = HTTP_DIGEST_AUTH;
}
else if(TRstrncasecmp(s, "Basic", 5) == 0)
{
cwmp_log_error("s is:%s, return", s);
for (s += 5; isspace(*s); s++);
end = s + strlen(s);
*auth_type = HTTP_BASIC_AUTH;
}
else
{
cwmp_log_error("s is:%s, return", s);
return -1;
}
// memset(buffer, 0, 128);
while (s<end && NULL != s)
{
if (!strncmp(s, "realm=", 6))
{
http_parse_key_value(&s, realm, sizeof(realm), 6);
}
else if (! strncmp(s, "nonce=", 6))
{
http_parse_key_value(&s, nonce, sizeof(nonce), 6);
}
else if (! strncmp(s, "response=", 9))
{
http_parse_key_value(&s, response, sizeof(response), 9);
}
else if (! strncmp(s, "uri=", 4))
{
http_parse_key_value(&s, uri, sizeof(uri), 4);
}
else if (! strncmp(s, "qop=", 4))
{
http_parse_key_value(&s, qop, sizeof(qop), 4);
}
else if (! strncmp(s, "cnonce=", 7))
{
http_parse_key_value(&s, cnonce, sizeof(cnonce), 7);
}
else if (! strncmp(s, "nc=", 3))
{
http_parse_key_value(&s, nc, sizeof(nc), 3);
}
else if (! strncmp(s, "domain=", 7))
{
http_parse_key_value(&s, uri, sizeof(uri), 7);
}
s++;
}
cwmp_log_info("user[%s], realm[%s], nonce[%s], response[%s], uri[%s], qop[%s], cnonce[%s], nc[%s]\n",
user, realm, nonce, response, uri, qop, cnonce, nc);
TRstrncpy(digest_auth->realm, realm, MIN_DEFAULT_LEN);
TRstrncpy(digest_auth->nonce, nonce, MIN_DEFAULT_LEN);
TRstrncpy(digest_auth->uri, uri, MIN_DEFAULT_LEN*4);
TRstrncpy(digest_auth->cnonce, cnonce, MIN_DEFAULT_LEN);
TRstrncpy(digest_auth->qop, "auth", MIN_DEFAULT_LEN);
TRstrncpy(digest_auth->nc, nc, MIN_DEFAULT_LEN);
return CWMP_OK;
}
static int acs_g_AuthNonce = 0;
static void get_acs_nc_cnonce(char * nc, int nc_len, char *cnonce, int cnonce_len)
{
char nonce[33] = {0};
char buffer[256] = {0};
acs_g_AuthNonce++;
TRsnprintf(nc, nc_len, "%08x", acs_g_AuthNonce);
TRsnprintf(buffer, 256, "%d", acs_g_AuthNonce);
cwmp_MD5(nonce, buffer, NULL);
nonce[32] = 0;
TRstrncpy(cnonce, nonce, cnonce_len);
}
int http_write_request(http_socket_t * sock , http_request_t * request, cwmp_chunk_t * chunk, pool_t * pool)
{
char buffer[HTTP_DEFAULT_LEN+1];
char * data;
size_t len1 = 0 ;
size_t len2 = 0;
size_t totallen = 0;
const char * header_fmt_4 =
"%s %s HTTP/1.1\r\n"
"Host: %s:%d\r\n"
"User-Agent: %s\r\n"
"Accept: */*\r\n"
"Content-Type: text/xml; charset=utf-8\r\n"
"Content-Length: %d\r\n"
"Connection:Keep-Alive\r\n"
;
const char * header_fmt_6 =
"%s %s HTTP/1.1\r\n"
"Host: [%s]:%d\r\n"
"User-Agent: %s\r\n"
"Accept: */*\r\n"
"Content-Type: text/xml; charset=utf-8\r\n"
"Content-Length: %d\r\n"
"Connection:Keep-Alive\r\n"
;
int family = -1;
const char * auth_fmt = "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=\"MD5\", cnonce=\"%s\", nc=%s, qop=\"%s\"\r\n";
const char * auth_basic = "Authorization: Basic %s\r\n";
const char * basic_autn_info = "%s:%s";
char userinfo[HTTP_DEFAULT_LEN+1] = {0};
char *encoded_userinfo = NULL;
http_dest_t * dest = request->dest;
FUNCTION_TRACE();
len2 = cwmp_chunk_length(chunk);
family = get_family_type();
if(family == AF_INET6)
{
len1 = TRsnprintf(buffer, HTTP_DEFAULT_LEN, header_fmt_6,
http_method(request->method),
dest->uri,
dest->host,
dest->port,
"CPE Netcwmp Agent",
len2);
}
else if(family == AF_INET)
{
len1 = TRsnprintf(buffer, HTTP_DEFAULT_LEN, header_fmt_4,
http_method(request->method),
dest->uri,
dest->host,
dest->port,
"CPE Netcwmp Agent",
len2);
}
else
{
cwmp_log_error("unsupport family type:%d", family);
}
cwmp_log_info("dest->auth.active:%d, dest->auth_type:%d, len2:%d", dest->auth.active, dest->auth_type, len2);
cwmp_log_info("dest->user:%s, dest->password:%s,dest->auth.realm:%s, dest->auth.nonce:%s, dest->auth.uri:%s, dest->auth.cnonce:%s, dest->auth.nc:%s, dest->auth.qop:%s, dest->auth.response:%s", dest->user, dest->password,
dest->auth.realm, dest->auth.nonce, dest->auth.uri, dest->auth.cnonce, dest->auth.nc, dest->auth.qop, dest->auth.response);
if(/*(dest->auth.active == CWMP_FALSE) && */ (dest->auth_type == HTTP_DIGEST_AUTH) &&(0 != TRstrcmp(dest->auth.realm, "")))
{
cwmp_log_info("enter digest auth");
char nc[9] = {0};
char cnonce[33] = {0};
get_acs_nc_cnonce(nc, 9, cnonce, 33);
cnonce[32] = '\0';
http_calc_digest_response(dest->user, dest->password,
dest->auth.realm, dest->auth.nonce, dest->uri, cnonce, nc, "auth", dest->auth.response);
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, auth_fmt,
dest->user,
dest->auth.realm, dest->auth.nonce,
dest->uri, dest->auth.response,
cnonce, nc, "auth"
);
}
else if(dest->auth_type == HTTP_BASIC_AUTH)
{
cwmp_log_info("enter basic auth");
TRsnprintf(userinfo, HTTP_DEFAULT_LEN, basic_autn_info, dest->user, dest->password);
encoded_userinfo = base64_encode(userinfo, TRstrlen(userinfo));
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, auth_basic,
encoded_userinfo);
free(encoded_userinfo);
}
if(dest->cookie[0] != '\0')
{
cwmp_log_info("dest->cookie:%s", dest->cookie);
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, "Cookie: %s\r\n",
dest->cookie);
}
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, "\r\n");
len1 = TRstrlen(buffer);
if(len2 > 0)
{
data = (char *)pool_palloc(pool, len1 + len2 + 1);
if(NULL == data)
{
cwmp_log_error("data pool_palloc return null");
return -1;
}
TRstrncpy(data, buffer, len1);
cwmp_chunk_copy(data+len1,chunk, len2);
}
else
{
data = buffer;
}
return http_socket_write(sock, data, (int)len1 + len2);
}
int http_get(http_socket_t * sock, http_request_t * request, cwmp_chunk_t * data, pool_t * pool)
{
request->method = HTTP_GET;
return http_write_request(sock, request, data, pool);
}
int http_post(http_socket_t * sock, http_request_t * request, cwmp_chunk_t * data, pool_t * pool)
{
request->method = HTTP_POST;
return http_write_request(sock, request, data, pool);
}
size_t http_send_file_callback(char *data, size_t size, size_t nmemb, void * calldata)
{
FILE * tf = (FILE*) calldata;
return fread(data, size, nmemb, tf);
}
size_t http_receive_file_callback(char *data, size_t size, size_t nmemb, void * calldata)
{
FILE * tf = (FILE*) calldata;
size_t len = 0;
int file_no;
cwmp_log_info("write file len:%d", size);
len = fwrite(data, size, nmemb, tf);
file_no = fileno(tf);
if(fsync(file_no) < 0) {
// todo: cov m
}
return len;
}
int http_send_file_request(http_socket_t * sock , http_request_t * request, const char * fromfile, pool_t * pool)
{
char buffer[HTTP_DEFAULT_LEN+1];
char * data;
size_t len1, len2, totallen;
int rc = CWMP_ERROR;
int family = -1;
const char * header_fmt_4 =
"%s %s HTTP/1.1\r\n"
"Authorization: Basic ZnRwdXNlcjpmdHB1c2Vy\r\n"
"Host: %s:%d\r\n"
//"User-Agent: %s\r\n"
"Accept: */*\r\n"
// "Content-Type: multipart/form-data\r\n"
//"Connection: Keep-Alive\r\n"
"Content-Length: %d\r\n"
"Expect: 100-continue\r\n"
;
const char * header_fmt_6 =
"%s %s HTTP/1.1\r\n"
"Authorization: Basic ZnRwdXNlcjpmdHB1c2Vy\r\n"
"Host: [%s]:%d\r\n"
//"User-Agent: %s\r\n"
"Accept: */*\r\n"
// "Content-Type: multipart/form-data\r\n"
//"Connection: Keep-Alive\r\n"
"Content-Length: %d\r\n"
"Expect: 100-continue\r\n"
;
#if 0
const char * auth_fmt = "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n";
//qop=%s, nc=%s, cnonce=\"%s\"
#else
const char * auth_fmt = "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=\"MD5\", cnonce=\"%s\", nc=%s, qop=\"%s\"\r\n";
#endif
http_dest_t * dest = request->dest;
struct stat buf;
if(stat(fromfile, &buf)<0)
{
cwmp_log_error("send file not exist:%s", fromfile);
len2 = 0;
}
else
{
len2 = buf.st_size;
}
FILE *tf = fopen(fromfile, "rb");
if(!tf)
{
cwmp_log_error("open file fail:%s", fromfile);
return CWMP_ERROR;
}
family = get_family_type();
if(family == AF_INET6)
{
len1 = TRsnprintf(buffer, HTTP_DEFAULT_LEN, header_fmt_6,
http_method(request->method),
dest->uri,
dest->host,
dest->port,
"CPE Netcwmp Agent",
len2);
}
else if(family == AF_INET)
{
len1 = TRsnprintf(buffer, HTTP_DEFAULT_LEN, header_fmt_4,
http_method(request->method),
dest->uri,
dest->host,
dest->port,
"CPE Netcwmp Agent",
len2);
}
else
{
cwmp_log_error("unknown family type:%d", family);
fclose(tf);
return CWMP_ERROR;
}
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, "\r\n");
if(/*(dest->auth.active == CWMP_FALSE) && (dest->auth_type == HTTP_DIGEST_AUTH) && */(0 != TRstrcmp(dest->auth.realm, "")))
{
cwmp_log_info("enter auth");
char nc[9] = {0};
char cnonce[33] = {0};
get_acs_nc_cnonce(nc, 9, cnonce, 33);
cnonce[32] = '\0';
http_calc_digest_response(dest->user, dest->password,
dest->auth.realm, dest->auth.nonce, dest->uri, cnonce, nc, "auth", dest->auth.response);
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, auth_fmt,
dest->user,
dest->auth.realm, dest->auth.nonce,
dest->uri, dest->auth.response,
cnonce, nc, "auth"
);
}
cwmp_log_debug("SEND: %d[\n%s\n]", len1, buffer);
http_socket_write(sock, buffer, (int)len1);
http_response_t * response;
rc = http_response_create(&response, pool);
if(rc != CWMP_OK)
{
cwmp_log_error("http_response_create return:%d", rc);
fclose(tf);
return CWMP_ERROR;
}
rc = http_read_response(sock, response, pool);
if(rc != HTTP_100)
{
fclose(tf);
cwmp_log_debug("rc[%d] != 100", rc);
return CWMP_ERROR;
}
totallen = len1;
while(1)
{
len2 = fread(buffer, 1, HTTP_DEFAULT_LEN, tf);
if(len2 <= 0)
{
break;
}
len2 = http_socket_write(sock, buffer, (int)len2);
if(len2 <= 0)
{
break;
}
totallen += len2;
}
fclose(tf);
return totallen;
}
int http_send_file(const char * fromfile, const char *tourl )
{
pool_t * pool;
http_dest_t * dest;
http_socket_t * sock;
http_request_t * request;
http_response_t * response;
int family = -1;
int rc = -1;
pool = pool_create(POOL_DEFAULT_SIZE);
if(NULL == pool)
{
cwmp_log_error("pool create return null, size: POOL_DEFAULT_SIZE");
return CWMP_ERROR;
}
cwmp_log_info("send from:[%s] to url:[%s]", fromfile, tourl);
http_dest_create(&dest, tourl, pool);
family = get_family_type();
if(family == -1)
{
cwmp_log_error("family error:%d.", family);
goto out;
}
rc = http_socket_create(&sock, family, SOCK_STREAM, 0, pool);
if (rc != CWMP_OK)
{
cwmp_log_error("http send file: create socket error.");
goto out;
}
rc = http_socket_connect(sock, family, dest->host, dest->port);
if(rc != CWMP_OK)
{
cwmp_log_error("connect to host faild. Host is %s:%d.", dest->host, dest->port);
goto out;
}
http_socket_set_recvtimeout(sock, 30);
http_request_create(&request, pool);
request->dest = dest;
request->method = HTTP_PUT;
rc = http_send_file_request_chunked(sock, request, fromfile, pool);
if(rc <= 0)
{
cwmp_log_error("http_send_file_request_chunked rc : %d.", rc);
pool_destroy(pool);
return CWMP_ERROR;
}
rc = http_response_create(&response, pool);
if(rc != CWMP_OK)
{
cwmp_log_error("http_response_create rc : %d.", rc);
pool_destroy(pool);
return CWMP_ERROR;
}
rc = http_read_response(sock, response, pool);
cwmp_log_debug("http_read_response rc is:%d", rc);
out:
pool_destroy(pool);
if(rc != HTTP_200)
return CWMP_ERROR;
else
return CWMP_OK;
}
int http_receive_file(const char *fromurl, const char * tofile)
{
pool_t * pool;
http_dest_t * dest;
http_socket_t * sock;
http_request_t * request;
http_response_t * response;
int family = -1;
int rc = -1;
FILE * tf = NULL;
pool = pool_create(POOL_DEFAULT_SIZE);
if(NULL == pool)
{
cwmp_log_error("pool create return null, size: POOL_DEFAULT_SIZE");
return CWMP_ERROR;
}
http_dest_create(&dest, fromurl, pool);
family = get_family_type();
if(family == -1)
{
cwmp_log_error("family error:%d.", family);
goto out;
}
rc = http_socket_create(&sock, family, SOCK_STREAM, 0, pool);
if (rc != CWMP_OK)
{
cwmp_log_error("http receive file: create socket error.");
goto out;
}
rc = http_socket_connect(sock, family, dest->host, dest->port);
if(rc != CWMP_OK)
{
cwmp_log_error("connect to host faild. Host is %s:%d.", dest->host, dest->port);
goto out;
}
tf = fopen(tofile, "wb+");
if(!tf)
{
cwmp_log_error("create file faild. %s\n", tofile);
goto out;
}
http_socket_set_writefunction(sock, http_receive_file_callback, tf);
http_socket_set_recvtimeout(sock, 30);
rc = http_request_create(&request, pool);
if(rc != CWMP_OK)
{
cwmp_log_error("http_request_create fail.");
goto out;
}
request->dest = dest;
rc = http_get(sock, request, NULL, pool);
if(rc <= 0)
{
cwmp_log_error("http get host faild. Host is %s:%d.", dest->host, dest->port);
goto out;
}
rc = http_response_create(&response, pool);
if(rc != CWMP_OK)
{
cwmp_log_error("http_response_create fail.");
goto out;
}
rc = http_read_response(sock, response, pool);
cwmp_log_info("read response: %d", rc);
out:
if(tf)
{
fclose(tf);
}
pool_destroy(pool);
return rc;
}
/****************************************************************************/
int htoi(unsigned char *s)
{
int i;
int n = 0;
if (s[0] == '0' && (s[1]=='x' || s[1]=='X')) //???????0x??0X
{
i = 2;
}
else
{
i = 0;
}
for (; (s[i] >= '0' && s[i] <= '9') || (s[i] >= 'a' && s[i] <= 'z') || (s[i] >='A' && s[i] <= 'Z');++i)
{
if (tolower(s[i]) > '9')
{
n = 16 * n + (10 + tolower(s[i]) - 'a');
}
else
{
n = 16 * n + (tolower(s[i]) - '0');
}
}
return n;
}
int http_read_line_chunk(http_socket_t * sock, char * buffer, int max)
{
char c;
int i=0;
while (i < max)
{
if ( http_socket_read(sock, &c, 1) <= 0 )
{
cwmp_log_error("recv, CANNOT READ 1 char");
return CWMP_ERROR;
};
buffer[i++]=c;
cwmp_log_info("get c:%c", c);
if (c=='\r') // GOT CR
{
// ??n
if ( http_socket_read(sock, &c, 1) < 0 )
{
cwmp_log_error("http_socket_read fail");
return CWMP_ERROR;
}
if(c != '\n')
{
cwmp_log_info("the char after enter is:%d, not n ", c);
return CWMP_ERROR;
}else
{
cwmp_log_info("the char after enter is:%d", c);
buffer[i++]=c;
break;
}
}
}
if (i >= max)
{
cwmp_log_error("i(%d) > max(%d)", i, max);
return CWMP_ERROR;
}
buffer[i] = 0;
return i;
}
int http_read_body_chunked(http_socket_t * sock)
{
int bytes = 0;
int rc;
char buf_len[256];
int len = 0;
char tail[3] = {0};
char c;
FUNCTION_TRACE();
while (1)
{
rc = http_read_line_chunk(sock, buf_len, 256);
if (rc < 0)
{
cwmp_log_error("http_read_line_chunk return:%d", rc);
return rc;
}
buf_len[rc] = 0;
len = htoi(buf_len);
cwmp_log_info("%s:%d", buf_len, len);
if(len == 0)
{
cwmp_log_info("read content end!");
break;
}
rc = http_read_body(sock, len);
if (rc <= 0)
{
cwmp_log_error("read body content fail!");
return rc;
}
cwmp_log_info("read len:%d", rc);
if (http_socket_read(sock, tail, 2) <= 0 )
{
cwmp_log_error("recv, CANNOT READ 2 char");
return CWMP_ERROR;
};
if(tail[0] != '\r' || tail[1] != '\n')
{
cwmp_log_error("read len is wrong! %d%d", tail[0], tail[1]);
}
bytes += len;
}
cwmp_log_info("read len:%d", bytes);
return bytes;
}
#define MAX_LEN (10)
int http_send_file_request_chunked(http_socket_t * sock , http_request_t * request, const char * fromfile, pool_t * pool)
{
char buffer[HTTP_DEFAULT_LEN+3];
char * data;
size_t len1, len2, totallen, read_len, file_len;
int hex_len;
int rc = CWMP_ERROR;
char chunked_len[MAX_LEN + 1];
int family = -1;
const char * header_fmt_6 =
"%s %s HTTP/1.1\r\n"
"Authorization: Basic ZnRwdXNlcjpmdHB1c2Vy\r\n"
"Host: [%s]:%d\r\n"
"User-Agent: %s\r\n"
"Accept: */*\r\n"
// "Content-Type: multipart/form-data\r\n"
//"Connection: Keep-Alive\r\n"
"Content-Type: application/octet-stream\r\n"
"Transfer-Encoding: chunked\r\n"
"Expect: 100-continue\r\n"
;
const char * header_fmt_4 =
"%s %s HTTP/1.1\r\n"
"Authorization: Basic ZnRwdXNlcjpmdHB1c2Vy\r\n"
"Host: %s:%d\r\n"
"User-Agent: %s\r\n"
"Accept: */*\r\n"
// "Content-Type: multipart/form-data\r\n"
//"Connection: Keep-Alive\r\n"
"Content-Type: application/octet-stream\r\n"
"Transfer-Encoding: chunked\r\n"
"Expect: 100-continue\r\n"
;
#if 0
const char * auth_fmt = "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"\r\n";
//qop=%s, nc=%s, cnonce=\"%s\"
#else
const char * auth_fmt = "Authorization: Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\", algorithm=\"MD5\", cnonce=\"%s\", nc=%s, qop=\"%s\"\r\n";
#endif
http_dest_t * dest = request->dest;
family = get_family_type();
if(family == AF_INET6)
{
len1 = TRsnprintf(buffer, HTTP_DEFAULT_LEN, header_fmt_6,
http_method(request->method),
dest->uri,
dest->host,
dest->port,
"CPE Netcwmp Agent");
}
else if(family == AF_INET)
{
len1 = TRsnprintf(buffer, HTTP_DEFAULT_LEN, header_fmt_4,
http_method(request->method),
dest->uri,
dest->host,
dest->port,
"CPE Netcwmp Agent");
}
else
{
cwmp_log_error("unsupport family type:%d", family);
return CWMP_ERROR;
}
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, "\r\n");
if(/*(dest->auth.active == CWMP_FALSE) && */ (dest->auth_type == HTTP_DIGEST_AUTH) && (0 != TRstrcmp(dest->auth.realm, "")))
{
cwmp_log_info("enter auth");
char nc[9] = {0};
char cnonce[33] = {0};
get_acs_nc_cnonce(nc, 9, cnonce, 33);
cnonce[32] = '\0';
http_calc_digest_response(dest->user, dest->password,
dest->auth.realm, dest->auth.nonce, dest->uri, cnonce, nc, "auth", dest->auth.response);
len1 += TRsnprintf(buffer + len1, HTTP_DEFAULT_LEN - len1, auth_fmt,
dest->user,
dest->auth.realm, dest->auth.nonce,
dest->uri, dest->auth.response,
cnonce, nc, "auth"
);
}
cwmp_log_debug("SEND: %d[\n%s\n]", len1, buffer);
http_socket_write(sock, buffer, (int)len1);
http_response_t * response;
rc = http_response_create(&response, pool);
if(CWMP_OK != rc)
{
cwmp_log_error("http_response_create return :%d", rc);
return CWMP_ERROR;
}
rc = http_read_response(sock, response, pool);
if(rc != HTTP_100)
{
cwmp_log_debug("rc[%d] != 100", rc);
return CWMP_ERROR;
}
totallen = len1;
cwmp_log_info("begin to send file:%s", fromfile);
struct stat buf;
if(stat(fromfile, &buf)<0)
{
cwmp_log_error("send file not exist:%s", fromfile);
file_len = 0;
return CWMP_ERROR;
}
else
{
file_len = buf.st_size;
}
FILE *tf = fopen(fromfile, "rb");
if(!tf)
{
cwmp_log_error("open file fail:%s", fromfile);
return CWMP_ERROR;
}
while(file_len > 0)
{
memset(chunked_len, 0, sizeof(chunked_len));
memset(buffer, 0, sizeof(buffer));
read_len = fread(buffer, 1, 1024, tf);
if(read_len <= 0)
{
break;
}
cwmp_log_info("read len:%d", read_len);
TRsnprintf(chunked_len, MAX_LEN, "%0x\r\n", read_len);
hex_len = TRstrlen(chunked_len);
len2 = http_socket_write(sock, chunked_len, hex_len);
if(len2 <= 0)
{
cwmp_log_error("write len:%d", len2);
// if(tf != NULL)
{
fclose(tf);
}
return CWMP_ERROR;
}
totallen += len2;
buffer[read_len] = '\r';
buffer[read_len+1] = '\n';
len2 = http_socket_write(sock, buffer, (int)read_len + 2);
if(len2 <= 0)
{
cwmp_log_error("write len:%d", len2);
// if(tf != NULL)
{
fclose(tf);
}
return CWMP_ERROR;
}
totallen += len2;
file_len -= read_len;
}
len2 = http_socket_write(sock, "0\r\n", 3);
if(len2 <= 0)
{
cwmp_log_error("write len:%d", len2);
// if(tf != NULL)
{
fclose(tf);
}
return CWMP_ERROR;
}
totallen += 3;
// if(tf != NULL)
{
fclose(tf);
}
return totallen;
}