Files
gb28181/GB28181Device/http/http_cln.cpp
2024-12-15 20:42:32 +08:00

627 lines
15 KiB
C++

/***************************************************************************************
*
* IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
*
* By downloading, copying, installing or using the software you agree to this license.
* If you do not agree to this license, do not download, install,
* copy or use the software.
*
* Copyright (C) 2014-2022, Happytimesoft Corporation, all rights reserved.
*
* Redistribution and use in binary forms, with or without modification, are permitted.
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
* language governing permissions and limitations under the License.
*
****************************************************************************************/
#include "sys_inc.h"
#include "http.h"
#include "http_parse.h"
#include "http_cln.h"
#include "rfc_md5.h"
#include "base64.h"
/***************************************************************************************/
#ifdef HTTPS
#if __WINDOWS_OS__
#pragma comment(lib, "libcrypto.lib")
#pragma comment(lib, "libssl.lib")
#endif
#endif
/***************************************************************************************/
#define MAX_CTT_LEN (2*1024*1024)
/***************************************************************************************/
BOOL http_get_digest_info(HTTPMSG * rx_msg, HD_AUTH_INFO * auth_info)
{
char word_buf[128];
int next_offset;
HDRV * chap_id = NULL;
chap_id = http_find_headline(rx_msg, "WWW-Authenticate");
if (chap_id == NULL)
{
return FALSE;
}
auth_info->auth_response[0] = '\0';
auth_info->auth_uri[0] = '\0';
RETRY:
word_buf[0] = '\0';
GetLineWord(chap_id->value_string, 0, (int)strlen(chap_id->value_string),
word_buf, sizeof(word_buf), &next_offset, WORD_TYPE_STRING);
if (strcasecmp(word_buf, "digest") != 0)
{
// There may be multiple "WWW-Authenticate" header line
chap_id = http_find_headline_next(rx_msg, "WWW-Authenticate", chap_id);
if (chap_id)
{
goto RETRY;
}
return FALSE;
}
word_buf[0] = '\0';
if (GetNameValuePair(chap_id->value_string+next_offset,
(int)strlen(chap_id->value_string)-next_offset, "realm", word_buf, sizeof(word_buf)) == FALSE)
{
return FALSE;
}
strcpy(auth_info->auth_realm, word_buf);
word_buf[0] = '\0';
if (GetNameValuePair(chap_id->value_string+next_offset,
(int)strlen(chap_id->value_string)-next_offset, "nonce", word_buf, sizeof(word_buf)) == FALSE)
{
return FALSE;
}
strcpy(auth_info->auth_nonce, word_buf);
word_buf[0] = '\0';
if (GetNameValuePair(chap_id->value_string+next_offset,
(int)strlen(chap_id->value_string)-next_offset, "qop", word_buf, sizeof(word_buf)))
{
strncpy(auth_info->auth_qop, word_buf, sizeof(auth_info->auth_qop)-1);
}
else
{
auth_info->auth_qop[0] = '\0';
}
word_buf[0] = '\0';
if (GetNameValuePair(chap_id->value_string+next_offset,
(int)strlen(chap_id->value_string)-next_offset, "opaque", word_buf, sizeof(word_buf)))
{
strcpy(auth_info->auth_opaque, word_buf);
}
else
{
auth_info->auth_opaque[0] = '\0';
}
snprintf(auth_info->auth_cnonce, sizeof(auth_info->auth_cnonce), "%08X%08X", rand(), rand());
auth_info->auth_nc++;
snprintf(auth_info->auth_ncstr, sizeof(auth_info->auth_ncstr), "%08X", auth_info->auth_nc);
return TRUE;
}
BOOL http_calc_auth_digest(HD_AUTH_INFO * auth_info, const char * method)
{
MD5_CTX Md5Ctx;
HASH HA1;
HASH HA2;
HASH HA3;
HASHHEX HA1Hex;
HASHHEX HA2Hex;
HASHHEX HA3Hex;
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_name, (int)strlen(auth_info->auth_name));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_realm, (int)strlen(auth_info->auth_realm));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_pwd, (int)strlen(auth_info->auth_pwd));
MD5Final(HA1, &Md5Ctx);
BinToHexStr(HA1, HA1Hex);
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, (uint8 *)method, (int)strlen(method));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_uri, (int)strlen(auth_info->auth_uri));
MD5Final(HA2, &Md5Ctx);
BinToHexStr(HA2, HA2Hex);
MD5Init(&Md5Ctx);
MD5Update(&Md5Ctx, (uint8 *)HA1Hex, HASHHEXLEN);
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_nonce, (int)strlen(auth_info->auth_nonce));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
if (auth_info->auth_qop[0] != '\0')
{
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_ncstr, (int)strlen(auth_info->auth_ncstr));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_cnonce, (int)strlen(auth_info->auth_cnonce));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
MD5Update(&Md5Ctx, (uint8 *)auth_info->auth_qop, (int)strlen(auth_info->auth_qop));
MD5Update(&Md5Ctx, (uint8 *)&(":"), 1);
};
MD5Update(&Md5Ctx, (uint8 *)HA2Hex, HASHHEXLEN);
MD5Final(HA3, &Md5Ctx);
BinToHexStr(HA3, HA3Hex);
strcpy(auth_info->auth_response, HA3Hex);
return TRUE;
}
int http_build_auth_msg(HTTPREQ * p_http, const char * method, char * buff, int buflen)
{
int offset = 0;
if (p_http->auth_mode == 1) // digest auth
{
if (p_http->auth_info.auth_qop[0] != '\0')
{
strcpy(p_http->auth_info.auth_qop, "auth");
}
http_calc_auth_digest(&p_http->auth_info, method);
offset += snprintf(buff+offset, buflen-offset, "Authorization: Digest username=\"%s\", realm=\"%s\", "
"nonce=\"%s\", uri=\"%s\", response=\"%s\"",
p_http->auth_info.auth_name, p_http->auth_info.auth_realm, p_http->auth_info.auth_nonce,
p_http->auth_info.auth_uri, p_http->auth_info.auth_response);
if (p_http->auth_info.auth_qop[0] != '\0')
{
offset += snprintf(buff+offset, buflen-offset, ", qop=\"auth\", cnonce=\"%s\", nc=%s",
p_http->auth_info.auth_cnonce, p_http->auth_info.auth_ncstr);
}
if (p_http->auth_info.auth_opaque[0] != '\0')
{
offset += snprintf(buff+offset, buflen-offset, ", opaque=\"%s\"", p_http->auth_info.auth_opaque);
}
offset += snprintf(buff+offset, buflen-offset, ", algorithm=\"MD5\"\r\n");
}
else if (p_http->auth_mode == 0) // basic auth
{
char auth[128] = {'\0'};
char basic[256] = {'\0'};
snprintf(auth, sizeof(auth), "%s:%s", p_http->auth_info.auth_name, p_http->auth_info.auth_pwd);
base64_encode((uint8 *)auth, (int)strlen(auth), basic, sizeof(basic));
offset += snprintf(buff+offset, buflen-offset, "Authorization: Basic %s\r\n", basic);
}
return offset;
}
int http_cln_auth_set(HTTPREQ * p_http, const char * user, const char * pass)
{
if (user)
{
strncpy(p_http->auth_info.auth_name, user, sizeof(p_http->auth_info.auth_name)-1);
}
if (pass)
{
strncpy(p_http->auth_info.auth_pwd, pass, sizeof(p_http->auth_info.auth_pwd)-1);
}
if (http_get_digest_info(p_http->rx_msg, &p_http->auth_info))
{
p_http->auth_mode = 1; // digest
strcpy(p_http->auth_info.auth_uri, p_http->url);
}
else
{
p_http->auth_mode = 0; // basic
}
p_http->need_auth = TRUE;
return p_http->auth_mode;
}
BOOL http_cln_ssl_conn(HTTPREQ * p_http, int timeout)
{
#ifdef HTTPS
SSL_CTX * ctx = NULL;
const SSL_METHOD * method = NULL;
SSLeay_add_ssl_algorithms();
SSL_load_error_strings();
method = SSLv23_client_method();
ctx = SSL_CTX_new(method);
if (NULL == ctx)
{
log_print(HT_LOG_ERR, "%s, SSL_CTX_new failed!\r\n", __FUNCTION__);
return FALSE;
}
#if __WINDOWS_OS__
int tv = timeout;
#else
struct timeval tv = {timeout / 1000, (timeout % 1000) * 1000};
#endif
setsockopt(p_http->cfd, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(tv));
p_http->ssl = SSL_new(ctx);
if (NULL == p_http->ssl)
{
SSL_CTX_free(ctx);
log_print(HT_LOG_ERR, "%s, SSL_new failed!\r\n", __FUNCTION__);
return FALSE;
}
SSL_set_fd(p_http->ssl, p_http->cfd);
if (SSL_connect(p_http->ssl) == -1)
{
SSL_CTX_free(ctx);
log_print(HT_LOG_ERR, "%s, SSL_connect failed!\r\n", __FUNCTION__);
return FALSE;
}
SSL_CTX_free(ctx);
p_http->ssl_mutex = sys_os_create_mutex();
return TRUE;
#else
return FALSE;
#endif
}
BOOL http_cln_rx(HTTPREQ * p_http)
{
int rlen;
HTTPMSG * rx_msg;
if (p_http->rbuf == NULL)
{
p_http->rbuf = p_http->rcv_buf;
p_http->mlen = sizeof(p_http->rcv_buf)-4;
p_http->rcv_dlen = 0;
p_http->ctt_len = 0;
p_http->hdr_len = 0;
}
#ifdef HTTPS
if (p_http->https)
{
sys_os_mutex_enter(p_http->ssl_mutex);
rlen = SSL_read(p_http->ssl, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen);
sys_os_mutex_leave(p_http->ssl_mutex);
}
else
#endif
rlen = recv(p_http->cfd, p_http->rbuf+p_http->rcv_dlen, p_http->mlen-p_http->rcv_dlen, 0);
if (rlen < 0)
{
log_print(HT_LOG_INFO, "%s, recv return = %d, dlen[%d], mlen[%d]\r\n",
__FUNCTION__, rlen, p_http->rcv_dlen, p_http->mlen);
return FALSE;
}
p_http->rcv_dlen += rlen;
p_http->rbuf[p_http->rcv_dlen] = '\0';
if (0 == rlen)
{
if (p_http->rcv_dlen < p_http->ctt_len + p_http->hdr_len && p_http->ctt_len == MAX_CTT_LEN)
{
// without Content-Length filed, when recv finish, fix the ctt length
p_http->ctt_len = p_http->rcv_dlen - p_http->hdr_len;
}
else
{
log_print(HT_LOG_INFO, "%s, recv return = %d, dlen[%d], mlen[%d]\r\n",
__FUNCTION__, rlen, p_http->rcv_dlen, p_http->mlen);
return FALSE;
}
}
if (p_http->rcv_dlen < 16)
{
return TRUE;
}
if (http_is_http_msg(p_http->rbuf) == FALSE)
{
return FALSE;
}
rx_msg = NULL;
if (p_http->hdr_len == 0)
{
int parse_len;
int http_pkt_len;
http_pkt_len = http_pkt_find_end(p_http->rbuf);
if (http_pkt_len == 0)
{
return TRUE;
}
p_http->hdr_len = http_pkt_len;
rx_msg = http_get_msg_buf(http_pkt_len+1);
if (rx_msg == NULL)
{
log_print(HT_LOG_ERR, "%s, get msg buf failed\r\n", __FUNCTION__);
return FALSE;
}
memcpy(rx_msg->msg_buf, p_http->rbuf, http_pkt_len);
rx_msg->msg_buf[http_pkt_len] = '\0';
log_print(HT_LOG_DBG, "RX from %s << %s\r\n", p_http->host, rx_msg->msg_buf);
parse_len = http_msg_parse_part1(rx_msg->msg_buf, http_pkt_len, rx_msg);
if (parse_len != http_pkt_len)
{
log_print(HT_LOG_ERR, "%s, http_msg_parse_part1=%d, http_pkt_len=%d!!!\r\n",
__FUNCTION__, parse_len, http_pkt_len);
http_free_msg(rx_msg);
return FALSE;
}
p_http->ctt_len = rx_msg->ctt_len;
}
if (p_http->ctt_len == 0 && p_http->rcv_dlen > p_http->hdr_len)
{
// without Content-Length field
p_http->ctt_len = MAX_CTT_LEN;
}
if ((p_http->ctt_len + p_http->hdr_len) > p_http->mlen)
{
if (p_http->dyn_recv_buf)
{
free(p_http->dyn_recv_buf);
}
p_http->dyn_recv_buf = (char *)malloc(p_http->ctt_len + p_http->hdr_len + 1);
if (NULL == p_http->dyn_recv_buf)
{
http_free_msg(rx_msg);
return FALSE;
}
memcpy(p_http->dyn_recv_buf, p_http->rcv_buf, p_http->rcv_dlen);
p_http->rbuf = p_http->dyn_recv_buf;
p_http->mlen = p_http->ctt_len + p_http->hdr_len;
http_free_msg(rx_msg);
// Need more data
return TRUE;
}
if (p_http->rcv_dlen >= (p_http->ctt_len + p_http->hdr_len))
{
if (rx_msg == NULL)
{
int nlen;
int parse_len;
nlen = p_http->ctt_len + p_http->hdr_len;
rx_msg = http_get_msg_buf(nlen+1);
if (rx_msg == NULL)
{
log_print(HT_LOG_ERR, "%s, get msg buf failed\r\n", __FUNCTION__);
return FALSE;
}
memcpy(rx_msg->msg_buf, p_http->rbuf, p_http->hdr_len);
rx_msg->msg_buf[p_http->hdr_len] = '\0';
parse_len = http_msg_parse_part1(rx_msg->msg_buf, p_http->hdr_len, rx_msg);
if (parse_len != p_http->hdr_len)
{
log_print(HT_LOG_ERR, "%s, http_msg_parse_part1=%d, sip_pkt_len=%d!!!\r\n", __FUNCTION__, parse_len, p_http->hdr_len);
http_free_msg(rx_msg);
return FALSE;
}
}
if (p_http->ctt_len > 0)
{
int parse_len;
memcpy(rx_msg->msg_buf+p_http->hdr_len, p_http->rbuf+p_http->hdr_len, p_http->ctt_len);
rx_msg->msg_buf[p_http->hdr_len + p_http->ctt_len] = '\0';
if (ctt_is_string(rx_msg->ctt_type))
{
log_print(HT_LOG_DBG, "%s\r\n\r\n", rx_msg->msg_buf+p_http->hdr_len);
}
parse_len = http_msg_parse_part2(rx_msg->msg_buf+p_http->hdr_len, p_http->ctt_len, rx_msg);
if (parse_len != p_http->ctt_len)
{
log_print(HT_LOG_ERR, "%s, http_msg_parse_part2=%d, sdp_pkt_len=%d!!!\r\n", __FUNCTION__, parse_len, p_http->ctt_len);
http_free_msg(rx_msg);
return FALSE;
}
}
p_http->rx_msg = rx_msg;
}
if (p_http->rx_msg != rx_msg)
{
http_free_msg(rx_msg);
}
return TRUE;
}
int http_cln_tx(HTTPREQ * p_http, const char * p_data, int len)
{
int slen;
if (p_http->cfd <= 0)
{
return -1;
}
#ifdef HTTPS
if (p_http->https)
{
sys_os_mutex_enter(p_http->ssl_mutex);
slen = SSL_write(p_http->ssl, p_data, len);
sys_os_mutex_leave(p_http->ssl_mutex);
}
else
#endif
slen = send(p_http->cfd, p_data, len, 0);
return slen;
}
BOOL http_cln_rx_timeout(HTTPREQ * p_http, int timeout)
{
int count = 0;
int sret;
BOOL ret = FALSE;
fd_set fdr;
struct timeval tv;
while (1)
{
#ifdef HTTPS
if (p_http->https && SSL_pending(p_http->ssl) > 0)
{
// There is data to read
}
else
#endif
{
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&fdr);
FD_SET(p_http->cfd, &fdr);
sret = select((int)(p_http->cfd+1), &fdr, NULL, NULL, &tv);
if (sret == 0)
{
count++;
if (count >= timeout / 1000)
{
log_print(HT_LOG_WARN, "%s, timeout!!!\r\n", __FUNCTION__);
break;
}
continue;
}
else if (sret < 0)
{
log_print(HT_LOG_ERR, "%s, select err[%s], sret[%d]!!!\r\n", __FUNCTION__, sys_os_get_socket_error(), sret);
break;
}
else if (!FD_ISSET(p_http->cfd, &fdr))
{
continue;
}
}
if (http_cln_rx(p_http) == FALSE)
{
break;
}
else if (p_http->rx_msg != NULL)
{
ret = TRUE;
break;
}
}
return ret;
}
void http_cln_free_req(HTTPREQ * p_http)
{
if (p_http->cfd > 0)
{
closesocket(p_http->cfd);
p_http->cfd = 0;
}
#ifdef HTTPS
if (p_http->https)
{
if (p_http->ssl)
{
SSL_free(p_http->ssl);
p_http->ssl = NULL;
}
if (p_http->ssl_mutex)
{
sys_os_destroy_sig_mutex(p_http->ssl_mutex);
p_http->ssl_mutex = NULL;
}
}
#endif
if (p_http->dyn_recv_buf)
{
free(p_http->dyn_recv_buf);
p_http->dyn_recv_buf = NULL;
}
if (p_http->rx_msg)
{
http_free_msg(p_http->rx_msg);
p_http->rx_msg = NULL;
}
p_http->rcv_dlen = 0;
p_http->hdr_len = 0;
p_http->ctt_len = 0;
p_http->rbuf = NULL;
p_http->mlen = 0;
}