Improved logging and added code to handle failed dns lookup of cached dns record.
[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()
22 : Hostname("")
019dc0d9
BS
23 , WebcheckIpUrl("")
24 , WebcheckIpUrlAlt("")
0665b239 25 , UseIPv6(false)
68c6b4af 26 , Log(new Logger)
0665b239
BS
27{
28}
29
30
31/**
32 * Constructor.
33 */
019dc0d9 34IPHelper::IPHelper(const Logger::Ptr _log, const string& _webcheck_url, const string& _webcheck_url_alt, const bool _use_ipv6)
0665b239 35 : Hostname("")
0665b239
BS
36{
37 Log = _log;
019dc0d9
BS
38 WebcheckIpUrl = _webcheck_url;
39 WebcheckIpUrlAlt = _webcheck_url_alt;
40 UseIPv6 = _use_ipv6;
41 Hostname = net::ip::host_name();
42
43 Log->print_hostname(Hostname);
0665b239
BS
44}
45
46
47/**
48 * Default destructor
49 */
50IPHelper::~IPHelper()
51{
52}
53
54
55/**
0665b239
BS
56 * Get the actual IP of this host through a conventional DNS query or through a IP webcheck URL if configured so.
57 * @return A string representation of the actual IP in dotted format or an empty string if something went wrong.
58 */
59string IPHelper::get_actual_ip() const
60{
3c0cd271 61 string ip;
20a5e1e4 62 if ( WebcheckIpUrl.empty() )
0665b239 63 {
c3dea5dc 64 ip = dns_query("");
0665b239
BS
65 }
66 else
67 {
3c0cd271 68 ip = webcheck_ip();
0665b239 69 }
3c0cd271
BS
70
71 // TODO IF IP is local, then return ""
72
73 return ip;
0665b239
BS
74}
75
0665b239
BS
76
77/**
78 * Get the actual IP of this host through a DNS query.
c3dea5dc 79 * @param _hostname The hostname for the dns lookup, if empty string, then perform a dns lookup to the local hostname.
0665b239
BS
80 * @return A string representation of the actual IP in dotted format or an empty string if something went wrong.
81 */
c3dea5dc 82string IPHelper::dns_query(const string& _hostname) const
0665b239 83{
20a5e1e4
TJ
84 string ip_addr_v4;
85 string ip_addr_v6;
0665b239 86
c3dea5dc
BS
87 string hostname = Hostname;
88 if ( !_hostname.empty() )
89 hostname = _hostname;
90
0665b239
BS
91 try
92 {
93 // BOOST asio isn't the simplest way to perform a DNS lookup, but it works just fine.
94 net::io_service io_service;
95 net::ip::tcp::resolver resolver(io_service);
c3dea5dc 96 net::ip::tcp::resolver::query query(hostname, "0");
0665b239
BS
97 net::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
98 net::ip::tcp::resolver::iterator end;
99 while(endpoint_iterator != end)
100 {
019dc0d9
BS
101 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
102 net::ip::address ip = endpoint.address(); // but why?
0665b239
BS
103 if ( ip.is_v4() )
104 ip_addr_v4 = ip.to_string();
105 else if ( ip.is_v6() )
106 ip_addr_v6 = ip.to_string();
c3dea5dc 107 Log->print_own_ipv4(ip_addr_v4, hostname);
019dc0d9 108 if ( UseIPv6 == true )
c3dea5dc 109 Log->print_own_ipv6(ip_addr_v6, hostname);
0665b239
BS
110 endpoint_iterator++;
111 }
112 io_service.reset();
113 }
114 catch(exception& e)
115 {
c3dea5dc 116 Log->print_error_hostname_to_ip(e.what(),hostname);
0665b239
BS
117 return "";
118 }
119
120 if ( (UseIPv6 == true) && (ip_addr_v6 != "") )
121 return ip_addr_v6;
122
123 return ip_addr_v4;
124}
125
126
127/**
128 * Get the actual IP of this host through a IP webcheck URL.
129 * @return A string representation of the actual IP in dotted format or an empty string if something went wrong.
130 */
131string IPHelper::webcheck_ip() const
132{
1c0908b5
BS
133 string ip_addr = "";
134
135 // Init CURL buffers
136 string curl_writedata_buff;
137 char curl_err_buff[CURL_ERROR_SIZE];
138 int curl_err_code=1;
139
140 // Init URL List
141 list<string> url_list;
142 url_list.push_back(WebcheckIpUrl);
143 url_list.push_back(WebcheckIpUrlAlt);
144 string actual_url;
145
146 // Init CURL
147 CURL * curl_easy_handle = init_curl(curl_writedata_buff,curl_err_buff);
148
149 // If perform_curl_operation returns a connection problem indicating return code, try the next ip webcheck url if there is one.
150 while ( (curl_err_code == 1) && (url_list.size() != 0) && (url_list.front() != "") )
151 {
152 // Set URL
153 actual_url = url_list.front();
154 url_list.pop_front();
155 set_curl_url(curl_easy_handle,actual_url);
156
157 // Perform curl operation
158 curl_err_code = perform_curl_operation(curl_easy_handle, curl_err_buff, actual_url);
159 }
160
161 // Cleanup CURL handle
162 curl_easy_cleanup(curl_easy_handle);
163
164 // If curl_err_code is not 0, the ip couldn't be determined through any configured webcheck url.
165 if ( curl_err_code != 0 )
166 {
167 Log->print_webcheck_no_ip();
168 // error handling
169 return ip_addr;
170 }
171
172 Log->print_received_curl_data(curl_writedata_buff);
173
174 ip_addr = parse_ip(curl_writedata_buff);
0665b239
BS
175
176 return ip_addr;
177}
1c0908b5
BS
178
179
180/**
181 * Tries to find a valid IPv4 Address in dottet format in a given string and returns the IP-Address found.
182 * @param data The string data to search in for a valid IPv4-Address.
183 * @return The IP Address found or an empty string.
184 */
185string IPHelper::parse_ip(const string& data) const
186{
187 string ip = "";
188
189 // regex for valid ip address
190 boost::regex expr("([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})");
191
192 boost::smatch matches;
193
194 if ( boost::regex_search(data,matches,expr) )
195 {
196 ip = matches[1];
197 Log->print_regex_found_ip(ip);
198 }
199 else
200 {
201 Log->print_regex_ip_not_found(data);
202 }
203
204 return ip;
205}
206
207
208/**
209 * Performs the curl operation.
210 * @param curl_easy_handle The initialized and configured curl handle.
211 * @param curl_err_buff The pointer to the curl error buffer to get error messages from.
212 * @param actual_url The actual configured URL.
213 * @return 0 if all is fine, 1 if an connection problem to the configured url occurs, -1 on other errors.
214 */
215int IPHelper::perform_curl_operation(CURL * curl_easy_handle, char* curl_err_buff, const string& actual_url) const
216{
217 int curl_err_code;
218 if ( (curl_err_code = curl_easy_perform(curl_easy_handle) ) != 0 )
219 {
220 // CURL error occured
221 if ( (curl_err_code == CURLE_COULDNT_CONNECT) || (curl_err_code == CURLE_OPERATION_TIMEOUTED) || (curl_err_code == CURLE_COULDNT_RESOLVE_HOST) )
222 {
223 // In case of connection problems we should return 1, that the fallback url will be used.
224 Log->print_webcheck_url_connection_problem(curl_err_buff, actual_url);
225 return 1;
226 }
227 else
228 {
229 // other error
230 Log->print_webcheck_error(curl_err_buff, actual_url);
231 return -1;
232 }
233 }
234 // Operation performed without any problems
235 return 0;
236}
237
238
239/**
240 * Sets a url to the easy curl handle
241 * @param curl_easy_handle The easy curl handle to set the url for.
242 * @param url The url to set.
243 */
244void IPHelper::set_curl_url(CURL * curl_easy_handle, const string& url) const
245{
246 curl_easy_setopt(curl_easy_handle,CURLOPT_URL,url.c_str());
247}
248
249
250/**
251 * Initialized curl easy handle with a few options.
252 * @param curl_writedata_buff Reference to a string wich will be filled with the curl result
253 * @param curl_err_buff A pointer to an char array which will be filled with error message.
254 * @return A pointer to the easy curl handle.
255 */
256CURL * IPHelper::init_curl(string& curl_writedata_buff,char* curl_err_buff) const
257{
258 CURL *curl_easy_handle = curl_easy_init();
259
260 curl_easy_setopt(curl_easy_handle,CURLOPT_NOPROGRESS,1);
261 curl_easy_setopt(curl_easy_handle,CURLOPT_CONNECTTIMEOUT,5);
262 curl_easy_setopt(curl_easy_handle,CURLOPT_TIMEOUT,10);
263 curl_easy_setopt(curl_easy_handle,CURLOPT_BUFFERSIZE,1024);
264 curl_easy_setopt(curl_easy_handle,CURLOPT_ERRORBUFFER,curl_err_buff);
265 curl_easy_setopt(curl_easy_handle,CURLOPT_WRITEFUNCTION,http_receive);
266 curl_easy_setopt(curl_easy_handle,CURLOPT_WRITEDATA,&curl_writedata_buff);
267
268 return curl_easy_handle;
269}
270
271
272/**
273 * Callback Function is called every time CURL is receiving data from HTTPS-Server and will copy all received Data to the given stream pointer
274 * @param inBuffer Pointer to input.
275 * @param size How many mem blocks are received
276 * @param nmemb size of each memblock
277 * @param outBuffer Pointer to output stream.
278 * @return The size received.
279 */
280int IPHelper::http_receive( char *inBuffer, size_t size, size_t nmemb, string *outBuffer )
281{
282 outBuffer->append(inBuffer);
283 return (size*nmemb);
284}