File indexing completed on 2024-04-28 16:43:06
0001 // SPDX-FileCopyrightText: 2022 Michael Lang <criticaltemp@protonmail.com> 0002 // 0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 0005 #include "ecurl.h" 0006 #include "modemcontroller.h" 0007 #include "settingsmanager.h" 0008 0009 #include <netdb.h> 0010 0011 ECurl::ECurl() 0012 { 0013 curl_global_init(CURL_GLOBAL_DEFAULT); 0014 ares_library_init(ARES_LIB_INIT_ALL); 0015 } 0016 0017 ECurl::~ECurl() 0018 { 0019 ares_library_cleanup(); 0020 curl_global_cleanup(); 0021 } 0022 0023 QByteArray ECurl::networkRequest(const QString &url, const QByteArray &data) const 0024 { 0025 CURL *curl = curl_easy_init(); 0026 if (!curl) { 0027 return QByteArray(); 0028 } 0029 struct curl_slist *host = NULL; 0030 0031 // provide a buffer to store errors in 0032 char errbuf[CURL_ERROR_SIZE]; 0033 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); 0034 0035 QString ifaceName = ModemController::instance().ifaceName; 0036 curl_easy_setopt(curl, CURLOPT_INTERFACE, (SL("if!") + ifaceName).toUtf8().constData()); 0037 0038 // only attempt to resolve if not using proxy 0039 if (SettingsManager::self()->mmsProxy().isEmpty()) { 0040 QString dnsServers = ModemController::instance().dnsServers; 0041 CURLcode supported = curl_easy_setopt(curl, CURLOPT_DNS_INTERFACE, ifaceName.toUtf8().constData()); 0042 0043 // use c-ares if curl was built without dns resolver support 0044 if (supported == CURLE_UNKNOWN_OPTION || supported == CURLE_NOT_BUILT_IN) { 0045 ares_channel channel; 0046 0047 int aresReturn = ares_init(&channel); 0048 0049 if (aresReturn != ARES_SUCCESS) { 0050 qDebug() << "Ares init failed:" << ares_strerror(aresReturn); 0051 } else { 0052 ares_set_local_dev(channel, ifaceName.toUtf8().constData()); 0053 ares_set_servers_csv(channel, dnsServers.toUtf8().constData()); 0054 0055 CURLU *curlUrl = curl_url(); 0056 curl_url_set(curlUrl, CURLUPART_URL, url.toUtf8().constData(), 0); 0057 char *hostname = url.toUtf8().data(); 0058 curl_url_get(curlUrl, CURLUPART_HOST, &hostname, 0); 0059 curl_url_cleanup(curlUrl); 0060 0061 char *hostIp = NULL; 0062 ares_gethostbyname(channel, hostname, AF_UNSPEC, aresResolveCallback, (void *)&hostIp); 0063 aresResolveWait(channel); 0064 0065 if (hostIp == NULL) { 0066 qDebug() << "Failed to resolve:" << hostname; 0067 } 0068 0069 const char *resolve = QByteArray("+").append(hostname).append(":80:[").append(hostIp).append("]").constData(); 0070 host = curl_slist_append(NULL, resolve); 0071 curl_easy_setopt(curl, CURLOPT_RESOLVE, host); 0072 0073 curl_free(hostname); 0074 ares_destroy(channel); 0075 } 0076 } else { 0077 curl_easy_setopt(curl, CURLOPT_DNS_SERVERS, dnsServers.toUtf8().constData()); 0078 } 0079 } 0080 0081 curl_easy_setopt(curl, CURLOPT_URL, url.toUtf8().constData()); 0082 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L); 0083 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60L); 0084 curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 30L); 0085 0086 // Fake user agent for carrier network compatibility 0087 curl_easy_setopt(curl, CURLOPT_USERAGENT, "Android MmsLib/1.0"); 0088 0089 if (!SettingsManager::self()->mmsProxy().isEmpty()) { 0090 QString proxy = SettingsManager::self()->mmsProxy() + SL(":") + QString::number(SettingsManager::self()->mmsPort()); 0091 curl_easy_setopt(curl, CURLOPT_PROXY, proxy.toUtf8().constData()); 0092 } 0093 0094 if (!data.isEmpty()) { 0095 curl_easy_setopt(curl, CURLOPT_POST, 1L); 0096 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data.constData()); 0097 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, data.length()); 0098 0099 QString len = QString::number(data.length()); 0100 struct curl_slist *headers = NULL; 0101 headers = curl_slist_append(headers, "Content-Type: application/vnd.wap.mms-message"); 0102 headers = curl_slist_append(headers, (SL("Content-Length: ") + len).toUtf8().constData()); 0103 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); 0104 } 0105 0106 QByteArray response; 0107 const QByteArray *pointer = &response; 0108 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curlWriteFunction); 0109 curl_easy_setopt(curl, CURLOPT_WRITEDATA, pointer); 0110 0111 CURLcode res = curl_easy_perform(curl); 0112 curl_easy_cleanup(curl); 0113 curl_slist_free_all(host); 0114 0115 if (res != CURLE_OK) { 0116 qDebug() << "Network Error:" << errbuf; 0117 return QByteArray(); 0118 } else { 0119 return response; 0120 } 0121 } 0122 0123 size_t ECurl::curlWriteFunction(char *chunk, size_t size, size_t len, QByteArray *response) 0124 { 0125 response->append(chunk, static_cast<int>(size * len)); 0126 return size * len; 0127 } 0128 0129 void ECurl::aresResolveCallback(void *arg, int status, int timeouts, struct hostent *host) 0130 { 0131 char **hostIp = (char **)arg; 0132 0133 // when the ares handle is getting destroyed, the 'arg' pointer may not 0134 // be valid so only defer it when we know the 'status' says its fine! 0135 if (!host || status != ARES_SUCCESS) { 0136 return; 0137 } 0138 0139 char ip[INET6_ADDRSTRLEN]; 0140 0141 for (int i = 0; host->h_addr_list[i]; ++i) { 0142 ares_inet_ntop(host->h_addrtype, host->h_addr_list[i], ip, sizeof(ip)); 0143 if (i > 0) { 0144 strcat(*hostIp, ","); 0145 strcat(*hostIp, strdup(ip)); 0146 } else { 0147 *hostIp = strdup(ip); 0148 } 0149 0150 qDebug() << "Found IP for" << host->h_name << *hostIp; 0151 } 0152 0153 (void)timeouts; // ignored 0154 } 0155 0156 void ECurl::aresResolveWait(ares_channel channel) 0157 { 0158 qint64 start = QTime::currentTime().msecsSinceStartOfDay(); 0159 0160 for (;;) { 0161 struct timeval *tvp, tv; 0162 fd_set read_fds, write_fds; 0163 int nfds; 0164 0165 FD_ZERO(&read_fds); 0166 FD_ZERO(&write_fds); 0167 nfds = ares_fds(channel, &read_fds, &write_fds); 0168 if (nfds == 0) { 0169 break; 0170 } 0171 tvp = ares_timeout(channel, NULL, &tv); 0172 int count = select(nfds, &read_fds, &write_fds, NULL, tvp); 0173 if (count == -1) { 0174 qDebug() << "Error waiting for c-ares read/write descriptors"; 0175 break; 0176 } 0177 if (QTime::currentTime().msecsSinceStartOfDay() - start > 15 * 1000) { 0178 qDebug() << "Resovler timeout"; 0179 break; 0180 } 0181 ares_process(channel, &read_fds, &write_fds); 0182 } 0183 }