Line data Source code
1 : // Network Address -- classes functions to ease handling IP addresses
2 : // Copyright (C) 2012-2017 Made to Order Software Corp.
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, write to the Free Software
16 : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 :
18 : // self
19 : //
20 : #include "libaddr/addr.h"
21 :
22 : // C++ lib
23 : //
24 : #include <algorithm>
25 : #include <sstream>
26 : #include <iostream>
27 :
28 : // C lib
29 : //
30 : #include <ifaddrs.h>
31 : #include <netdb.h>
32 :
33 :
34 :
35 : namespace addr
36 : {
37 :
38 :
39 : namespace
40 : {
41 :
42 :
43 : /** \brief Delete an addrinfo structure.
44 : *
45 : * This deleter is used to make sure all the addinfo get released when
46 : * an exception occurs or the function using such exists.
47 : *
48 : * \param[in] ai The addrinfo structure to free.
49 : */
50 131847 : void addrinfo_deleter(struct addrinfo * ai)
51 : {
52 131847 : freeaddrinfo(ai);
53 131847 : }
54 :
55 :
56 : }
57 :
58 :
59 :
60 :
61 :
62 206 : void addr_parser::set_default_address(std::string const & addr)
63 : {
64 206 : f_default_address = addr;
65 206 : }
66 :
67 :
68 104 : std::string const & addr_parser::get_default_address() const
69 : {
70 104 : return f_default_address;
71 : }
72 :
73 :
74 : /** \brief Define the default port.
75 : *
76 : * This function is used to define the default port to use in the address
77 : * parser object. By default this is set to -1 meaning: no default port.
78 : *
79 : * This function accepts any port number from 0 to 65535. It also accepts
80 : * -1 to reset the port back to "no default".
81 : *
82 : * \param[in] port The new default port.
83 : */
84 76 : void addr_parser::set_default_port(int const port)
85 : {
86 76 : if(port < -1
87 63 : || port > 65535)
88 : {
89 25 : throw addr_invalid_argument_exception("addr_parser::set_default_port(): port must be in range [-1..65535].");
90 : }
91 :
92 51 : f_default_port = port;
93 51 : }
94 :
95 :
96 76 : int addr_parser::get_default_port() const
97 : {
98 76 : return f_default_port;
99 : }
100 :
101 :
102 100 : void addr_parser::set_default_mask(std::string const & mask)
103 : {
104 100 : f_default_mask = mask;
105 100 : }
106 :
107 :
108 1 : std::string const & addr_parser::get_default_mask() const
109 : {
110 1 : return f_default_mask;
111 : }
112 :
113 :
114 109 : void addr_parser::set_protocol(std::string const & protocol)
115 : {
116 109 : if(protocol == "ip")
117 : {
118 2 : f_protocol = IPPROTO_IP;
119 : }
120 107 : else if(protocol == "tcp")
121 : {
122 103 : f_protocol = IPPROTO_TCP;
123 : }
124 4 : else if(protocol == "udp")
125 : {
126 2 : f_protocol = IPPROTO_UDP;
127 : }
128 : else
129 : {
130 : // not a protocol we support
131 : //
132 : throw addr_invalid_argument_exception(
133 : std::string("unknown protocol \"")
134 4 : + protocol
135 6 : + "\", expected \"tcp\" or \"udp\".");
136 : }
137 107 : }
138 :
139 :
140 131899 : void addr_parser::set_protocol(int const protocol)
141 : {
142 : // make sure that's a protocol we support
143 : //
144 131899 : switch(protocol)
145 : {
146 : case IPPROTO_IP:
147 : case IPPROTO_TCP:
148 : case IPPROTO_UDP:
149 131699 : break;
150 :
151 : default:
152 : throw addr_invalid_argument_exception(
153 : std::string("unknown protocol \"")
154 400 : + std::to_string(protocol)
155 600 : + "\", expected \"tcp\" or \"udp\".");
156 :
157 : }
158 :
159 131699 : f_protocol = protocol;
160 131699 : }
161 :
162 :
163 : /** \brief Use this function to reset the protocol back to "no default."
164 : *
165 : * This function sets the protocol to -1 (which is something you cannot
166 : * do by callingt he set_protocol() functions above.)
167 : *
168 : * The -1 special value means that the protocol is not defined, that
169 : * there is no default. In most cases this means all the addresses
170 : * that match, ignoring the protocol, will be returned by the parse()
171 : * function.
172 : */
173 3 : void addr_parser::clear_protocol()
174 : {
175 3 : f_protocol = -1;
176 3 : }
177 :
178 :
179 216 : int addr_parser::get_protocol() const
180 : {
181 216 : return f_protocol;
182 : }
183 :
184 :
185 : /** \brief Set or clear allow flags in the parser.
186 : *
187 : * This parser has a set of flags it uses to know whether the input
188 : * string can include certain things such as a port or a mask.
189 : *
190 : * This function is used to allow or require certain parameters and
191 : * to disallow others.
192 : *
193 : * By default, the ADDRESS and PORT flags are set, meaning that an
194 : * address and a port can appear, but either or both are optinal.
195 : * If unspecified, then the default will be used. If not default
196 : * is defined, then the parser may fail in this situation.
197 : *
198 : * One problem is that we include contradictory syntatical features.
199 : * The parser supports lists of addresses separated by commas and
200 : * lists of ports separated by commas. Both are not supported
201 : * simultaneously. This means you want to allow multiple addresses
202 : * separated by commas, the function makes sure that the multiple
203 : * port separated by commas support is turned of.
204 : *
205 : * \li ADDRESS -- the IP address is allowed, but optional
206 : * \li REQUIRED_ADDRESS -- the IP address is mandatory
207 : * \li PORT -- the port is allowed, but optional
208 : * \li REQUIRED_PORT -- the port is mandatory
209 : * \li MASK -- the mask is allowed, but optional
210 : * \li MULTI_ADDRESSES_COMMAS -- the input can have multiple addresses
211 : * separated by commas, spaces are not allowed (prevents MULTI_PORTS_COMMAS)
212 : * \li MULTI_ADDRESSES_SPACES -- the input can have multiple addresses
213 : * separated by spaces
214 : * \li MULTI_ADDRESSES_COMMAS_AND_SPACES -- the input can have multiple
215 : * addresses separated by spaces and commas (prevents MULTI_PORTS_COMMAS)
216 : * \li MULTI_PORTS_SEMICOLONS -- the input can have multiple ports
217 : * separated by semicolons NOT IMPLEMENTED YET
218 : * \li MULTI_PORTS_COMMAS -- the input can have multiple ports separated
219 : * by commas (prevents MULTI_ADDRESSES_COMMAS and
220 : * MULTI_ADDRESSES_COMMAS_AND_SPACES) NOT IMPLEMENTED YET
221 : * \li PORT_RANGE -- the input supports port ranges (p1-p2) NOT
222 : * IMPLEMENTED YET
223 : * \li ADDRESS_RANGE -- the input supports address ranges (addr-addr) NOT
224 : * IMPLEMENTED YET
225 : *
226 : * \param[in] flag The flag to set or clear.
227 : * \param[in] allow Whether to allow (true) or disallow (false).
228 : *
229 : * \sa get_allow()
230 : */
231 417 : void addr_parser::set_allow(flag_t const flag, bool const allow)
232 : {
233 417 : if(flag < static_cast<flag_t>(0)
234 397 : || flag >= flag_t::FLAG_max)
235 : {
236 40 : throw addr_invalid_argument_exception("addr_parser::set_allow(): flag has to be one of the valid flags.");
237 : }
238 :
239 377 : f_flags[static_cast<int>(flag)] = allow;
240 :
241 : // if we just set a certain flag, others may need to go to false
242 : //
243 377 : if(allow)
244 : {
245 : // we can only support one type of commas
246 : //
247 369 : switch(flag)
248 : {
249 : case flag_t::MULTI_ADDRESSES_COMMAS:
250 : case flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES:
251 7 : f_flags[static_cast<int>(flag_t::MULTI_PORTS_COMMAS)] = false;
252 7 : break;
253 :
254 : case flag_t::MULTI_PORTS_COMMAS:
255 2 : f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS)] = false;
256 2 : f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)] = false;
257 2 : break;
258 :
259 : default:
260 360 : break;
261 :
262 : }
263 : }
264 377 : }
265 :
266 :
267 56 : bool addr_parser::get_allow(flag_t const flag) const
268 : {
269 56 : if(flag < static_cast<flag_t>(0)
270 46 : || flag >= flag_t::FLAG_max)
271 : {
272 20 : throw addr_invalid_argument_exception("addr_parser::get_allow(): flag has to be one of the valid flags.");
273 : }
274 :
275 36 : return f_flags[static_cast<int>(flag)];
276 : }
277 :
278 :
279 131710 : bool addr_parser::has_errors() const
280 : {
281 131710 : return !f_error.empty();
282 : }
283 :
284 :
285 46 : void addr_parser::emit_error(std::string const & msg)
286 : {
287 46 : f_error += msg;
288 46 : f_error += "\n";
289 46 : ++f_error_count;
290 46 : }
291 :
292 :
293 46 : std::string const & addr_parser::error_messages() const
294 : {
295 46 : return f_error;
296 : }
297 :
298 :
299 46 : int addr_parser::error_count() const
300 : {
301 46 : return f_error_count;
302 : }
303 :
304 :
305 5 : void addr_parser::clear_errors()
306 : {
307 5 : f_error.clear();
308 5 : f_error_count = 0;
309 5 : }
310 :
311 :
312 131700 : addr_range::vector_t addr_parser::parse(std::string const & in)
313 : {
314 131700 : addr_range::vector_t result;
315 :
316 131700 : if(f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS)]
317 131699 : || f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_SPACES)])
318 : {
319 2 : char sep(f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS)] ? ',' : ' ');
320 2 : std::string::size_type s(0);
321 24 : while(s < in.length())
322 : {
323 11 : std::string::size_type e(in.find(sep, s));
324 11 : if(e == std::string::npos)
325 : {
326 2 : e = in.length();
327 : }
328 11 : if(e > s)
329 : {
330 6 : parse_cidr(in.substr(s, e - s), result);
331 : }
332 11 : s = e + 1;
333 2 : }
334 : }
335 131698 : else if(f_flags[static_cast<int>(flag_t::MULTI_ADDRESSES_COMMAS_AND_SPACES)])
336 : {
337 2 : std::string comma_space(", ");
338 1 : std::string::size_type s(0);
339 17 : while(s < in.length())
340 : {
341 : // since C++11 we have a way to search for a set of character
342 : // in a string with an algorithm!
343 : //
344 8 : auto const it(std::find_first_of(in.begin() + s, in.end(), comma_space.begin(), comma_space.end()));
345 8 : std::string::size_type const e(it == in.end() ? in.length() : it - in.begin());
346 8 : if(e > s)
347 : {
348 3 : parse_cidr(in.substr(s, e - s), result);
349 : }
350 8 : s = e + 1;
351 : }
352 : }
353 : else
354 : {
355 131697 : parse_cidr(in, result);
356 : }
357 :
358 131700 : return result;
359 : }
360 :
361 :
362 : /** \brief Check one address.
363 : *
364 : * This function checks one address, although if it is a name, it could
365 : * represent multiple IP addresses.
366 : *
367 : * This function separate the address:port from the mask if the mask is
368 : * allowed. Then it parses the address:port part and the mask separately.
369 : *
370 : * \param[in] in The address to parse.
371 : * \param[in] result The resulting list of addresses.
372 : */
373 131706 : void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
374 : {
375 131706 : if(f_flags[static_cast<int>(flag_t::MASK)])
376 : {
377 : // check whether there is a mask
378 : //
379 710 : std::string mask(f_default_mask);
380 :
381 710 : std::string address;
382 355 : std::string::size_type const p(in.find('/'));
383 355 : if(p != std::string::npos)
384 : {
385 302 : address = in.substr(0, p);
386 302 : mask = in.substr(p + 1);
387 : }
388 : else
389 : {
390 53 : address = in;
391 : }
392 355 : if(mask.empty())
393 : {
394 : // mask not found, do as if none defined
395 : //
396 5 : parse_address(address, result);
397 : }
398 : else
399 : {
400 350 : int const errcnt(f_error_count);
401 :
402 : // handle the address first
403 : //
404 700 : addr_range::vector_t addr_mask;
405 350 : parse_address(address, addr_mask);
406 :
407 : // now check for the mask
408 : //
409 700 : for(auto & am : addr_mask)
410 : {
411 350 : parse_mask(mask, am.get_from());
412 : }
413 :
414 : // now append the list to the result if no errors occurred
415 : //
416 350 : if(errcnt == f_error_count)
417 : {
418 315 : result.insert(result.end(), addr_mask.begin(), addr_mask.end());
419 : }
420 : }
421 : }
422 : else
423 : {
424 : // no mask allowed, if there is one, this call will fail
425 : //
426 131351 : parse_address(in, result);
427 : }
428 131706 : }
429 :
430 :
431 131706 : void addr_parser::parse_address(std::string const & in, addr_range::vector_t & result)
432 : {
433 : // With our only supportted format, ipv6 addresses must be between square
434 : // brackets. The address may just be a mask in which case the '[' may
435 : // not be at the very start (i.e. "/[ffff:ffff::]")
436 : //
437 131706 : if(in.find('[') != std::string::npos)
438 : {
439 : // IPv6 parsing
440 : //
441 65769 : parse_address6(in, result);
442 : }
443 : else
444 : {
445 65937 : parse_address4(in, result);
446 : }
447 131706 : }
448 :
449 :
450 65937 : void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
451 : {
452 131873 : std::string port_str(f_default_port == -1 ? std::string() : std::to_string(f_default_port));
453 131873 : std::string address(f_default_address);
454 :
455 65937 : std::string::size_type const p(in.find(':'));
456 :
457 65937 : if(f_flags[static_cast<int>(flag_t::PORT)]
458 5 : || f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
459 : {
460 : // the address can include a port
461 : //
462 131866 : if(p != std::string::npos)
463 : {
464 : // get the address only if not empty (otherwise we want to
465 : // keep the default)
466 : //
467 65897 : if(p > 0)
468 : {
469 65697 : address = in.substr(0, p);
470 : }
471 :
472 : // get the port only if not empty (otherwise we want to
473 : // keep the default)
474 : //
475 65897 : if(p + 1 < in.length())
476 : {
477 65872 : port_str = in.substr(p + 1);
478 : }
479 : }
480 36 : else if(!in.empty())
481 : {
482 33 : address = in;
483 : }
484 : }
485 : else
486 : {
487 4 : if(p != std::string::npos
488 1 : && !f_flags[static_cast<int>(flag_t::PORT)]
489 1 : && !f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
490 : {
491 1 : emit_error("Port not allowed (" + in + ").");
492 1 : return;
493 : }
494 :
495 3 : if(!in.empty())
496 : {
497 1 : address = in;
498 : }
499 : }
500 :
501 65936 : parse_address_port(address, port_str, result);
502 : }
503 :
504 :
505 65769 : void addr_parser::parse_address6(std::string const & in, addr_range::vector_t & result)
506 : {
507 65769 : std::string::size_type p(0);
508 :
509 131536 : std::string address(f_default_address);
510 131536 : std::string port_str(f_default_port == -1 ? std::string() : std::to_string(f_default_port));
511 :
512 : // if there is an address extract it otherwise put the default
513 : //
514 131538 : if(!in.empty()
515 65769 : && in[0] == '[')
516 : {
517 65769 : p = in.find(']');
518 :
519 65769 : if(p == std::string::npos)
520 : {
521 1 : emit_error("IPv6 is missing the ']' (" + in + ").");
522 1 : return;
523 : }
524 :
525 65768 : if(p != 1)
526 : {
527 : // get the address only if not empty (otherwise we want to
528 : // keep the default) -- so we actually support "[]" to
529 : // represent "use the default address if defined".
530 : //
531 65767 : address = in.substr(1, p - 1);
532 : }
533 : }
534 :
535 : // on entry 'p' is either 0 or the position of the ']' character
536 : //
537 65768 : p = in.find(':', p);
538 :
539 65768 : if(p != std::string::npos)
540 : {
541 65762 : if(f_flags[static_cast<int>(flag_t::PORT)]
542 1 : || f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
543 : {
544 : // there is also a port, extract it
545 : //
546 65761 : port_str = in.substr(p + 1);
547 : }
548 1 : else if(!f_flags[static_cast<int>(flag_t::PORT)]
549 1 : && !f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
550 : {
551 1 : emit_error("Port not allowed (" + in + ").");
552 1 : return;
553 : }
554 : }
555 :
556 65767 : parse_address_port(address, port_str, result);
557 : }
558 :
559 :
560 131703 : void addr_parser::parse_address_port(std::string const & address, std::string const & port_str, addr_range::vector_t & result)
561 : {
562 : // make sure the port is good
563 : //
564 263406 : if(port_str.empty()
565 131703 : && f_flags[static_cast<int>(flag_t::REQUIRED_PORT)])
566 : {
567 4 : emit_error("Required port is missing.");
568 12 : return;
569 : }
570 :
571 : // make sure the address is good
572 : //
573 263398 : if(address.empty()
574 131699 : && f_flags[static_cast<int>(flag_t::REQUIRED_ADDRESS)])
575 : {
576 2 : emit_error("Required address is missing.");
577 2 : return;
578 : }
579 :
580 : // prepare hints for the the getaddrinfo() function
581 : //
582 : struct addrinfo hints;
583 131697 : memset(&hints, 0, sizeof(hints));
584 131697 : hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
585 131697 : hints.ai_family = AF_UNSPEC;
586 :
587 131697 : switch(f_protocol)
588 : {
589 : case IPPROTO_TCP:
590 65722 : hints.ai_socktype = SOCK_STREAM;
591 65722 : hints.ai_protocol = IPPROTO_TCP;
592 65722 : break;
593 :
594 : case IPPROTO_UDP:
595 65972 : hints.ai_socktype = SOCK_DGRAM;
596 65972 : hints.ai_protocol = IPPROTO_UDP;
597 65972 : break;
598 :
599 : }
600 :
601 : // convert address to binary
602 : //
603 131697 : struct addrinfo * addrlist(nullptr);
604 : {
605 131697 : errno = 0;
606 131697 : int const r(getaddrinfo(address.c_str(), port_str.c_str(), &hints, &addrlist));
607 131697 : if(r != 0)
608 : {
609 : // break on invalid addresses
610 : //
611 2 : int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
612 : emit_error("Invalid address in \""
613 4 : + address
614 6 : + (port_str.empty() ? "" : ":")
615 4 : + port_str
616 4 : + "\" error "
617 8 : + std::to_string(r)
618 4 : + " -- "
619 6 : + gai_strerror(r)
620 4 : + " (errno: "
621 8 : + std::to_string(e)
622 4 : + " -- "
623 6 : + strerror(e)
624 2 : + ").");
625 2 : return;
626 : }
627 : }
628 263390 : std::shared_ptr<struct addrinfo> ai(addrlist, addrinfo_deleter);
629 :
630 131695 : bool first(true);
631 395089 : while(addrlist != nullptr)
632 : {
633 : // go through the addresses and create ranges and save that in the result
634 : //
635 131697 : if(addrlist->ai_family == AF_INET)
636 : {
637 65832 : if(addrlist->ai_addrlen != sizeof(struct sockaddr_in))
638 : {
639 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
640 : + std::to_string(addrlist->ai_addrlen) // LCOV_EXCL_LINE
641 : + ", expected" // LCOV_EXCL_LINE
642 : + std::to_string(sizeof(struct sockaddr_in)) // LCOV_EXCL_LINE
643 : + ")."); // LCOV_EXCL_LINE
644 : }
645 : else
646 : {
647 131664 : addr a(*reinterpret_cast<struct sockaddr_in *>(addrlist->ai_addr));
648 : // in most cases we do not get a protocol from
649 : // the getaddrinfo() function...
650 65832 : if(addrlist->ai_protocol != 0)
651 : {
652 65831 : a.set_protocol(addrlist->ai_protocol);
653 : }
654 131664 : addr_range r;
655 65832 : r.set_from(a);
656 65832 : result.push_back(r);
657 : }
658 : }
659 65865 : else if(addrlist->ai_family == AF_INET6)
660 : {
661 65865 : if(addrlist->ai_addrlen != sizeof(struct sockaddr_in6))
662 : {
663 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
664 : + std::to_string(addrlist->ai_addrlen) // LCOV_EXCL_LINE
665 : + ", expected " // LCOV_EXCL_LINE
666 : + std::to_string(sizeof(struct sockaddr_in6)) // LCOV_EXCL_LINE
667 : + ")."); // LCOV_EXCL_LINE
668 : }
669 : else
670 : {
671 131730 : addr a(*reinterpret_cast<struct sockaddr_in6 *>(addrlist->ai_addr));
672 65865 : a.set_protocol(addrlist->ai_protocol);
673 131730 : addr_range r;
674 65865 : r.set_from(a);
675 65865 : result.push_back(r);
676 : }
677 : }
678 : else if(first) // LCOV_EXCL_LINE
679 : {
680 : // ignore errors from further addresses
681 : //
682 : emit_error("Unsupported address family " // LCOV_EXCL_LINE
683 : + std::to_string(addrlist->ai_family) // LCOV_EXCL_LINE
684 : + "."); // LCOV_EXCL_LINE
685 : }
686 :
687 131697 : first = false;
688 :
689 131697 : addrlist = addrlist->ai_next;
690 : }
691 : }
692 :
693 :
694 : /** \brief Parse a mask.
695 : *
696 : * If the input string is a decimal number, then use that as the
697 : * number of bits to clear.
698 : *
699 : * If the mask is not just one decimal number, try to convert it
700 : * as an address.
701 : *
702 : * If the string is neither a decimal number nor a valid IP address
703 : * then the parser adds an error string to the f_error variable.
704 : *
705 : * \param[in] mask The mask to transform to binary.
706 : * \param[in,out] cidr The address to which the mask will be added.
707 : */
708 350 : void addr_parser::parse_mask(std::string const & mask, addr & cidr)
709 : {
710 : // no mask?
711 : //
712 350 : if(mask.empty())
713 : {
714 : // in the current implementation this cannot happen since we
715 : // do not call this function when mask is empty
716 : //
717 : // hwoever, the algorithm below expect that 'mask' is not
718 : // empty (otherwise we get the case of 0 even though it
719 : // may not be correct.)
720 : //
721 : return; // LCOV_EXCL_LINE
722 : }
723 :
724 : // the mask may be a decimal number or an address, if just one number
725 : // then it's not an address, so test that first
726 : //
727 350 : uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
728 :
729 : // convert the mask to an integer, if possible
730 : //
731 350 : int mask_count(0);
732 : {
733 942 : for(char const * s(mask.c_str()); *s != '\0'; ++s)
734 : {
735 768 : if(*s >= '0' && *s <= '9')
736 : {
737 602 : mask_count = mask_count * 10 + *s - '0';
738 1194 : if(mask_count > 1000)
739 : {
740 : emit_error("Mask number too large ("
741 20 : + mask
742 10 : + ", expected a maximum of 128).");
743 10 : return;
744 : }
745 : }
746 : else
747 : {
748 166 : mask_count = -1;
749 166 : break;
750 : }
751 : }
752 : }
753 :
754 : // the conversion to an integer worked if mask_count != -1
755 : //
756 340 : if(mask_count != -1)
757 : {
758 174 : if(cidr.is_ipv4())
759 : {
760 40 : if(mask_count > 32)
761 : {
762 : emit_error("Unsupported mask size ("
763 10 : + std::to_string(mask_count)
764 5 : + ", expected 32 at the most for an IPv4).");
765 5 : return;
766 : }
767 35 : mask_count = 32 - mask_count;
768 : }
769 : else
770 : {
771 134 : if(mask_count > 128)
772 : {
773 : emit_error("Unsupported mask size ("
774 10 : + std::to_string(mask_count)
775 5 : + ", expected 128 at the most for an IPv6).");
776 5 : return;
777 : }
778 129 : mask_count = 128 - mask_count;
779 : }
780 :
781 : // clear a few bits at the bottom of mask_bits
782 : //
783 164 : int idx(15);
784 2184 : for(; mask_count > 8; mask_count -= 8, --idx)
785 : {
786 1010 : mask_bits[idx] = 0;
787 : }
788 164 : mask_bits[idx] = 255 << mask_count;
789 : }
790 : else //if(mask_count < 0)
791 : {
792 : // prepare hints for the the getaddrinfo() function
793 : //
794 : struct addrinfo hints;
795 166 : memset(&hints, 0, sizeof(hints));
796 166 : hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
797 166 : hints.ai_family = AF_UNSPEC;
798 :
799 166 : switch(cidr.get_protocol())
800 : {
801 : case IPPROTO_TCP:
802 80 : hints.ai_socktype = SOCK_STREAM;
803 80 : hints.ai_protocol = IPPROTO_TCP;
804 80 : break;
805 :
806 : case IPPROTO_UDP:
807 86 : hints.ai_socktype = SOCK_DGRAM;
808 86 : hints.ai_protocol = IPPROTO_UDP;
809 86 : break;
810 :
811 : }
812 :
813 316 : std::string const port_str(std::to_string(cidr.get_port()));
814 :
815 : // if the mask is an IPv6, then it has to have the '[...]'
816 316 : std::string m(mask);
817 166 : if(cidr.is_ipv4())
818 : {
819 82 : if(mask[0] == '[')
820 : {
821 1 : emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
822 1 : return;
823 : }
824 : }
825 : else //if(!cidr.is_ipv4())
826 : {
827 84 : if(mask[0] != '[')
828 : {
829 1 : emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
830 1 : return;
831 : }
832 83 : if(mask.back() != ']')
833 : {
834 1 : emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
835 1 : return;
836 : }
837 :
838 : // note that we know that mask.length() >= 2 here since
839 : // we at least have a '[' and ']'
840 : //
841 82 : m = mask.substr(1, mask.length() - 2);
842 82 : if(m.empty())
843 : {
844 : // an empty mask is valid, it just means keep the default
845 : // (getaddrinfo() fails on an empty string)
846 : //
847 1 : return;
848 : }
849 : }
850 :
851 : // if negative, we may have a full address here, so call the
852 : // getaddrinfo() on this other string
853 : //
854 162 : struct addrinfo * masklist(nullptr);
855 162 : errno = 0;
856 162 : int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
857 162 : if(r != 0)
858 : {
859 : // break on invalid addresses
860 : //
861 10 : int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
862 : emit_error("Invalid mask in \"/"
863 20 : + mask
864 20 : + "\", error "
865 40 : + std::to_string(r)
866 20 : + " -- "
867 30 : + gai_strerror(r)
868 20 : + " (errno: "
869 40 : + std::to_string(e)
870 20 : + " -- "
871 30 : + strerror(e)
872 10 : + ").");
873 10 : return;
874 : }
875 302 : std::shared_ptr<struct addrinfo> mask_ai(masklist, addrinfo_deleter);
876 :
877 152 : if(cidr.is_ipv4())
878 : {
879 76 : if(masklist->ai_family != AF_INET)
880 : {
881 : // this one happens when the user does not put the '[...]'
882 : // around an IPv6 address
883 : //
884 2 : emit_error("Incompatible address between the address and"
885 1 : " mask address (first was an IPv4 second an IPv6).");
886 1 : return;
887 : }
888 75 : if(masklist->ai_addrlen != sizeof(struct sockaddr_in))
889 : {
890 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
891 : + std::to_string(masklist->ai_addrlen) // LCOV_EXCL_LINE
892 : + ", expected" // LCOV_EXCL_LINE
893 : + std::to_string(sizeof(struct sockaddr_in)) // LCOV_EXCL_LINE
894 : + ")."); // LCOV_EXCL_LINE
895 : return; // LCOV_EXCL_LINE
896 : }
897 75 : memcpy(mask_bits + 12, &reinterpret_cast<struct sockaddr_in *>(masklist->ai_addr)->sin_addr.s_addr, 4); // last 4 bytes are the IPv4 address, keep the rest as 1s
898 : }
899 : else //if(!cidr.is_ipv4())
900 : {
901 76 : if(masklist->ai_family != AF_INET6)
902 : {
903 : // this one happens if the user puts the '[...]'
904 : // around an IPv4 address
905 : //
906 2 : emit_error("Incompatible address between the address"
907 1 : " and mask address (first was an IPv6 second an IPv4).");
908 1 : return;
909 : }
910 75 : if(masklist->ai_addrlen != sizeof(struct sockaddr_in6))
911 : {
912 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
913 : + std::to_string(masklist->ai_addrlen) // LCOV_EXCL_LINE
914 : + ", expected " // LCOV_EXCL_LINE
915 : + std::to_string(sizeof(struct sockaddr_in6)) // LCOV_EXCL_LINE
916 : + ")."); // LCOV_EXCL_LINE
917 : return; // LCOV_EXCL_LINE
918 : }
919 75 : memcpy(mask_bits, &reinterpret_cast<struct sockaddr_in6 *>(masklist->ai_addr)->sin6_addr.s6_addr, 16);
920 : }
921 : }
922 :
923 314 : cidr.set_mask(mask_bits);
924 : }
925 :
926 :
927 :
928 :
929 :
930 :
931 :
932 :
933 6 : }
934 : // snap_addr namespace
935 : // vim: ts=4 sw=4 et
|