/*************************************************************************************** * * 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; }