/*************************************************************************************** * * 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-2024, 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 "sha256.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) /***************************************************************************************/ static inline int http_isspace(int c) { return c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v'; } void http_choose_qop(char * qop, int size) { char * ptr = strstr(qop, "auth"); char * end = ptr + strlen("auth"); if (ptr && (!*end || http_isspace(*end) || *end == ',') && (ptr == qop || http_isspace(ptr[-1]) || ptr[-1] == ',')) { strncpy(qop, "auth", size); } else { qop[0] = 0; } } BOOL http_get_digest_params(char * p, int len, HD_AUTH_INFO * p_auth) { char word_buf[128]; if (GetNameValuePair(p, len, "algorithm", word_buf, sizeof(word_buf))) { strncpy(p_auth->auth_algorithm, word_buf, sizeof(p_auth->auth_algorithm)-1); } else { p_auth->auth_algorithm[0] = '\0'; } if (GetNameValuePair(p, len, "realm", word_buf, sizeof(word_buf))) { strncpy(p_auth->auth_realm, word_buf, sizeof(p_auth->auth_realm)-1); } else { return FALSE; } if (GetNameValuePair(p, len, "nonce", word_buf, sizeof(word_buf))) { strncpy(p_auth->auth_nonce, word_buf, sizeof(p_auth->auth_nonce)-1); } else { return FALSE; } if (GetNameValuePair(p, len, "qop", word_buf, sizeof(word_buf))) { strncpy(p_auth->auth_qop, word_buf, sizeof(p_auth->auth_qop)-1); } else { p_auth->auth_qop[0] = '\0'; } http_choose_qop(p_auth->auth_qop, sizeof(p_auth->auth_qop)); if (GetNameValuePair(p, len, "opaque", word_buf, sizeof(word_buf))) { p_auth->auth_opaque_flag = 1; strncpy(p_auth->auth_opaque, word_buf, sizeof(p_auth->auth_opaque)-1); } else { p_auth->auth_opaque_flag = 0; p_auth->auth_opaque[0] = '\0'; } return TRUE; } BOOL http_get_digest_info(HTTPMSG * rx_msg, HD_AUTH_INFO * p_auth) { int len; int next_offset; char word_buf[128]; HDRV * chap_id = NULL; char * p; p_auth->auth_response[0] = '\0'; RETRY: if (chap_id) { chap_id = http_find_headline_next(rx_msg, "WWW-Authenticate", chap_id); } else { chap_id = http_find_headline(rx_msg, "WWW-Authenticate"); } if (chap_id == NULL) { return FALSE; } 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) { goto RETRY; } p = chap_id->value_string + next_offset; len = (int)strlen(chap_id->value_string) - next_offset; if (!http_get_digest_params(p, len, p_auth)) { goto RETRY; } if (p_auth->auth_algorithm[0] != '\0' && strncasecmp(p_auth->auth_algorithm, "MD5", 3) && strncasecmp(p_auth->auth_algorithm, "SHA-256", 7)) { goto RETRY; } return TRUE; } BOOL http_calc_auth_md5_digest(HD_AUTH_INFO * p_auth, const char * method) { uint8 HA1[16]; uint8 HA2[16]; uint8 HA3[16]; char HA1Hex[33]; char HA2Hex[33]; char HA3Hex[33]; md5_context ctx; md5_starts(&ctx); md5_update(&ctx, (uint8 *)p_auth->auth_name, strlen(p_auth->auth_name)); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_realm, strlen(p_auth->auth_realm)); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_pwd, strlen(p_auth->auth_pwd)); md5_finish(&ctx, HA1); bin_to_hex_str(HA1, 16, HA1Hex, 33); if (!strcasecmp(p_auth->auth_algorithm, "MD5-sess")) { md5_starts(&ctx); md5_update(&ctx, (uint8 *)HA1Hex, 32); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_nonce, strlen(p_auth->auth_nonce)); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_cnonce, strlen(p_auth->auth_cnonce)); md5_finish(&ctx, HA1); bin_to_hex_str(HA1, 16, HA1Hex, 33); } md5_starts(&ctx); md5_update(&ctx, (uint8 *)method, strlen(method)); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_uri, strlen(p_auth->auth_uri)); md5_finish(&ctx, HA2); bin_to_hex_str(HA2, 16, HA2Hex, 33); md5_starts(&ctx); md5_update(&ctx, (uint8 *)HA1Hex, 32); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_nonce, strlen(p_auth->auth_nonce)); md5_update(&ctx, (uint8 *)&(":"), 1); if (p_auth->auth_qop[0] != '\0') { md5_update(&ctx, (uint8 *)p_auth->auth_ncstr, strlen(p_auth->auth_ncstr)); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_cnonce, strlen(p_auth->auth_cnonce)); md5_update(&ctx, (uint8 *)&(":"), 1); md5_update(&ctx, (uint8 *)p_auth->auth_qop, strlen(p_auth->auth_qop)); md5_update(&ctx, (uint8 *)&(":"), 1); }; md5_update(&ctx, (uint8 *)HA2Hex, 32); md5_finish(&ctx, HA3); bin_to_hex_str(HA3, 16, HA3Hex, 33); strcpy(p_auth->auth_response, HA3Hex); return TRUE; } BOOL http_calc_auth_sha256_digest(HD_AUTH_INFO * p_auth, const char * method) { uint8 HA1[32]; uint8 HA2[32]; uint8 HA3[32]; char HA1Hex[65]; char HA2Hex[65]; char HA3Hex[65]; sha256_context ctx; sha256_starts(&ctx); sha256_update(&ctx, (uint8 *)p_auth->auth_name, strlen(p_auth->auth_name)); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_realm, strlen(p_auth->auth_realm)); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_pwd, strlen(p_auth->auth_pwd)); sha256_finish(&ctx, HA1); bin_to_hex_str(HA1, 32, HA1Hex, 65); if (!strcasecmp(p_auth->auth_algorithm, "SHA-256-sess")) { sha256_starts(&ctx); sha256_update(&ctx, (uint8 *)HA1Hex, 64); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_nonce, strlen(p_auth->auth_nonce)); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_cnonce, strlen(p_auth->auth_cnonce)); sha256_finish(&ctx, HA1); bin_to_hex_str(HA1, 32, HA1Hex, 65); } sha256_starts(&ctx); sha256_update(&ctx, (uint8 *)method, strlen(method)); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_uri, strlen(p_auth->auth_uri)); sha256_finish(&ctx, HA2); bin_to_hex_str(HA2, 32, HA2Hex, 65); sha256_starts(&ctx); sha256_update(&ctx, (uint8 *)HA1Hex, 64); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_nonce, strlen(p_auth->auth_nonce)); sha256_update(&ctx, (uint8 *)&(":"), 1); if (p_auth->auth_qop[0] != '\0') { sha256_update(&ctx, (uint8 *)p_auth->auth_ncstr, strlen(p_auth->auth_ncstr)); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_cnonce, strlen(p_auth->auth_cnonce)); sha256_update(&ctx, (uint8 *)&(":"), 1); sha256_update(&ctx, (uint8 *)p_auth->auth_qop, strlen(p_auth->auth_qop)); sha256_update(&ctx, (uint8 *)&(":"), 1); }; sha256_update(&ctx, (uint8 *)HA2Hex, 64); sha256_finish(&ctx, HA3); bin_to_hex_str(HA3, 32, HA3Hex, 65); strcpy(p_auth->auth_response, HA3Hex); return TRUE; } BOOL http_calc_auth_digest(HD_AUTH_INFO * p_auth, const char * method) { BOOL ret = FALSE; p_auth->auth_nc++; sprintf(p_auth->auth_ncstr, "%08X", p_auth->auth_nc); sprintf(p_auth->auth_cnonce, "%08X%08X", rand(), rand()); if (p_auth->auth_algorithm[0] == '\0' || strncasecmp(p_auth->auth_algorithm, "MD5", 3) == 0) { ret = http_calc_auth_md5_digest(p_auth, method); } else if (strncasecmp(p_auth->auth_algorithm, "SHA-256", 7) == 0) { ret = http_calc_auth_sha256_digest(p_auth, method); } return ret; } int http_build_auth_msg(HTTPREQ * p_http, const char * method, char * buff, int buflen) { int offset = 0; HD_AUTH_INFO * p_auth = &p_http->auth_info; if (p_http->auth_mode == 1) // digest auth { http_calc_auth_digest(p_auth, method); offset += snprintf(buff+offset, buflen-offset, "Authorization: Digest username=\"%s\", realm=\"%s\", " "nonce=\"%s\", uri=\"%s\", response=\"%s\"", p_auth->auth_name, p_auth->auth_realm, p_auth->auth_nonce, p_auth->auth_uri, p_auth->auth_response); if (p_auth->auth_opaque_flag) { offset += snprintf(buff+offset, buflen-offset, ", opaque=\"%s\"", p_auth->auth_opaque); } if (p_auth->auth_qop[0] != '\0') { offset += snprintf(buff+offset, buflen-offset, ", qop=\"%s\", cnonce=\"%s\", nc=%s", p_auth->auth_qop, p_auth->auth_cnonce, p_auth->auth_ncstr); } if (p_auth->auth_algorithm[0] != '\0') { offset += snprintf(buff+offset, buflen-offset, ", algorithm=%s", p_auth->auth_algorithm); } offset += snprintf(buff+offset, buflen-offset, "\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_auth->auth_name, p_auth->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 = 0; 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); if (p_http->ssl) { 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 = 0; if (p_http->cfd <= 0) { return -1; } #ifdef HTTPS if (p_http->https) { sys_os_mutex_enter(p_http->ssl_mutex); if (p_http->ssl) { 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) { sys_os_mutex_enter(p_http->ssl_mutex); SSL_free(p_http->ssl); p_http->ssl = NULL; sys_os_mutex_leave(p_http->ssl_mutex); } 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; }