mavtables  0.2.1
MAVLink router and firewall.
IPAddress.cpp
Go to the documentation of this file.
1 // MAVLink router and firewall.
2 // Copyright (C) 2017-2018 Michael R. Shannon <mrshannon.aerospace@gmail.com>
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 
17 
18 #include <algorithm>
19 #include <memory>
20 #include <set>
21 #include <sstream>
22 #include <stdexcept>
23 #include <string>
24 #include <vector>
25 
26 #include <cstring>
27 #include <netdb.h>
28 #include <netinet/in.h>
29 
30 #include <boost/algorithm/string.hpp>
31 
32 #include "config.hpp"
33 #include "DNSLookupError.hpp"
34 #include "IPAddress.hpp"
35 #include "utility.hpp"
36 
37 
38 // Private functions.
39 #ifdef UNIX
40 namespace
41 {
42  IPAddress unix_dnslookup(const std::string &url, unsigned int port);
43 }
44 #elif WINDOWS
45 #endif
46 
47 
48 /** Construct IP address from address and port number.
49  *
50  * \param address Numeric (32 bit) IP address.
51  * \param port 16 bit port number.
52  * \throws std::out_of_range if either the IP address or the port number is
53  * outside of the respectively allowed 32 or 16 bit ranges.
54  */
55 void IPAddress::construct_(unsigned long address, unsigned int port)
56 {
57  if (address > 0xFFFFFFFF)
58  {
59  std::stringstream ss;
60  ss << "Address (0x"
61  << std::uppercase << std::hex << address << std::nouppercase
62  << ") is outside of the allowed range (0x00000000 - 0xFFFFFFFF).";
63  throw std::out_of_range(ss.str());
64  }
65 
66  if (port > 65535)
67  {
68  throw std::out_of_range(
69  "port number (" + std::to_string(port) +
70  ") is outside of the allowed range (0 - 65535).");
71  }
72 
73  address_ = address;
74  port_ = port;
75 }
76 
77 
78 /** Construct IP address from another IP address, changing the port number.
79  *
80  * Copy constructor that also changes the port.
81  *
82  * \param other IP address to copy from.
83  * \param port Port number (0 - 65535). A port number of 0 has the special
84  * meaning of no specific port.
85  * \throws std::out_of_range if the port number is outside of the allowed 16
86  * bit range.
87  */
88 IPAddress::IPAddress(const IPAddress &other, unsigned int port)
89 {
90  construct_(other.address_, port);
91 }
92 
93 
94 /** Construct IP address from address and port number.
95  *
96  * \param address 32-bit IP address in system byte order (0x00000000 -
97  * 0xFFFFFFFF).
98  * \param port Port number (0 - 65535). A port number of 0 has the special
99  * meaning of no specific port.
100  * \throws std::out_of_range if either the IP address or the port number is
101  * outside of the respectively allowed 32 or 16 bit ranges.
102  */
103 IPAddress::IPAddress(unsigned long address, unsigned int port)
104 {
105  construct_(address, port);
106 }
107 
108 
109 /** Construct IP address from a string.
110  *
111  * Parse a string of the form "<IP Address>" or "<IP Address>:<Port Number>".
112  *
113  * Some examples are:
114  * - "127.0.0.1"
115  * - "127.0.0.1:8888"
116  * - "183.125.120.42:443"
117  *
118  * %If no port is given the 0 port is used which represents no specific port.
119  *
120  * \param address String representing the IP address and optionally the port
121  * number.
122  * \throws std::invalid_argument if the string does not represent a valid IP
123  * address.
124  * \throws std::out_of_range if an address octet or the port number is out of
125  * range.
126  */
127 IPAddress::IPAddress(std::string address)
128 {
129  // Separate port from address.
130  unsigned int port = 0;
131  std::vector<std::string> parts;
132  boost::split(parts, address, [](char c)
133  {
134  return c == ':';
135  });
136 
137  // Read port.
138  if (parts.size() == 2)
139  {
140  std::istringstream(parts.back()) >> port;
141  parts.pop_back();
142  }
143 
144  if (parts.size() != 1)
145  {
146  throw std::invalid_argument("Invalid IP address string.");
147  }
148 
149  address = parts.back();
150 
151  // Check validity of address string.
152  if (address.size() < 7 || !(isdigit(address.front()))
153  || !isdigit(address.back()))
154  {
155  throw std::invalid_argument("Invalid IP address string.");
156  }
157 
158  for (auto c : address)
159  {
160  if (!(c == '.' || isdigit(c)))
161  {
162  throw std::invalid_argument("Invalid IP address string.");
163  }
164  }
165 
166  // Read address.
167  std::replace(address.begin(), address.end(), '.', ' ');
168  std::vector<unsigned long> octets;
169  std::istringstream ss(address);
170  unsigned long octet;
171 
172  while (ss >> octet)
173  {
174  octets.push_back(octet);
175  }
176 
177  // Ensure proper number of octets.
178  if (octets.size() != 4)
179  {
180  throw std::invalid_argument("Invalid IP address string.");
181  }
182 
183  // Ensure octets are between 0 and 255.
184  for (auto i : octets)
185  if (i > 255)
186  {
187  throw std::out_of_range(
188  "Address octet (" + std::to_string(i) +
189  ") is outside of the allowed range (0 - 255).");
190  }
191 
192  {
193  }
194 
195  construct_((octets[0] << 8 * 3) | (octets[1] << 8 * 2) |
196  (octets[2] << 8) | octets[3], port);
197 }
198 
199 
200 /** Return the IP address.
201  *
202  * \returns The 32-bit IP address (in system byte order) as an integer
203  * (0x00000000 - 0xFFFFFFFF).
204  */
205 unsigned long IPAddress::address() const
206 {
207  return address_;
208 }
209 
210 
211 /** Return the port.
212  *
213  * \returns The port number (0 - 65535).
214  */
215 unsigned int IPAddress::port() const
216 {
217  return port_;
218 }
219 
220 
221 /** Equality comparison.
222  *
223  * \note Compares address and port number.
224  *
225  * \relates IPAddress
226  * \param lhs The left hand side IP address.
227  * \param rhs The right hand side IP address.
228  * \retval true if \p lhs and \p rhs have the same address and port.
229  * \retval false if \p lhs and \p rhs do not have the same address and port.
230  */
231 bool operator==(const IPAddress &lhs, const IPAddress &rhs)
232 {
233  return (lhs.address() == rhs.address()) && (lhs.port() == rhs.port());
234 }
235 
236 
237 /** Inequality comparison.
238  *
239  * \note Compares address and port number.
240  *
241  * \relates IPAddress
242  * \param lhs The left hand side IP address.
243  * \param rhs The right hand side IP address.
244  * \retval true if \p lhs and \p rhs do not have the same address and port.
245  * \retval false if \p lhs and \p rhs have the same address and port.
246  */
247 bool operator!=(const IPAddress &lhs, const IPAddress &rhs)
248 {
249  return (lhs.address() != rhs.address()) || (lhs.port() != rhs.port());
250 }
251 
252 
253 /** Less than comparison.
254  *
255  * \note The address is considered first followed by the port.
256  *
257  * \relates IPAddress
258  * \param lhs The left hand side IP address.
259  * \param rhs The right hand side IP address.
260  * \retval true if \p lhs is less than \p rhs.
261  * \retval false if \p lhs is not less than \p rhs.
262  */
263 bool operator<(const IPAddress &lhs, const IPAddress &rhs)
264 {
265  return (lhs.address() < rhs.address()) || ((lhs.address() == rhs.address())
266  && (lhs.port() < rhs.port()));
267 }
268 
269 
270 /** Greater than comparison.
271  *
272  * \note The address is considered first followed by the port.
273  *
274  * \relates IPAddress
275  * \param lhs The left hand side IP address.
276  * \param rhs The right hand side IP address.
277  * \retval true if \p lhs is greater than \p rhs.
278  * \retval false if \p lhs is not greater than \p rhs.
279  */
280 bool operator>(const IPAddress &lhs, const IPAddress &rhs)
281 {
282  return (lhs.address() > rhs.address()) || ((lhs.address() == rhs.address())
283  && (lhs.port() > rhs.port()));
284 }
285 
286 
287 /** Less than or equal comparison.
288  *
289  * \note The address is considered first followed by the port.
290  *
291  * \relates IPAddress
292  * \param lhs The left hand side IP address.
293  * \param rhs The right hand side IP address.
294  * \retval true if \p lhs is less than or eqaul to \p rhs.
295  * \retval false if \p lhs is greater than \p rhs.
296  */
297 bool operator<=(const IPAddress &lhs, const IPAddress &rhs)
298 {
299  return (lhs < rhs) || (lhs == rhs);
300 }
301 
302 
303 /** Greater than comparison.
304  *
305  * \note The address is considered first followed by the port.
306  *
307  * \relates IPAddress
308  * \param lhs The left hand side IP address.
309  * \param rhs The right hand side IP address.
310  * \retval true if \p lhs is greater than or equal to \p rhs.
311  * \retval false if \p lhs is less than \p rhs.
312  */
313 bool operator>=(const IPAddress &lhs, const IPAddress &rhs)
314 {
315  return (lhs > rhs) || (lhs == rhs);
316 }
317 
318 
319 /** Print the IP address to the given output stream.
320  *
321  * The format is "<IP Address>" or "<IP Address>:<Port Number>" if the port
322  * number is nonzero an "<IP Address>" if the port is 0.
323  *
324  * Some examples are:
325  * - `127.0.0.1`
326  * - `127.0.0.1:14555`
327  * - `183.125.120.42:443`
328  *
329  * \note The string constructor \ref IPAddress(std::string) and the output
330  * stream operator are inverses and thus:
331  * ```
332  * std::string addr = "127.0.0.1:14555"
333  * str(IPAddress(addr)) == addr
334  * ```
335  *
336  * \param os The output stream to print to.
337  * \param ipaddress The IP address to print.
338  * \returns The output stream.
339  */
340 std::ostream &operator<<(std::ostream &os, const IPAddress &ipaddress)
341 {
342  const auto bytes = to_bytes(ipaddress.address_);
343 
344  for (size_t i = 3; i > 0; --i)
345  {
346  os << static_cast<int>(bytes[i]) << ".";
347  }
348 
349  os << static_cast<int>(bytes[0]);
350 
351  if (ipaddress.port_ != 0)
352  {
353  os << ":" << ipaddress.port_;
354  }
355 
356  return os;
357 }
358 
359 
360 /** Lookup an IP address based on a hostname.
361  *
362  * \warning Currently only supports IPv4.
363  *
364  * \note Currently only UNIX based operating system are supported.
365  *
366  * \relates IPAddress
367  * \param url The URL to get an IP address for.
368  * \returns IP addresses corresponding to the given URL.
369  * \throws DNSLookupError if the address cannot be found.
370  */
371 IPAddress dnslookup(const std::string &url)
372 {
373  #ifdef UNIX
374  return unix_dnslookup(url, 0);
375  #elif WINDOWS
376  return win32_dnslookup(url, 0);
377  #endif
378 }
379 
380 
381 #ifdef UNIX
382 
383 namespace
384 {
385 
386  /** Lookup an IP address based on a hostname.
387  *
388  * This version is UNIX only and will be called by \ref dnslookup on UNIX
389  * systems.
390  *
391  * \relates IPAddress
392  * \param url The URL to get an IP address for.
393  * \returns IP addresses corresponding to the given URL.
394  * \throws DNSLookupError if the address cannot be found.
395  *
396  */
397  IPAddress unix_dnslookup(const std::string &url, unsigned int port)
398  {
399  std::set<IPAddress> addresses;
400  // Setup hints.
401  struct addrinfo hints;
402  std::memset(&hints, 0, sizeof(hints));
403  hints.ai_family = AF_INET; // IPv4 only (for now)
404  hints.ai_protocol = static_cast<int>(port);
405  // Get IP addresses.
406  struct addrinfo *result_ptr = nullptr;
407 
408  if (getaddrinfo(url.c_str(), nullptr, &hints, &result_ptr))
409  {
410  throw DNSLookupError(url);
411  }
412 
413  std::unique_ptr<struct addrinfo, void(*)(struct addrinfo *)>
414  result(result_ptr, freeaddrinfo);
415  result_ptr = nullptr;
416 
417  for (result_ptr = result.get(); result_ptr != nullptr;
418  result_ptr = result_ptr->ai_next)
419  {
420  struct sockaddr_in *address_ptr =
421  reinterpret_cast<struct sockaddr_in *>(result_ptr->ai_addr);
422  unsigned long address = ntohl(address_ptr->sin_addr.s_addr);
423  addresses.insert(IPAddress(address, port));
424  }
425 
426  // This should never be true but it's here just in case.
427  if (addresses.empty())
428  {
429  // LCOV_EXCL_START
430  throw DNSLookupError(url);
431  // LCOV_EXCL_STOP
432  }
433 
434  return *(addresses.begin());
435  }
436 
437 }
438 
439 #elif WINDOWS
440 
441 // Place a windows implementation of windows_dnslookup here.
442 
443 #endif
unsigned int port() const
Definition: IPAddress.cpp:215
bool operator!=(const IPAddress &lhs, const IPAddress &rhs)
Definition: IPAddress.cpp:247
std::array< ByteType, sizeof(T)> to_bytes(T number)
Definition: utility.hpp:145
IPAddress(const IPAddress &other)=default
std::ostream & operator<<(std::ostream &os, const Action &action)
Definition: Action.cpp:188
unsigned long address() const
Definition: IPAddress.cpp:205
bool operator<=(const IPAddress &lhs, const IPAddress &rhs)
Definition: IPAddress.cpp:297
IPAddress dnslookup(const std::string &url)
Definition: IPAddress.cpp:371
bool operator==(const IPAddress &lhs, const IPAddress &rhs)
Definition: IPAddress.cpp:231
bool operator>=(const IPAddress &lhs, const IPAddress &rhs)
Definition: IPAddress.cpp:313
bool operator>(const IPAddress &lhs, const IPAddress &rhs)
Definition: IPAddress.cpp:280
bool operator<(const IPAddress &lhs, const IPAddress &rhs)
Definition: IPAddress.cpp:263