Commit | Line | Data |
---|---|---|
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 | |
14 | using namespace std; | |
15 | ||
16 | namespace net = boost::asio; | |
17 | ||
18 | /** | |
19 | * Default constructor. | |
20 | */ | |
ad0e5016 | 21 | IPAddrHelper::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 | 34 | IPAddrHelper::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 | 52 | IPAddrHelper::~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 | 62 | bool 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 | 126 | string 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 | 154 | string 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 | 203 | string 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 | 275 | string 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 | 305 | int 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 | 334 | void 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 | 346 | CURL * 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 | 376 | int 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 | */ | |
387 | int IPAddrHelper::get_last_webcheck() const | |
388 | { | |
389 | return LastWebcheck; | |
390 | } |