Bugfix: Status code in curl data is delimited by carriage return + line feed, so...
[bpdyndnsd] / src / ip_addr_helper.cpp
CommitLineData
0665b239
BS
1/** @file
2 * @brief IPHelper class implementation. This class represents a Helper to get the actual IP.
3 *
4 *
5 *
6 * @copyright Intra2net AG
7 * @license GPLv2
8*/
9
ad0e5016 10#include "ip_addr_helper.h"
0665b239 11#include <boost/asio.hpp>
1c0908b5 12#include <boost/regex.hpp>
0665b239
BS
13
14using namespace std;
15
16namespace net = boost::asio;
17
18/**
19 * Default constructor.
20 */
ad0e5016 21IPAddrHelper::IPAddrHelper()
b30f392d 22 : Log(new Logger)
2b0f7c11 23 , WebcheckInterval(0)
20399847 24 , LastWebcheck(0)
b30f392d 25 , ProxyPort(0)
0665b239
BS
26 , UseIPv6(false)
27{
28}
29
30
31/**
32 * Constructor.
33 */
20399847 34IPAddrHelper::IPAddrHelper(const Logger::Ptr _log, const string& _webcheck_url, const string& _webcheck_url_alt, const int _webcheck_interval, const int _last_webcheck ,const bool _use_ipv6, const string& _proxy, const int _proxy_port)
9a0aff44
BS
35 : Log(_log)
36 , WebcheckIpUrl(_webcheck_url)
b30f392d 37 , WebcheckIpUrlAlt(_webcheck_url_alt)
2b0f7c11 38 , WebcheckInterval(_webcheck_interval)
20399847 39 , LastWebcheck(_last_webcheck)
9a0aff44
BS
40 , Proxy(_proxy)
41 , ProxyPort(_proxy_port)
42 , UseIPv6(_use_ipv6)
0665b239 43{
019dc0d9 44 Hostname = net::ip::host_name();
019dc0d9 45 Log->print_hostname(Hostname);
0665b239
BS
46}
47
48
49/**
50 * Default destructor
51 */
ad0e5016 52IPAddrHelper::~IPAddrHelper()
0665b239
BS
53{
54}
55
56
57/**
4ef36a12
BS
58 * Tests if a given IP is a local address
59 * @param ip The IP to test
60 * @return true if given IP is local, false if not.
61 */
ad0e5016 62bool IPAddrHelper::is_local(const string ip) const
4ef36a12
BS
63{
64 // 127.0.0.1
65 boost::regex expr_loopback("127\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
66
67 // 192.168.x.x
68 boost::regex expr_192("192\\.168\\.[0-9]{1,3}\\.[0-9]{1,3}");
69
70 // 10.x.x.x
71 boost::regex expr_10("10\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}");
72
73 // 169.254.x.x
74 boost::regex expr_169_254("169\\.254\\.[0-9]{1,3}\\.[0-9]{1,3}");
75
76 // 172.16.x.x -> 172.31.x.x
77 boost::regex expr_172_1("172\\.1[6-9]{1}\\.[0-9]{1,3}\\.[0-9]{1,3}");
78 boost::regex expr_172_2("172\\.2[0-9]{1}\\.[0-9]{1,3}\\.[0-9]{1,3}");
79 boost::regex expr_172_3("172\\.3[0-1]{1}\\.[0-9]{1,3}\\.[0-9]{1,3}");
80
81 // It's time to test against the regex patterns
82 if ( boost::regex_search(ip,expr_loopback) )
83 {
84 Log->print_regex_match(expr_loopback.str(),ip);
85 return true;
86 }
87 else if ( boost::regex_search(ip,expr_192) )
88 {
89 Log->print_regex_match(expr_192.str(),ip);
90 return true;
91 }
92 else if ( boost::regex_search(ip,expr_10) )
93 {
94 Log->print_regex_match(expr_10.str(),ip);
95 return true;
96 }
97 else if ( boost::regex_search(ip,expr_169_254) )
98 {
99 Log->print_regex_match(expr_169_254.str(),ip);
100 return true;
101 }
102 else if ( boost::regex_search(ip,expr_172_1) )
103 {
104 Log->print_regex_match(expr_172_1.str(),ip);
105 return true;
106 }
107 else if ( boost::regex_search(ip,expr_172_2) )
108 {
109 Log->print_regex_match(expr_172_2.str(),ip);
110 return true;
111 }
112 else if ( boost::regex_search(ip,expr_172_3) )
113 {
114 Log->print_regex_match(expr_172_3.str(),ip);
115 return true;
116 }
117
118 return false;
119}
120
121
122/**
0665b239
BS
123 * Get the actual IP of this host through a conventional DNS query or through a IP webcheck URL if configured so.
124 * @return A string representation of the actual IP in dotted format or an empty string if something went wrong.
125 */
2b0f7c11 126string IPAddrHelper::get_actual_ip()
0665b239 127{
3c0cd271 128 string ip;
20a5e1e4 129 if ( WebcheckIpUrl.empty() )
0665b239 130 {
c3dea5dc 131 ip = dns_query("");
0665b239
BS
132 }
133 else
134 {
3c0cd271 135 ip = webcheck_ip();
0665b239 136 }
3c0cd271 137
4ef36a12
BS
138 // If IP is within a private range then return ""
139 if ( is_local(ip) )
140 {
141 Log->print_ip_is_local(ip);
142 return "";
143 }
3c0cd271
BS
144
145 return ip;
0665b239
BS
146}
147
0665b239
BS
148
149/**
687d99fb 150 * Get the actual IP of the given host through a DNS query.
c3dea5dc 151 * @param _hostname The hostname for the dns lookup, if empty string, then perform a dns lookup to the local hostname.
0665b239
BS
152 * @return A string representation of the actual IP in dotted format or an empty string if something went wrong.
153 */
ad0e5016 154string IPAddrHelper::dns_query(const string& _hostname) const
0665b239 155{
20a5e1e4
TJ
156 string ip_addr_v4;
157 string ip_addr_v6;
0665b239 158
c3dea5dc
BS
159 string hostname = Hostname;
160 if ( !_hostname.empty() )
161 hostname = _hostname;
162
0665b239
BS
163 try
164 {
165 // BOOST asio isn't the simplest way to perform a DNS lookup, but it works just fine.
166 net::io_service io_service;
167 net::ip::tcp::resolver resolver(io_service);
c3dea5dc 168 net::ip::tcp::resolver::query query(hostname, "0");
0665b239
BS
169 net::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
170 net::ip::tcp::resolver::iterator end;
171 while(endpoint_iterator != end)
172 {
687d99fb
BS
173 net::ip::address ip;
174 ip = endpoint_iterator->endpoint().address();
0665b239
BS
175 if ( ip.is_v4() )
176 ip_addr_v4 = ip.to_string();
177 else if ( ip.is_v6() )
178 ip_addr_v6 = ip.to_string();
c3dea5dc 179 Log->print_own_ipv4(ip_addr_v4, hostname);
019dc0d9 180 if ( UseIPv6 == true )
c3dea5dc 181 Log->print_own_ipv6(ip_addr_v6, hostname);
0665b239
BS
182 endpoint_iterator++;
183 }
184 io_service.reset();
185 }
186 catch(exception& e)
187 {
c3dea5dc 188 Log->print_error_hostname_to_ip(e.what(),hostname);
0665b239
BS
189 return "";
190 }
191
192 if ( (UseIPv6 == true) && (ip_addr_v6 != "") )
193 return ip_addr_v6;
194
195 return ip_addr_v4;
196}
197
198
199/**
200 * Get the actual IP of this host through a IP webcheck URL.
201 * @return A string representation of the actual IP in dotted format or an empty string if something went wrong.
202 */
2b0f7c11 203string IPAddrHelper::webcheck_ip()
0665b239 204{
2b0f7c11 205 // Init IPAddress with a empty string.
1c0908b5
BS
206 string ip_addr = "";
207
2b0f7c11
BS
208 // Get the current time.
209 int current_time = time(NULL);
210
211 // Test if webcheck is allowed due to webcheck_interval.
212 if ( (LastWebcheck + (WebcheckInterval*60)) >= current_time )
213 {
214 // Webcheck not allowed, log it and return empty string.
215 Log->print_webcheck_exceed_interval( LastWebcheck, (WebcheckInterval*60), current_time );
216 return ip_addr;
217 }
218
1c0908b5
BS
219 // Init CURL buffers
220 string curl_writedata_buff;
221 char curl_err_buff[CURL_ERROR_SIZE];
222 int curl_err_code=1;
223
224 // Init URL List
225 list<string> url_list;
226 url_list.push_back(WebcheckIpUrl);
227 url_list.push_back(WebcheckIpUrlAlt);
228 string actual_url;
229
230 // Init CURL
231 CURL * curl_easy_handle = init_curl(curl_writedata_buff,curl_err_buff);
232
233 // If perform_curl_operation returns a connection problem indicating return code, try the next ip webcheck url if there is one.
234 while ( (curl_err_code == 1) && (url_list.size() != 0) && (url_list.front() != "") )
235 {
236 // Set URL
237 actual_url = url_list.front();
238 url_list.pop_front();
239 set_curl_url(curl_easy_handle,actual_url);
240
2b0f7c11 241 // Perform curl operation, err_code of 1 indicated connection problem, so try next url.
1c0908b5
BS
242 curl_err_code = perform_curl_operation(curl_easy_handle, curl_err_buff, actual_url);
243 }
244
245 // Cleanup CURL handle
246 curl_easy_cleanup(curl_easy_handle);
247
248 // If curl_err_code is not 0, the ip couldn't be determined through any configured webcheck url.
249 if ( curl_err_code != 0 )
250 {
2b0f7c11 251 // Log it and return the empty string.
1c0908b5 252 Log->print_webcheck_no_ip();
1c0908b5
BS
253 return ip_addr;
254 }
255
2b0f7c11 256 // Log the received curl data.
1c0908b5
BS
257 Log->print_received_curl_data(curl_writedata_buff);
258
2b0f7c11 259 // Try to parse a IPAddress out of the received data.
1c0908b5 260 ip_addr = parse_ip(curl_writedata_buff);
0665b239 261
2b0f7c11
BS
262 // Set the LastWebcheck time to current time.
263 LastWebcheck = current_time;
264
265 // Return the parsed IPAddress.
0665b239
BS
266 return ip_addr;
267}
1c0908b5
BS
268
269
270/**
271 * Tries to find a valid IPv4 Address in dottet format in a given string and returns the IP-Address found.
272 * @param data The string data to search in for a valid IPv4-Address.
273 * @return The IP Address found or an empty string.
274 */
ad0e5016 275string IPAddrHelper::parse_ip(const string& data) const
1c0908b5
BS
276{
277 string ip = "";
278
2b0f7c11 279 // Regex for valid ip address
1c0908b5
BS
280 boost::regex expr("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
281
282 boost::smatch matches;
283
284 if ( boost::regex_search(data,matches,expr) )
285 {
286 ip = matches[1];
287 Log->print_regex_found_ip(ip);
288 }
289 else
290 {
291 Log->print_regex_ip_not_found(data);
292 }
293
294 return ip;
295}
296
297
298/**
299 * Performs the curl operation.
300 * @param curl_easy_handle The initialized and configured curl handle.
301 * @param curl_err_buff The pointer to the curl error buffer to get error messages from.
302 * @param actual_url The actual configured URL.
303 * @return 0 if all is fine, 1 if an connection problem to the configured url occurs, -1 on other errors.
304 */
ad0e5016 305int IPAddrHelper::perform_curl_operation(CURL * curl_easy_handle, char* curl_err_buff, const string& actual_url) const
1c0908b5
BS
306{
307 int curl_err_code;
308 if ( (curl_err_code = curl_easy_perform(curl_easy_handle) ) != 0 )
309 {
310 // CURL error occured
311 if ( (curl_err_code == CURLE_COULDNT_CONNECT) || (curl_err_code == CURLE_OPERATION_TIMEOUTED) || (curl_err_code == CURLE_COULDNT_RESOLVE_HOST) )
312 {
313 // In case of connection problems we should return 1, that the fallback url will be used.
314 Log->print_webcheck_url_connection_problem(curl_err_buff, actual_url);
315 return 1;
316 }
317 else
318 {
319 // other error
320 Log->print_webcheck_error(curl_err_buff, actual_url);
321 return -1;
322 }
323 }
324 // Operation performed without any problems
325 return 0;
326}
327
328
329/**
330 * Sets a url to the easy curl handle
331 * @param curl_easy_handle The easy curl handle to set the url for.
332 * @param url The url to set.
333 */
ad0e5016 334void IPAddrHelper::set_curl_url(CURL * curl_easy_handle, const string& url) const
1c0908b5
BS
335{
336 curl_easy_setopt(curl_easy_handle,CURLOPT_URL,url.c_str());
337}
338
339
340/**
341 * Initialized curl easy handle with a few options.
342 * @param curl_writedata_buff Reference to a string wich will be filled with the curl result
343 * @param curl_err_buff A pointer to an char array which will be filled with error message.
344 * @return A pointer to the easy curl handle.
345 */
ad0e5016 346CURL * IPAddrHelper::init_curl(string& curl_writedata_buff,char* curl_err_buff) const
1c0908b5
BS
347{
348 CURL *curl_easy_handle = curl_easy_init();
349
350 curl_easy_setopt(curl_easy_handle,CURLOPT_NOPROGRESS,1);
351 curl_easy_setopt(curl_easy_handle,CURLOPT_CONNECTTIMEOUT,5);
352 curl_easy_setopt(curl_easy_handle,CURLOPT_TIMEOUT,10);
353 curl_easy_setopt(curl_easy_handle,CURLOPT_BUFFERSIZE,1024);
354 curl_easy_setopt(curl_easy_handle,CURLOPT_ERRORBUFFER,curl_err_buff);
355 curl_easy_setopt(curl_easy_handle,CURLOPT_WRITEFUNCTION,http_receive);
356 curl_easy_setopt(curl_easy_handle,CURLOPT_WRITEDATA,&curl_writedata_buff);
357
4eb87664
BS
358 if ( !Proxy.empty() )
359 {
c93d5480 360 curl_easy_setopt(curl_easy_handle,CURLOPT_PROXY,Proxy.c_str());
4eb87664
BS
361 curl_easy_setopt(curl_easy_handle,CURLOPT_PROXYPORT,ProxyPort);
362 }
363
1c0908b5
BS
364 return curl_easy_handle;
365}
366
367
368/**
369 * Callback Function is called every time CURL is receiving data from HTTPS-Server and will copy all received Data to the given stream pointer
370 * @param inBuffer Pointer to input.
371 * @param size How many mem blocks are received
372 * @param nmemb size of each memblock
373 * @param outBuffer Pointer to output stream.
374 * @return The size received.
375 */
ad0e5016 376int IPAddrHelper::http_receive( char *inBuffer, size_t size, size_t nmemb, string *outBuffer )
1c0908b5
BS
377{
378 outBuffer->append(inBuffer);
379 return (size*nmemb);
380}
20399847
BS
381
382
383/**
384 * Get member LastWebcheck
385 * @return LastWebcheck
386 */
387int IPAddrHelper::get_last_webcheck() const
388{
389 return LastWebcheck;
390}