]> git.ipfire.org Git - thirdparty/pdns.git/blame - pdns/minicurl.cc
rec: ensure correct service user on debian
[thirdparty/pdns.git] / pdns / minicurl.cc
CommitLineData
eab9b173
CHB
1/*
2 * MIT License
3 *
79fb4337 4 * Copyright (c) 2018-2019 powerdns.com bv
eab9b173
CHB
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
5f7d5c56 25#include "minicurl.hh"
26#include <curl/curl.h>
27#include <stdexcept>
28
8340b048
RG
29void MiniCurl::init()
30{
31 static std::atomic_flag s_init = ATOMIC_FLAG_INIT;
32
33 if (s_init.test_and_set())
34 return;
35
36 CURLcode code = curl_global_init(CURL_GLOBAL_ALL);
37 if (code != 0) {
38 throw std::runtime_error("Error initializing libcurl");
39 }
40}
41
63dfa8df 42MiniCurl::MiniCurl(const string& useragent)
5f7d5c56 43{
44 d_curl = curl_easy_init();
8340b048
RG
45 if (d_curl == nullptr) {
46 throw std::runtime_error("Error creating a MiniCurl session");
63dfa8df 47 }
8340b048 48 curl_easy_setopt(d_curl, CURLOPT_USERAGENT, useragent.c_str());
5f7d5c56 49}
50
51MiniCurl::~MiniCurl()
52{
5ca4872a 53 // NEEDS TO CLEAN HOSTLIST
5f7d5c56 54 curl_easy_cleanup(d_curl);
55}
56
57size_t MiniCurl::write_callback(char *ptr, size_t size, size_t nmemb, void *userdata)
58{
59 MiniCurl* us = (MiniCurl*)userdata;
60 us->d_data.append(ptr, size*nmemb);
61 return size*nmemb;
62}
63
64static string extractHostFromURL(const std::string& url)
65{
66 auto pos = url.find("://");
67 if(pos == string::npos)
68 throw runtime_error("Can't find host part of '"+url+"'");
69 pos += 3;
70 auto endpos = url.find('/', pos);
71 if(endpos == string::npos)
72 return url.substr(pos);
73
74 return url.substr(pos, endpos-pos);
75}
76
25bcfaec 77void MiniCurl::setupURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
5f7d5c56 78{
79 if(rem) {
1bc56192 80 struct curl_slist *hostlist = nullptr; // THIS SHOULD BE FREED
5f7d5c56 81
eab9b173 82 // url = http://hostname.enzo/url
5f7d5c56 83 string host4=extractHostFromURL(str);
1bc56192
CHB
84 // doest the host contain port indication
85 std::size_t found = host4.find(':');
86 vector<uint16_t> ports{80, 443};
87 if (found != std::string::npos) {
88 int port = std::stoi(host4.substr(found + 1));
89 if (port <= 0 || port > 65535)
90 throw std::overflow_error("Invalid port number");
91 ports = {(uint16_t)port};
92 host4 = host4.substr(0, found);
93 }
94
95 for (const auto& port : ports) {
96 string hcode = boost::str(boost::format("%s:%u:%s") % host4 % port % rem->toString());
97 hostlist = curl_slist_append(hostlist, hcode.c_str());
98 }
5f7d5c56 99
100 curl_easy_setopt(d_curl, CURLOPT_RESOLVE, hostlist);
101 }
25bcfaec 102 if(src) {
89ff3721 103 curl_easy_setopt(d_curl, CURLOPT_INTERFACE, src->toString().c_str());
25bcfaec 104 }
5f7d5c56 105 curl_easy_setopt(d_curl, CURLOPT_FOLLOWLOCATION, true);
c6bf4c23 106 /* only allow HTTP and HTTPS */
5f7d5c56 107 curl_easy_setopt(d_curl, CURLOPT_PROTOCOLS, CURLPROTO_HTTP | CURLPROTO_HTTPS);
108 curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYPEER, false);
109 curl_easy_setopt(d_curl, CURLOPT_SSL_VERIFYHOST, false);
110 curl_easy_setopt(d_curl, CURLOPT_FAILONERROR, true);
111 curl_easy_setopt(d_curl, CURLOPT_URL, str.c_str());
112 curl_easy_setopt(d_curl, CURLOPT_WRITEFUNCTION, write_callback);
113 curl_easy_setopt(d_curl, CURLOPT_WRITEDATA, this);
e24b5737 114 curl_easy_setopt(d_curl, CURLOPT_TIMEOUT, 2L);
eab9b173 115
6ce74194 116 clearHeaders();
5f7d5c56 117 d_data.clear();
118}
8340b048 119
25bcfaec 120std::string MiniCurl::getURL(const std::string& str, const ComboAddress* rem, const ComboAddress* src)
5f7d5c56 121{
25bcfaec 122 setupURL(str, rem, src);
5f7d5c56 123 auto res = curl_easy_perform(d_curl);
124 long http_code = 0;
125 curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
126
127 if(res != CURLE_OK || http_code != 200) {
128 throw std::runtime_error("Unable to retrieve URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
129 }
130 std::string ret=d_data;
131 d_data.clear();
132 return ret;
133}
134
6ce74194 135std::string MiniCurl::postURL(const std::string& str, const std::string& postdata, MiniCurlHeaders& headers)
5f7d5c56 136{
137 setupURL(str);
6ce74194
PD
138 setHeaders(headers);
139 curl_easy_setopt(d_curl, CURLOPT_POSTFIELDSIZE, postdata.size());
5f7d5c56 140 curl_easy_setopt(d_curl, CURLOPT_POSTFIELDS, postdata.c_str());
141
142 auto res = curl_easy_perform(d_curl);
6ce74194
PD
143
144 long http_code = 0;
145 curl_easy_getinfo(d_curl, CURLINFO_RESPONSE_CODE, &http_code);
146
eab9b173 147 if(res != CURLE_OK)
6ce74194 148 throw std::runtime_error("Unable to post URL ("+std::to_string(http_code)+"): "+string(curl_easy_strerror(res)));
5f7d5c56 149
150 std::string ret=d_data;
151
152 d_data.clear();
153 return ret;
154}
6ce74194
PD
155
156void MiniCurl::clearHeaders()
157{
158 if (d_curl) {
159 curl_easy_setopt(d_curl, CURLOPT_HTTPHEADER, NULL);
160 if (d_header_list != nullptr) {
161 curl_slist_free_all(d_header_list);
162 d_header_list = nullptr;
163 }
164 }
165}
166
167void MiniCurl::setHeaders(const MiniCurlHeaders& headers)
168{
169 if (d_curl) {
170 for (auto& header : headers) {
171 std::stringstream header_ss;
172 header_ss << header.first << ": " << header.second;
173 d_header_list = curl_slist_append(d_header_list, header_ss.str().c_str());
174 }
175 curl_easy_setopt(d_curl, CURLOPT_HTTPHEADER, d_header_list);
176 }
177}