]> git.ipfire.org Git - thirdparty/pdns.git/blob - pdns/minicurl.cc
915eeecb133d7d58553c142cdd82897ee0b01129
[thirdparty/pdns.git] / pdns / minicurl.cc
1 /*
2 * MIT License
3 *
4 * Copyright (c) 2018-2019 powerdns.com bv
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in all
14 * copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
25 #include "minicurl.hh"
26 #include <curl/curl.h>
27 #include <stdexcept>
28
29 MiniCurl::MiniCurl(const string& useragent)
30 {
31 d_curl = curl_easy_init();
32 if (d_curl != nullptr) {
33 curl_easy_setopt(d_curl, CURLOPT_USERAGENT, useragent.c_str());
34 }
35 }
36
37 MiniCurl::~MiniCurl()
38 {
39 // NEEDS TO CLEAN HOSTLIST
40 curl_easy_cleanup(d_curl);
41 }
42
43 size_t MiniCurl::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
44 {
45 MiniCurl* us = (MiniCurl*)userdata;
46 us->d_data.append(ptr, size*nmemb);
47 return size*nmemb;
48 }
49
50 static string extractHostFromURL(const std::string& url)
51 {
52 auto pos = url.find("://");
53 if(pos == string::npos)
54 throw runtime_error("Can't find host part of '"+url+"'");
55 pos += 3;
56 auto endpos = url.find('/', pos);
57 if(endpos == string::npos)
58 return url.substr(pos);
59
60 return url.substr(pos, endpos-pos);
61 }
62
63 void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
64 {
65 if(rem) {
66 struct curl_slist *hostlist = nullptr; // THIS SHOULD BE FREED
67
68 // url = http://hostname.enzo/url
69 string host4=extractHostFromURL(str);
70 // doest the host contain port indication
71 std::size_t found = host4.find(':');
72 vector<uint16_t> ports{80, 443};
73 if (found != std::string::npos) {
74 int port = std::stoi(host4.substr(found + 1));
75 if (port <= 0 || port > 65535)
76 throw std::overflow_error("Invalid port number");
77 ports = {(uint16_t)port};
78 host4 = host4.substr(0, found);
79 }
80
81 for (const auto& port : ports) {
82 string hcode = boost::str(boost::format("%s:%u:%s") % host4 % port % rem->toString());
83 hostlist = curl_slist_append(hostlist, hcode.c_str());
84 }
85
86 curl_easy_setopt(d_curl, CURLOPT_RESOLVE, hostlist);
87 }
88 if(src) {
89 curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str());
90 }
91 curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, true);
92 /* only allow HTTP, TFTP and SFTP */
93 curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
94 curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, false);
95 curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, false);
96 curl_easy_setopt(d_curl, CURLOPT_FAILONERROR, true);
97 curl_easy_setopt(d_curl, CURLOPT_URL, str.c_str());
98 curl_easy_setopt(d_curl, CURLOPT_WRITEFUNCTION, write_callback);
99 curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this);
100 curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 2L);
101
102 d_data.clear();
103 }
104 std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
105 {
106 setupURL(str, rem, src);
107 auto res = curl_easy_perform(d_curl);
108 long http_code = 0;
109 curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
110
111 if(res != CURLE_OK || http_code != 200) {
112 throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
113 }
114 std::string ret=d_data;
115 d_data.clear();
116 return ret;
117 }
118
119 std::string MiniCurl::postURL(const std::string& str, const std::string& postdata)
120 {
121 setupURL(str);
122 curl_easy_setopt(d_curl, CURLOPT_POSTFIELDS, postdata.c_str());
123
124 auto res = curl_easy_perform(d_curl);
125 if(res != CURLE_OK)
126 throw std::runtime_error("Unable to post URL");
127
128 std::string ret=d_data;
129
130 d_data.clear();
131 return ret;
132 }