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