Fix typo: transferred
[mfgtools.git] / libuuu / http.cpp
blob454ab51b1bfe65f9172ebb822839f97eee70f1d9
1 /*
2 * Copyright 2019, 2023 NXP.
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright notice, this
11 * list of conditions and the following disclaimer in the documentation and/or
12 * other materials provided with the distribution.
14 * Neither the name of the NXP Semiconductor nor the names of its
15 * contributors may be used to endorse or promote products derived from this
16 * software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
33 #ifdef _WIN32
34 // request += "Connection: Keep-Alive\n";
35 #include <ws2tcpip.h>
36 #include <windows.h>
37 #include <winhttp.h>
38 #pragma comment(lib, "Winhttp.lib")
39 #else
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <netdb.h>
43 #define INVALID_SOCKET -1
44 #include <unistd.h>
45 #endif
47 #include "http.h"
48 #include "libuuu.h"
49 #include "liberror.h"
50 #include "libcomm.h"
51 #include <string.h>
52 #include <locale>
53 #include <codecvt>
55 uuu_askpasswd g_ask_passwd;
56 int uuu_set_askpasswd(uuu_askpasswd ask)
58 g_ask_passwd = ask;
59 return 0;
62 map<string, pair<string, string>> g_passwd_map;
64 #ifdef UUUSSL
65 #include <openssl/ssl.h>
66 #include <openssl/err.h>
68 static const char* base64_table =
69 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
70 "abcdefghijklmnopqrstuvwxyz"
71 "0123456789+/";
73 static string base64_encode(string str)
75 string ret;
76 for (int i = 0; i < str.length(); i += 3)
78 ret.push_back(base64_table[(str[i] >> 2) & 0x3f]);
79 if (i + 1 < str.length())
81 int index;
82 index = ((str[i] & 3) << 4)
83 + ((str[i + 1] >> 4) & 0xf);
84 ret.push_back(base64_table[index]);
86 else
88 ret.push_back(base64_table[(str[i] & 0x3) << 4]);
89 ret.push_back('=');
90 ret.push_back('=');
91 return ret;
94 if (i + 2 < str.length())
96 int index;
97 index = (((str[i + 1]) & 0xF) << 2)
98 + (((str[i + 2]) >> 6) & 0x3);
99 ret.push_back(base64_table[index]);
100 index = str[i + 2] & 0x3f;
101 ret.push_back(base64_table[index]);
104 else
106 ret.push_back(base64_table[((str[i + 1]) & 0xF) << 2]);
107 ret.push_back('=');
108 return ret;
111 return ret;
114 class CUUUSSL
116 public:
117 CUUUSSL()
119 #if OPENSSL_VERSION_NUMBER < 0x10100000L
120 SSL_library_init();
121 SSLeay_add_ssl_algorithms();
122 SSL_load_error_strings();
123 #else
124 OPENSSL_init_ssl(0, nullptr);
125 SSLeay_add_ssl_algorithms();
126 #endif
128 ~CUUUSSL()
133 static CUUUSSL g_uuussl;
135 #endif
136 using namespace std;
138 #ifdef _WIN32
139 /* Win32 implement*/
141 DWORD ChooseAuthScheme(DWORD dwSupportedSchemes)
143 if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NEGOTIATE)
144 return WINHTTP_AUTH_SCHEME_NEGOTIATE;
145 else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_NTLM)
146 return WINHTTP_AUTH_SCHEME_NTLM;
147 else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_PASSPORT)
148 return WINHTTP_AUTH_SCHEME_PASSPORT;
149 else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_DIGEST)
150 return WINHTTP_AUTH_SCHEME_DIGEST;
151 else if (dwSupportedSchemes & WINHTTP_AUTH_SCHEME_BASIC)
152 return WINHTTP_AUTH_SCHEME_BASIC;
154 return 0;
157 HttpStream::HttpStream()
159 m_buff.empty();
160 m_hConnect = 0;
161 m_hSession = 0;
162 m_hRequest = 0;
165 int HttpStream::HttpGetHeader(std::string host, std::string path, int port, bool ishttps)
168 m_hSession = WinHttpOpen(L"WinHTTP UUU/1.0",
169 WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
170 WINHTTP_NO_PROXY_NAME,
171 WINHTTP_NO_PROXY_BYPASS, 0);
173 if (!m_hSession)
175 set_last_err_string("fail WinHttpOpen");
176 return -1;
179 wstring_convert<codecvt_utf8_utf16<wchar_t>> converter;
180 wstring whost = converter.from_bytes(host);
182 if (m_hSession)
183 m_hConnect = WinHttpConnect(m_hSession, whost.c_str(),
184 port, 0);
186 if (!m_hConnect)
188 set_last_err_string("Fail Connection");
189 return -1;
192 wstring wpath = converter.from_bytes(path);
194 m_hRequest = WinHttpOpenRequest(m_hConnect, L"GET", wpath.c_str(),
195 nullptr, WINHTTP_NO_REFERER,
196 WINHTTP_DEFAULT_ACCEPT_TYPES,
197 ishttps ?WINHTTP_FLAG_SECURE:0);
199 BOOL bResults = FALSE;
200 if (!m_hRequest)
202 set_last_err_string("Fail WinHttpOpenRequest");
203 return -1;
206 DWORD dwProxyAuthScheme = 0;
207 int retry = 3;
209 pair<string, string> up = g_passwd_map[host];
211 while (1)
213 DWORD status = 0;
214 DWORD dwSize = sizeof(status);
216 bResults = WinHttpSendRequest(m_hRequest,
217 WINHTTP_NO_ADDITIONAL_HEADERS, 0,
218 WINHTTP_NO_REQUEST_DATA, 0,
219 0, 0);
221 // End the request.
222 if (bResults)
223 bResults = WinHttpReceiveResponse(m_hRequest, NULL);
225 // Resend the request in case of
226 // ERROR_WINHTTP_RESEND_REQUEST error.
227 if (!bResults && GetLastError() == ERROR_WINHTTP_RESEND_REQUEST)
228 continue;
230 // Check the status code.
231 if (bResults)
232 bResults = WinHttpQueryHeaders(m_hRequest,
233 WINHTTP_QUERY_STATUS_CODE |
234 WINHTTP_QUERY_FLAG_NUMBER,
235 NULL,
236 &status,
237 &dwSize,
238 NULL);
240 if (bResults)
242 DWORD dwSupportedSchemes;
243 DWORD dwFirstScheme;
244 DWORD dwSelectedScheme;
245 DWORD dwTarget;
247 switch (status)
249 case HTTP_STATUS_OK: //200
250 g_passwd_map[host] = up;
251 return 0;
253 case HTTP_STATUS_DENIED: //401
254 // The server requires authentication.
255 if(g_passwd_map[host].first.empty())
257 char user[MAX_USER_LEN];
258 char passwd[MAX_USER_LEN];
259 if (g_ask_passwd((char*)host.c_str(), user, passwd))
260 return -1;
262 up.first = user;
263 up.second = passwd;
265 // Obtain the supported and preferred schemes.
266 bResults = WinHttpQueryAuthSchemes(m_hRequest,
267 &dwSupportedSchemes,
268 &dwFirstScheme,
269 &dwTarget);
271 // Set the credentials before resending the request.
272 if (bResults)
274 dwSelectedScheme = ChooseAuthScheme(dwSupportedSchemes);
276 std::wstring_convert<std::codecvt_utf8<wchar_t>> convert;
278 if (dwSelectedScheme == 0)
280 set_last_err_string("unsupported http auth");
281 return -1;
283 else
284 bResults = WinHttpSetCredentials(m_hRequest,
285 dwTarget,
286 dwSelectedScheme,
287 convert.from_bytes(up.first).c_str(),
288 convert.from_bytes(up.second).c_str(),
289 NULL);
292 retry--;
293 if (!retry)
294 return -1;
296 break;
298 case HTTP_STATUS_PROXY_AUTH_REQ:
299 set_last_err_string("unsupport proxy auth");
300 return -1;
302 default:
303 g_passwd_map[host] = up;
304 // The status code does not indicate success.
305 string_ex str;
306 str.format("Error. Status code %d returned.\n", status);
307 set_last_err_string(str);
308 return -1;
313 return -1;
316 size_t HttpStream::HttpGetFileSize()
318 DWORD dwSize = 0;
319 BOOL bResults = FALSE;
320 wstring out;
322 WinHttpQueryHeaders(m_hRequest, WINHTTP_QUERY_CONTENT_LENGTH,
323 WINHTTP_HEADER_NAME_BY_INDEX, nullptr,
324 &dwSize, WINHTTP_NO_HEADER_INDEX);
326 // Allocate memory for the buffer.
327 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
329 out.resize(dwSize / sizeof(WCHAR));
331 // Now, use WinHttpQueryHeaders to retrieve the header.
332 bResults = WinHttpQueryHeaders(m_hRequest,
333 WINHTTP_QUERY_CONTENT_LENGTH,
334 WINHTTP_HEADER_NAME_BY_INDEX,
335 (LPVOID)out.c_str(), &dwSize,
336 WINHTTP_NO_HEADER_INDEX);
338 return _wtoll(out.c_str());
341 int HttpStream::HttpDownload(char *buff, size_t sz)
343 DWORD dwSize = 0;
344 DWORD dwDownloaded = 0;
345 while (sz)
347 if (!WinHttpQueryDataAvailable(m_hRequest, &dwSize))
349 set_last_err_string("WinHttpQueryDataAvailable");
350 return -1;
353 if (dwSize > sz)
354 dwSize = sz;
356 if (!WinHttpReadData(m_hRequest, (LPVOID)buff,
357 dwSize, &dwDownloaded))
359 set_last_err_string("Fail at WinHttpReadData");
360 return -1;
362 buff += dwDownloaded;
363 sz -= dwDownloaded;
365 return 0;
368 HttpStream::~HttpStream()
370 if (m_hRequest)
371 WinHttpCloseHandle(m_hRequest);
372 if (m_hConnect)
373 WinHttpCloseHandle(m_hConnect);
374 if (m_hSession)
375 WinHttpCloseHandle(m_hSession);
378 #else
380 HttpStream::HttpStream()
382 m_buff.empty();
385 int HttpStream::SendPacket(char *buff, size_t sz)
387 #ifdef UUUSSL
388 if(m_ssl)
389 return SSL_write((SSL*)m_ssl, buff, sz);
390 #endif
391 return send(m_socket, buff, sz, 0);
395 int HttpStream::RecvPacket(char *buff, size_t sz)
397 #ifdef UUUSSL
398 if(m_ssl)
399 return SSL_read((SSL*)m_ssl, buff, sz);
400 #endif
401 return recv(m_socket, buff, sz, 0);
404 class CAutoAddrInfo
406 addrinfo *m_p;
407 public:
408 CAutoAddrInfo(addrinfo *pAddrInfo)
410 m_p = pAddrInfo;
412 ~CAutoAddrInfo()
414 freeaddrinfo(m_p);
417 #include <iostream>
418 int HttpStream::HttpGetHeader(std::string host, std::string path, int port, bool ishttps)
420 int ret;
421 addrinfo *pAddrInfo;
422 char s_port[10];
423 snprintf(s_port, 10, "%d", port);
425 if (getaddrinfo(host.c_str(), s_port, 0, &pAddrInfo))
427 set_last_err_string("get network address error");
428 return -1;
431 CAutoAddrInfo A(pAddrInfo);
433 m_socket = socket(pAddrInfo->ai_family, pAddrInfo->ai_socktype, pAddrInfo->ai_protocol);
435 struct timeval tv;
436 tv.tv_sec = 10;
437 tv.tv_usec = 0;
439 setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
441 if (m_socket == INVALID_SOCKET)
443 set_last_err_string("Can't get sock");
444 return -1;
447 if (connect(m_socket, pAddrInfo->ai_addr, pAddrInfo->ai_addrlen))
449 set_last_err_string("connect error");
450 return -1;
453 if(ishttps)
455 #ifdef UUUSSL
457 const SSL_METHOD* meth =
458 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
459 TLSv1_2_client_method();
460 #else
461 TLS_client_method();
462 #endif
463 if(!meth)
465 set_last_err_string("Failure at TLSv1_2_client_method\n");
466 return -1;
468 SSL_CTX *ctx = SSL_CTX_new (meth);
469 if(!ctx)
471 set_last_err_string("Error create ssl ctx\n");
472 return -1;
474 m_ssl = SSL_new (ctx);
475 if(!m_ssl)
477 set_last_err_string("Error create SSL\n");
478 return -1;
480 SSL_set_fd((SSL*)m_ssl, m_socket);
481 if( SSL_connect((SSL*)m_ssl) <= 0)
483 set_last_err_string("error build ssl connection");
484 return -1;
486 #else
487 set_last_err_string("Can't support https");
488 return -1;
489 #endif
492 int retry = 3;
493 pair<string, string> up = g_passwd_map[host];
494 while(retry--)
496 string userpd = up.first + ":" + up.second;
497 string httppath = path;
499 if(ishttps)
500 httppath = "https://" + host + path;
502 string request = "GET " + httppath + " HTTP/1.1\r\n";
503 request += "Host: " + host + "\r\n";
505 if (!up.first.empty())
506 request += "Authorization: Basic " + base64_encode(userpd) + "\r\n";
508 request += "User-Agent:uuu\r\nAccept: */*\r\n";
509 request += "\r\n";
511 ret = SendPacket((char*)request.c_str(), request.size());
512 if (ret != request.size())
514 set_last_err_string("http send error");
515 return -1;
518 m_buff.resize(1024);
519 ret = RecvPacket((char*)m_buff.data(), m_buff.size());
520 if (ret < 0)
522 set_last_err_string("http recv Error");
523 return -1;
526 int i;
527 for (i = 0; i < 1024 - 4; i++)
529 if (m_buff[i] == 0xd &&
530 m_buff[i + 1] == 0xa &&
531 m_buff[i + 2] == 0xd &&
532 m_buff[i + 3] == 0xa)
534 break;
538 if (i >= 1024 - 4)
540 set_last_err_string("Can't find terminate");
541 return -1;
544 m_data_start = i + 4;
546 string str;
547 str.resize(i + 2);
548 memcpy((void*)str.c_str(), m_buff.data(), i + 2);
550 int ret = parser_response(str);
551 if (ret == ERR_ACCESS_DENIED)
553 if(g_passwd_map[host].first.empty())
555 char user[MAX_USER_LEN];
556 char passwd[MAX_USER_LEN];
557 if (g_ask_passwd((char*)host.c_str(), user, passwd))
558 return -1;
560 up.first = user;
561 up.second = passwd;
563 continue;
565 else if(ret == 0)
567 g_passwd_map[host] = up;
568 return 0;
570 g_passwd_map[host] = up;
573 return -1;
576 size_t HttpStream::HttpGetFileSize()
578 return atoll(m_response["Content-Length"].c_str());
581 int HttpStream::parser_response(string rep)
583 size_t pos = rep.find("\r\n");
584 if (pos == string::npos)
586 set_last_err_string("Can't find \r\n");
587 return -1;
590 string str = rep.substr(0, pos);
591 if (str == "HTTP/1.1 401 Unauthorized")
592 return ERR_ACCESS_DENIED;
594 if (str != "HTTP/1.1 200 OK")
596 set_last_err_string(str);
597 return -1;
600 m_response.clear();
602 while (pos != string::npos)
604 pos += 2;
605 size_t split = rep.find(':', pos);
606 if (split == string::npos)
607 break;
608 string key = rep.substr(pos, split - pos);
609 pos = rep.find("\r\n", pos);
610 string value = rep.substr(split + 1, pos - split - 1);
611 m_response[key] = value;
614 return 0;
617 int HttpStream::HttpDownload(char *buff, size_t sz)
619 size_t left = 0;
620 if (m_data_start < m_buff.size())
621 left = m_buff.size() - m_data_start;
623 size_t trim_transferred = 0;
625 if (left)
628 trim_transferred = sz;
629 if (trim_transferred > left)
630 trim_transferred = left;
632 memcpy(buff, m_buff.data() + m_data_start, trim_transferred);
633 m_data_start += trim_transferred;
636 if (trim_transferred < sz)
638 int ret = 0;
639 sz -= trim_transferred;
640 buff += trim_transferred;
641 while (sz && ((ret = RecvPacket(buff, sz)) > 0))
643 buff += ret;
644 sz -= ret;
647 if (ret < 0)
649 set_last_err_string("recv error");
650 return -1;
654 return 0;
657 HttpStream::~HttpStream()
659 close(m_socket);
660 #ifdef UUUSSL
661 if(m_ssl)
663 SSL_CTX_free(SSL_get_SSL_CTX((SSL*)m_ssl));
664 SSL_free((SSL*)m_ssl);
666 #endif
669 #endif