Line data Source code
1 : // Copyright (c) 2012-2025 Made to Order Software Corp. All Rights Reserved
2 : //
3 : // https://snapwebsites.org/project/libaddr
4 : //
5 : // Permission is hereby granted, free of charge, to any person obtaining a
6 : // copy of this software and associated documentation files (the
7 : // "Software"), to deal in the Software without restriction, including
8 : // without limitation the rights to use, copy, modify, merge, publish,
9 : // distribute, sublicense, and/or sell copies of the Software, and to
10 : // permit persons to whom the Software is furnished to do so, subject to
11 : // the following conditions:
12 : //
13 : // The above copyright notice and this permission notice shall be included
14 : // in all copies or substantial portions of the Software.
15 : //
16 : // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 : // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 : // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 : // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20 : // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 : // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22 : // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 :
24 :
25 : /** \file
26 : * \brief The implementation of the IP address parser.
27 : *
28 : * This function is used to parse IP addresses from a string to a
29 : * vector of ranges.
30 : *
31 : * The type of addresses support is really wide:
32 : *
33 : * * <domain name> -- if allowed to do a lookup
34 : * * <ipv4> -- an IPv4 with syntax x.x.x.x
35 : * * <ipv6> -- an IPv6 with syntax x:x:x:...:x
36 : * * <port> -- a decimal number from 0 to 65535
37 : * * <mask> -- a number from 0 to 128 or an <ipv4> or an <ipv6>
38 : * * <ip>-<ip> -- a range of <ipv4> or <ipv6> addresses
39 : *
40 : * The port is separated from the address by a colon (:). For IPv6, this means
41 : * the IPv6 address itself must be defined between square brackets as in
42 : * `[x:x:...:x]`. The square brackets are not required if the port is not
43 : * allowed.
44 : *
45 : * The `<mask>` appears after a slash (/). It is expected to be a number
46 : * from 0 to 128 (0 to 32 for IPv4 addresses). It can be written as an
47 : * address only if the ALLOW_ADDRESS_MASK flag is set to true.
48 : *
49 : * \code
50 : * start: ips
51 : *
52 : * ips: domain port mask
53 : * | port mask
54 : * | ip port mask
55 : * | ip '-' ip port mask
56 : * | ip '-' port mask
57 : * | '-' ip port mask
58 : *
59 : * ip: '[' ipv6 ']'
60 : * | ipv6
61 : * | ipv4
62 : *
63 : * ipv4: number '.' number '.' number '.' number
64 : *
65 : * ipv6: hex
66 : * | ':'
67 : * | ipv6 ipv6
68 : *
69 : * port: <empty>
70 : * | ':' number
71 : *
72 : * mask: <empty>
73 : * | '/' number
74 : * | '/' ip
75 : *
76 : * number: number number
77 : * | '0' | '1' | '2' | ... | '9'
78 : *
79 : * hex: hex hex
80 : * | number
81 : * | 'a' | 'b' | ... | 'f'
82 : * | 'A' | 'B' | ... | 'F'
83 : *
84 : * domain: domain domain
85 : * | number
86 : * | 'a' | 'b' | ... | 'z'
87 : * | 'A' | 'B' | ... | 'Z'
88 : * | '.' | '-'
89 : * | UTF8_CHARACTER (most domain systems do not support all UTF-8)
90 : * \endcode
91 : */
92 :
93 : // self
94 : //
95 : #include "libaddr/addr_parser.h"
96 : #include "libaddr/exception.h"
97 :
98 :
99 : // advgetopt
100 : //
101 : #include <advgetopt/validator_integer.h>
102 :
103 :
104 : // snapdev
105 : //
106 : #include <snapdev/trim_string.h>
107 :
108 :
109 : // C++
110 : //
111 : #include <algorithm>
112 : #include <iostream>
113 :
114 :
115 : // C
116 : //
117 : #include <ifaddrs.h>
118 : #include <netdb.h>
119 :
120 :
121 : // last include
122 : //
123 : #include <snapdev/poison.h>
124 :
125 :
126 :
127 : namespace addr
128 : {
129 :
130 :
131 : namespace
132 : {
133 :
134 :
135 : /** \brief Delete an addrinfo structure.
136 : *
137 : * This deleter is used to make sure all the addinfo get released when
138 : * an exception occurs or the function using such exists.
139 : *
140 : * \param[in] ai The addrinfo structure to free.
141 : */
142 132258 : void addrinfo_deleter(addrinfo * ai)
143 : {
144 132258 : freeaddrinfo(ai);
145 132258 : }
146 :
147 :
148 : }
149 :
150 :
151 :
152 :
153 :
154 : /** \brief Initialize an addr_parser object.
155 : *
156 : * This function initializes the addr_parser object.
157 : *
158 : * Especially, it calls the set_allow() functions a few times to set
159 : * flags which are expected to be true on initialization.
160 : */
161 132066 : addr_parser::addr_parser()
162 : {
163 : // allow addresses & DNS lookups by default
164 : //
165 132066 : set_allow(allow_t::ALLOW_ADDRESS, true);
166 132066 : set_allow(allow_t::ALLOW_ADDRESS_LOOKUP, true);
167 :
168 : // allow port after address
169 : //
170 132066 : set_allow(allow_t::ALLOW_PORT, true);
171 132066 : }
172 :
173 :
174 : /** \brief Set the default IP addresses.
175 : *
176 : * This function sets the default IP addresses to be used by the parser
177 : * when the input string of the parse() function does not include an IP
178 : * address.
179 : *
180 : * The \p address parameter cannot include a port. See
181 : * set_default_port() as a way to change the default port.
182 : *
183 : * The function expects either an IPv4 or an IPv6 address. It can be
184 : * called twice if you need to define both types of addresses (which
185 : * is often a good idea.)
186 : *
187 : * For example, the following input is considered valid when a default
188 : * address is defined:
189 : *
190 : * \code
191 : * parser.parse(":123");
192 : * \endcode
193 : *
194 : * It returns the default address and port 123. Note that by default
195 : * an address is mandatory unless a default address is defined.
196 : *
197 : * To prevent the parser from working when no default and no address
198 : * are specified, then make sure to set the REQUIRED_ADDRESS allow
199 : * flag to true:
200 : *
201 : * \code
202 : * parser.set_allow(parser.allow_t::ALLOW_REQUIRED_ADDRESS, true);
203 : * // now address is mandatory
204 : * \endcode
205 : *
206 : * To completely prevent the use of an address in an input string, set
207 : * the `ADDRESS` and `REQUIRED_ADDRESS` values to false:
208 : *
209 : * \code
210 : * parser.set_allow(parser.allow_t::ALLOW_ADDRESS, false);
211 : * parser.set_allow(parser.allow_t::ALLOW_REQUIRED_ADDRESS, false);
212 : * \endcode
213 : *
214 : * To remove both default IP addresses, call this function with an empty
215 : * string:
216 : *
217 : * \code
218 : * parser.set_default_address(std::string());
219 : * \endcode
220 : *
221 : * \todo
222 : * Consider saving the default IPs as addr structures and allow such
223 : * as input (keep in mind that the default could also represent multiple
224 : * addresses).
225 : *
226 : * \param[in] addr The new address.
227 : */
228 230 : void addr_parser::set_default_address(std::string const & address)
229 : {
230 230 : if(address.empty())
231 : {
232 4 : f_default_address4.clear();
233 4 : f_default_address6.clear();
234 : }
235 226 : else if(address[0] == '[')
236 : {
237 : // remove the '[' and ']'
238 : //
239 6 : if(address.back() != ']')
240 : {
241 9 : throw addr_invalid_argument("an IPv6 address starting with '[' must end with ']'.");
242 : }
243 3 : f_default_address6 = address.substr(1, address.length() - 2);
244 : }
245 220 : else if(address.find(':') != std::string::npos)
246 : {
247 103 : f_default_address6 = address;
248 : }
249 : else
250 : {
251 117 : f_default_address4 = address;
252 : }
253 227 : }
254 :
255 :
256 : /** \brief Retrieve the default IP address for IPv4 parsing.
257 : *
258 : * This function returns a copy of the default IP address used by
259 : * the parser when the input string does not include an IP address.
260 : *
261 : * If the function returns an empty string, then no default address
262 : * is defined.
263 : *
264 : * \return The default IPv4 address.
265 : *
266 : * \sa get_default_address6()
267 : * \sa set_default_address()
268 : */
269 115 : std::string const & addr_parser::get_default_address4() const
270 : {
271 115 : return f_default_address4;
272 : }
273 :
274 :
275 : /** \brief Retrieve the default IP address for IPv4 parsing.
276 : *
277 : * This function returns a copy of the default IP address used by
278 : * the parser when the input string does not include an IP address.
279 : *
280 : * If the function returns an empty string, then no default address
281 : * is defined.
282 : *
283 : * \return The default IPv6 address, without square brackets.
284 : *
285 : * \sa get_default_address4()
286 : * \sa set_default_address()
287 : */
288 115 : std::string const & addr_parser::get_default_address6() const
289 : {
290 115 : return f_default_address6;
291 : }
292 :
293 :
294 : /** \brief Set the default port using a string.
295 : *
296 : * By default, you are expected to call the set_default_port() function
297 : * with an integer. If you do not have a number for the port (which
298 : * happens quite frequently) we offer a string version. The string is
299 : * expected to be an exact integer between 0 and 65535 inclusive.
300 : *
301 : * The function will also accept -1 and the empty string to reset the
302 : * default port to its default value (i.e. "no default port").
303 : *
304 : * \exception addr_invalid_argument
305 : * When the input \p port_str is not a valid integer, this exception is
306 : * raised.
307 : *
308 : * \param[in] port_str The string to convert as a port.
309 : */
310 54 : void addr_parser::set_default_port(std::string const & port_str)
311 : {
312 54 : std::int64_t port(-1);
313 54 : if(!port_str.empty())
314 : {
315 53 : if(!advgetopt::validator_integer::convert_string(port_str, port))
316 : {
317 : // TODO: add a lookup for string to port number via /etc/service
318 : throw addr_invalid_argument(
319 : "invalid port in \""
320 2 : + port_str
321 3 : + "\" (no service name lookup allowed).");
322 : }
323 : }
324 :
325 53 : set_default_port(port);
326 28 : }
327 :
328 :
329 : /** \brief Define the default port.
330 : *
331 : * This function is used to define the default port to use in the address
332 : * parser object. By default this is set to -1 meaning: no default port.
333 : *
334 : * This function accepts any port number from 0 to 65535. It also accepts
335 : * -1 to reset the port back to "no default".
336 : *
337 : * To prevent the parser from working when no default and no port
338 : * are specified, then make sure to set the REQUIRED_PORT allow
339 : * flag to true:
340 : *
341 : * \code
342 : * parser.set_allow(parser.allow_t::ALLOW_REQUIRED_PORT, true);
343 : * // now port is mandatory
344 : * \endcode
345 : *
346 : * To completely prevent the use of a port in an input string, set
347 : * the `PORT` and `REQUIRED_PORT` values to false:
348 : *
349 : * \code
350 : * parser.set_allow(parser.allow_t::ALLOW_PORT, false);
351 : * parser.set_allow(parser.allow_t::ALLOW_REQUIRED_PORT, false);
352 : * \endcode
353 : *
354 : * \exception addr_invalid_argument_exception
355 : * If the port number is out of range, then this exception is raised.
356 : * The allowed range for a port is 0 to 65535. This function also
357 : * accepts -1 meaning that no default port is specified.
358 : *
359 : * \param[in] port The new default port.
360 : */
361 113 : void addr_parser::set_default_port(int const port)
362 : {
363 113 : if(port < -1
364 89 : || port > 65535)
365 : {
366 150 : throw addr_invalid_argument("addr_parser::set_default_port(): port must be in range [-1..65535].");
367 : }
368 :
369 63 : f_default_port = port;
370 63 : }
371 :
372 :
373 : /** \brief Retrieve the default port.
374 : *
375 : * This function retrieves the default port as defined by the
376 : * set_default_port() function.
377 : */
378 76 : int addr_parser::get_default_port() const
379 : {
380 76 : return f_default_port;
381 : }
382 :
383 :
384 : /** \brief Define the default mask.
385 : *
386 : * This function is used to define the default mask. Note that the
387 : * default mask will not be used at all if the allow_t::ALLOW_MASK allow
388 : * flag is not set to true:
389 : *
390 : * \code
391 : * parser.set_allow(parser.allow_t::ALLOW_MASK, true);
392 : * parser.set_default_mask("16"); // IPv4 is 0 to 32
393 : * parser.set_default_mask("48"); // IPv6 is 0 to 128
394 : * \endcode
395 : *
396 : * If you want to allow the old syntax (i.e. the mask as an IP address
397 : * instead of just a number), make sure to also allow that:
398 : *
399 : * \code
400 : * parser.set_allow(parser.allow_t::ALLOW_MASK, true);
401 : * parser.set_allow(parser.allow_t::ALLOW_ADDRESS_MASK, true);
402 : * parser.set_default_mask("255.255.0.0");
403 : * parser.set_default_mask("[ffff:ffff:ffff::]");
404 : * \endcode
405 : *
406 : * The IPv6 mask does not require the square brackets (`'['` and `']'`).
407 : *
408 : * To remove the default mask, call this function with an empty
409 : * string:
410 : *
411 : * \code
412 : * parser.set_default_mask(std::string());
413 : * \endcode
414 : *
415 : * \note
416 : * As you can see, here we expect the mask to be a string. This is because
417 : * it gets parsed as if it came from the input string of the parser. This
418 : * also means that if the mask is invalid, it will not be detected until
419 : * you attempt to parse an input string that does not include a mask and
420 : * the default gets used.
421 : *
422 : * \warning
423 : * This function accepts address like values as the default mask. This
424 : * generates an error if no mask is defined by the user and you did not
425 : * do:
426 : *
427 : * \code
428 : * parser.set_allow(addr::allow_t::ALLOW_ADDRESS_MASK, true);
429 : * \endcode
430 : *
431 : * \exception addr_invalid_argument
432 : * An IPv6 address that start with a '[' must end with a ']'. If only one
433 : * of these characters appears in the string, then it is an error and this
434 : * exception is raised.
435 : *
436 : * \todo
437 : * The mask accepts a simple number from 0 to 128. This function is not
438 : * capable of understand whether a smaller number (0 to 32) is an IPv4
439 : * or an IPv6 mask. At the moment, small numbers are viewed as an IPv4
440 : * mask.
441 : *
442 : * \todo
443 : * Add a check of the default mask when it gets set so we can throw on
444 : * errors and that way it is much more likely that programmers can fix
445 : * their errors early. (Actually by pre-parsing we could save it as
446 : * an addr and allow a `set_default_mask(addr ...)`!)
447 : *
448 : * \param[in] mask The mask to use by default.
449 : */
450 158 : void addr_parser::set_default_mask(std::string const & mask)
451 : {
452 158 : if(mask.empty())
453 : {
454 4 : f_default_mask4.clear();
455 4 : f_default_mask6.clear();
456 4 : return;
457 : }
458 :
459 154 : bool const front_ipv6(mask.front() == '[');
460 154 : bool const back_ipv6(mask.back() == ']');
461 154 : if(front_ipv6 && back_ipv6)
462 : {
463 : // remove the '[' and ']'
464 : //
465 28 : f_default_mask6 = mask.substr(1, mask.length() - 2);
466 28 : return;
467 : }
468 :
469 126 : if(front_ipv6 || back_ipv6)
470 : {
471 9 : throw addr_invalid_argument("an IPv6 mask starting with '[' must end with ']' and vice versa.");
472 : }
473 :
474 123 : if(mask.find(':') != std::string::npos)
475 : {
476 25 : f_default_mask6 = mask;
477 25 : return;
478 : }
479 :
480 98 : std::int64_t m(0);
481 98 : bool const valid(advgetopt::validator_integer::convert_string(mask, m));
482 98 : if(valid)
483 : {
484 20 : if(m < 0 || m > 128)
485 : {
486 51 : throw addr_invalid_argument("a mask number must be between 0 and 128.");
487 : }
488 3 : if(m > 32)
489 : {
490 2 : f_default_mask6 = mask;
491 2 : return;
492 : }
493 : }
494 :
495 79 : f_default_mask4 = mask;
496 : }
497 :
498 :
499 : /** \brief Retrieve the default mask.
500 : *
501 : * This function returns a reference to the mask as set by the
502 : * set_default_mask() function. The value is an empty string by
503 : * default.
504 : *
505 : * The default mask will be used if no mask is specified in the
506 : * input string to the parse() function. When no default mask
507 : * is defined, the mask is set to all 1s.
508 : *
509 : * \note
510 : * The default mask is a string, not a binary mask. It gets
511 : * converted by the parser at the time it is required.
512 : *
513 : * \return The default mask.
514 : *
515 : * \sa get_default_mask6()
516 : * \sa set_default_mask()
517 : */
518 14 : std::string const & addr_parser::get_default_mask4() const
519 : {
520 14 : return f_default_mask4;
521 : }
522 :
523 :
524 : /** \brief Retrieve the default mask.
525 : *
526 : * This function returns a reference to the mask as set by the
527 : * set_default_mask() function. The value is an empty string by
528 : * default.
529 : *
530 : * The default mask will be used if no mask is specified in the
531 : * input string to the parse() function. When no default mask
532 : * is defined, the mask is set to all 1s.
533 : *
534 : * \note
535 : * The default mask is a string, not a binary mask. It gets
536 : * converted by the parser at the time it is required.
537 : *
538 : * \return The default mask.
539 : *
540 : * \sa get_default_mask4()
541 : * \sa set_default_mask()
542 : */
543 14 : std::string const & addr_parser::get_default_mask6() const
544 : {
545 14 : return f_default_mask6;
546 : }
547 :
548 :
549 : /** \brief Set the protocol to use to filter addresses.
550 : *
551 : * This function sets the protocol. The accepted names are defined in
552 : * the /etc/protocols file. In most cases, we support "tcp" and
553 : * "udp". Other transfer protocols may work too, but we have not
554 : * tested them.
555 : *
556 : * Any other value is refused. To reset the protocol to the default,
557 : * which is "do not filter by protocol", call the clear_protocol().
558 : *
559 : * \exception addr_invalid_argument_exception
560 : * If the string passed to this function is not one of the acceptable
561 : * protocols (ip, tcp, udp), then this exception is raised.
562 : *
563 : * \param[in] protocol The default protocol for this parser.
564 : *
565 : * \sa clear_protocol()
566 : * \sa get_protocol()
567 : */
568 119 : void addr_parser::set_protocol(std::string const & protocol)
569 : {
570 119 : char buf[1024];
571 119 : protoent p = {};
572 119 : protoent * ptr(&p);
573 119 : if(getprotobyname_r(
574 : protocol.c_str()
575 : , &p
576 : , buf
577 : , sizeof(buf)
578 : , &ptr) != 0
579 119 : || ptr == nullptr)
580 : {
581 : throw addr_invalid_argument(
582 : "unknown protocol named \""
583 4 : + protocol
584 6 : + "\", expected \"tcp\" or \"udp\" or another name from /etc/protocols.");
585 : }
586 117 : f_protocol = p.p_proto;
587 117 : }
588 :
589 :
590 : /** \brief Set the protocol to use to filter addresses.
591 : *
592 : * This function sets the protocol as one of the following:
593 : *
594 : * \li IPPROTO_IP -- only return IP address supporting the IP protocol
595 : * (this is offered because getaddrinfo() may return such IP addresses.)
596 : * \li IPPROTO_TCP -- only return IP address supporting TCP
597 : * \li IPPROTO_UDP -- only return IP address supporting UDP
598 : *
599 : * Any other value is refused. To reset the protocol to the default,
600 : * which is "do not filter by protocol", call the clear_protocol().
601 : *
602 : * \exception addr_invalid_argument_exception
603 : * If the string passed to this function is not one of the acceptable
604 : * protocols (ip, tcp, udp), then this exception is raised.
605 : *
606 : * \param[in] protocol The default protocol for this parser.
607 : *
608 : * \sa clear_protocol()
609 : * \sa get_protocol()
610 : */
611 132087 : void addr_parser::set_protocol(int const protocol)
612 : {
613 : // make sure that's a protocol we support
614 : //
615 132087 : switch(protocol)
616 : {
617 131887 : case IPPROTO_IP:
618 : case IPPROTO_TCP:
619 : case IPPROTO_UDP:
620 131887 : break;
621 :
622 200 : default:
623 : throw addr_invalid_argument(
624 800 : std::string("unknown protocol number \"")
625 800 : + std::to_string(protocol)
626 600 : + "\", expected \"tcp\" or \"udp\".");
627 :
628 : }
629 :
630 131887 : f_protocol = protocol;
631 131887 : }
632 :
633 :
634 : /** \brief Use this function to reset the protocol back to "no default."
635 : *
636 : * This function sets the protocol to -1 (which is something you cannot
637 : * do by calling the set_protocol() functions above.)
638 : *
639 : * The -1 special value means that the protocol is not defined, that
640 : * there is no default. In most cases this means all the addresses
641 : * that match, ignoring the protocol, will be returned by the parse()
642 : * function.
643 : *
644 : * \sa set_protocol()
645 : * \sa get_protocol()
646 : */
647 3 : void addr_parser::clear_protocol()
648 : {
649 3 : f_protocol = -1;
650 3 : }
651 :
652 :
653 : /** \brief Retrieve the protocol as defined by the set_protocol().
654 : *
655 : * This function returns the protocol number as defined by the
656 : * set_protocol.
657 : *
658 : * When defined, the protocol is used whenever we call the
659 : * getaddrinfo() function. In general, this means the IP addresses
660 : * returned will have to match that protocol.
661 : *
662 : * This function may return -1. The value -1 is used as "do not
663 : * filter by protocol". The protocol can be set to -1 by calling
664 : * the clear_protocol() function.
665 : *
666 : * \return The parser default protocol.
667 : *
668 : * \sa set_protocol()
669 : * \sa clear_protocol()
670 : */
671 216 : int addr_parser::get_protocol() const
672 : {
673 216 : return f_protocol;
674 : }
675 :
676 :
677 : /** \brief Change the set of flags defining the sorting order.
678 : *
679 : * The parser, once done parsing all the input, will sort the addresses
680 : * according to these flags.
681 : *
682 : * By default, it does not re-arrange the order in which the addresses
683 : * were defined.
684 : *
685 : * The parser is capable of sorting by IP type (IPv6 or IPv4 first),
686 : * and simply by IP addresses. It can also merge adjacent or overlapping
687 : * ranges into a single range.
688 : *
689 : * \exception addr_invalid_argument
690 : * This exception is raised you set SORT_IPV6_FIRST and SORT_IPV4_FIRST
691 : * at the same time because these flags are mutually exclusive.
692 : *
693 : * \param[in] sort The sort parameters.
694 : *
695 : * \sa get_sort_order()
696 : */
697 12 : void addr_parser::set_sort_order(sort_t const sort)
698 : {
699 12 : if((sort & (SORT_IPV6_FIRST | SORT_IPV4_FIRST)) == (SORT_IPV6_FIRST | SORT_IPV4_FIRST))
700 : {
701 3 : throw addr_invalid_argument("addr_parser::set_sort_order(): flags SORT_IPV6_FIRST and SORT_IPV4_FIRST are mutually exclusive.");
702 : }
703 :
704 11 : f_sort = sort;
705 11 : }
706 :
707 :
708 : /** \brief Get the flags defining the sort order of the parser.
709 : *
710 : * This function returns the sort order of the parser as a set of flags.
711 : *
712 : * By default this value is set to NO_SORT meaning that the input is
713 : * kept as is.
714 : *
715 : * \return The sort order flags.
716 : *
717 : * \sa set_sort_order()
718 : */
719 23 : sort_t addr_parser::get_sort_order() const
720 : {
721 23 : return f_sort;
722 : }
723 :
724 :
725 : /** \brief Set or clear allow flags in the parser.
726 : *
727 : * This parser has a set of flags it uses to know whether the input
728 : * string can include certain things such as a port or a mask.
729 : *
730 : * This function is used to allow or require certain parameters and
731 : * to disallow others.
732 : *
733 : * By default, the ADDRESS and PORT flags are set, meaning that an
734 : * address and a port can appear, but either or both are optinal.
735 : * If unspecified, then the default will be used. If not default
736 : * is defined, then the parser may fail in this situation.
737 : *
738 : * One problem is that we include contradictory syntatical features.
739 : * The parser supports lists of addresses separated by commas and
740 : * lists of ports separated by commas. Both are not supported
741 : * simultaneously. This means you want to allow multiple addresses
742 : * separated by commas, the function makes sure that the multiple
743 : * port separated by commas support is turned off.
744 : *
745 : * \li `ADDRESS` -- the IP address is allowed, but optional
746 : * \li `REQUIRED_ADDRESS` -- the IP address is mandatory
747 : * \li `PORT` -- the port is allowed, but optional
748 : * \li `REQUIRED_PORT` -- the port is mandatory
749 : * \li `MASK` -- the mask is allowed, but optional
750 : * \li `MULTI_ADDRESSES_COMMAS` -- the input can have multiple addresses
751 : * separated by commas (prevents MULTI_PORTS_COMMAS)
752 : * \li `MULTI_ADDRESSES_SPACES` -- the input can have multiple addresses
753 : * separated by spaces
754 : * \li `MULTI_ADDRESSES_NEWLINES` -- the input can have multiple addresses
755 : * separated by newlines (one address:port per line)
756 : * \li `MULTI_PORTS_SEMICOLONS` -- the input can have multiple ports
757 : * separated by semicolons _NOT IMPLEMENTED YET_
758 : * \li `MULTI_PORTS_COMMAS` -- the input can have multiple ports separated
759 : * by commas (prevents MULTI_ADDRESSES_COMMAS) _NOT IMPLEMENTED YET_
760 : * \li `PORT_RANGE` -- the input supports port ranges (p1-p2) _NOT
761 : * IMPLEMENTED YET_
762 : * \li `ADDRESS_RANGE` -- the input supports address ranges (addr-addr) _NOT
763 : * IMPLEMENTED YET_
764 : *
765 : * The `MULTI_ADDRESSES_COMMAS`, `MULTI_ADDRESSES_SPACES`, and
766 : * `MULTI_ADDRESSES_NEWLINES` can be used together in which case any
767 : * number of both characters are accepted between addresses.
768 : *
769 : * Note that the `MULTI_ADDRESSES_COMMAS` and `MULTI_PORTS_COMMAS` are
770 : * mutually exclusive. The last set_allow() counts as the one you are
771 : * interested in.
772 : *
773 : * \param[in] flag The flag to set or clear.
774 : * \param[in] allow Whether to allow (true) or disallow (false).
775 : *
776 : * \sa get_allow()
777 : */
778 397389 : void addr_parser::set_allow(allow_t const flag, bool const allow)
779 : {
780 397389 : if(flag < static_cast<allow_t>(0)
781 397369 : || flag >= allow_t::ALLOW_max)
782 : {
783 120 : throw addr_invalid_argument("addr_parser::set_allow(): flag has to be one of the valid flags.");
784 : }
785 :
786 397349 : f_flags[static_cast<int>(flag)] = allow;
787 :
788 : // if we just set a certain flag, others may need to go to false
789 : //
790 397349 : if(allow)
791 : {
792 : // we can only support one type of commas
793 : //
794 397022 : switch(flag)
795 : {
796 20 : case allow_t::ALLOW_MULTI_ADDRESSES_COMMAS:
797 20 : f_flags[static_cast<int>(allow_t::ALLOW_MULTI_PORTS_COMMAS)] = false;
798 20 : break;
799 :
800 2 : case allow_t::ALLOW_MULTI_PORTS_COMMAS:
801 2 : f_flags[static_cast<int>(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS)] = false;
802 2 : break;
803 :
804 397000 : default:
805 397000 : break;
806 :
807 : }
808 : }
809 397349 : }
810 :
811 :
812 : /** \brief Retrieve the current statius of an allow flag.
813 : *
814 : * This function returns the current status of the allow flags.
815 : *
816 : * By default, the `ADDRESS` and `PORT` flags are set to true.
817 : * All the other flags are set to false.
818 : *
819 : * You may change the value of an allow flag by calling the
820 : * set_allow() function.
821 : *
822 : * \param[in] flag Which flag is to be checked.
823 : *
824 : * \return The value of the flag: true or false.
825 : *
826 : * \sa set_allow()
827 : */
828 1255844 : bool addr_parser::get_allow(allow_t const flag) const
829 : {
830 1255844 : if(flag < static_cast<allow_t>(0)
831 1255834 : || flag >= allow_t::ALLOW_max)
832 : {
833 60 : throw addr_invalid_argument("addr_parser::get_allow(): flag has to be one of the valid flags.");
834 : }
835 :
836 1255824 : return f_flags[static_cast<int>(flag)];
837 : }
838 :
839 :
840 : /** \brief Check whether errors were registered so far.
841 : *
842 : * This function returns true if the system detected errors in one
843 : * of the previous calls to parse(). The flag can be cleared using
844 : * the clear_errors() function.
845 : *
846 : * On construction and after a call to clear_error(), this flag is
847 : * always false. If you are to call parser() multiple times with
848 : * the same addr_parser object, then you want to make sure to call
849 : * the clear_errors() function before calling the parse() function.
850 : * Otherwise you won't know whether errors occurred in a earlier
851 : * or later call.
852 : *
853 : * \code
854 : * // first time, not required
855 : * parser.parse(...);
856 : * ...
857 : *
858 : * // next time, required
859 : * parser.clear_errors();
860 : * parser.parse(...);
861 : * ...
862 : * \endcode
863 : *
864 : * \return true if errors were generated.
865 : */
866 132228 : bool addr_parser::has_errors() const
867 : {
868 132228 : return !f_error.empty();
869 : }
870 :
871 :
872 : /** \brief Emit an error and save it in this class.
873 : *
874 : * This function adds the message to the error string part of this
875 : * object. A newline is also added at the end of the message.
876 : *
877 : * Next the function increments the error counter.
878 : *
879 : * \note
880 : * You are expected to emit one error at a time. If you want to
881 : * emit several messages in a row, that will work and properly
882 : * count each message.
883 : *
884 : * \param[in] msg The message to add to the parser error messages.
885 : *
886 : * \sa error_messages()
887 : */
888 136 : void addr_parser::emit_error(std::string const & msg)
889 : {
890 136 : f_error += msg;
891 136 : f_error += "\n";
892 136 : ++f_error_count;
893 136 : }
894 :
895 :
896 : /** \brief Return the current error messages.
897 : *
898 : * The error messages are added to the addr_parser using the
899 : * emit_error() function.
900 : *
901 : * This function does not clear the list of error messages.
902 : * To do that, call the clear_errors() function.
903 : *
904 : * The number of messages can be determined by counting the
905 : * number of "\n" characters in the string. The error_count()
906 : * will return that same number (assuming no message included
907 : * a '\n' character when emit_error() was called.)
908 : *
909 : * \return A string with the list of messages.
910 : *
911 : * \sa emit_error()
912 : * \sa clear_errors()
913 : */
914 86 : std::string const & addr_parser::error_messages() const
915 : {
916 86 : return f_error;
917 : }
918 :
919 :
920 : /** \brief Return the number of error messages that were emitted.
921 : *
922 : * Each time the emit_error() function is called, the error
923 : * counter is incremented by 1. This function returns that
924 : * error counter.
925 : *
926 : * The clear_errors() function can be used to clear the
927 : * counter back to zero.
928 : *
929 : * \return The number of errors that were emitted so far.
930 : *
931 : * \sa emit_error()
932 : */
933 63 : int addr_parser::error_count() const
934 : {
935 63 : return f_error_count;
936 : }
937 :
938 :
939 : /** \brief Clear the error message and error counter.
940 : *
941 : * This function clears all the error messages and reset the
942 : * counter back to zero. In order words, it will be possible
943 : * to tell how many times the emit_error() was called since
944 : * the start or the last clear_errors() call.
945 : *
946 : * To retrieve a copy of the error counter, use the error_count()
947 : * function.
948 : *
949 : * \sa error_count()
950 : */
951 171 : void addr_parser::clear_errors()
952 : {
953 171 : f_error.clear();
954 171 : f_error_count = 0;
955 171 : }
956 :
957 :
958 : /** \brief Parse a string of addresses, ports, and masks.
959 : *
960 : * This function is used to parse the list of addresses defined
961 : * in the \p in parameter.
962 : *
963 : * One address is composed of one to three elements:
964 : *
965 : * \code
966 : * [ address ] [ ':' port ] [ '/' mask ]
967 : * \endcode
968 : *
969 : * Although all three elements are optional (at least by default),
970 : * a valid address is expected to include at least one of the
971 : * three elements. (i.e. an empty string is just skipped silently.)
972 : *
973 : * ### Multiple Addresses
974 : *
975 : * Multiple addresses can be defined if at least one of the
976 : * `MULTI_ADDRESSES_COMMAS`, `MULTI_ADDRESSES_SPACES`, or
977 : * `MULTI_ADDRESSES_NEWLINES` allow flags is set to true.
978 : *
979 : * The separator characters are limited to what is allowed. If all three
980 : * flags are set, then all the characters are allowed and are viewed as
981 : * valid address separators.
982 : *
983 : * ### Make Address Field Required
984 : *
985 : * To make the address field a required field, set the
986 : * `REQUIRED_ADDRESS` flag (see set_allow()) to true and do not define a
987 : * default address (see set_default_address()).
988 : *
989 : * ### Make Port Field Required
990 : *
991 : * To make the port field a required fiel, set the `REQUIRED_PORT`
992 : * flag (see set_allow()) to true and do not define a default port
993 : * (see set_default_port()).
994 : *
995 : * ### Allow Mask
996 : *
997 : * The mask cannot be made mandatory. However, you have to set
998 : * the `MASK` flag to true to allow it. By default it is not
999 : * allowed.
1000 : *
1001 : * ### Ranges
1002 : *
1003 : * Ranges are not yet implemented.
1004 : *
1005 : * ### Sort
1006 : *
1007 : * After the function parsed all the input, it sorts the results in
1008 : * the vector of ranges. Ranges are sorted using the addr_range::compare()
1009 : * function. Sorting can also be used to merge ranges. So if two ranges
1010 : * have an overlap or are adjacent, the union of those two ranges will
1011 : * be kept and the two ranges are otherwise removed from the result.
1012 : *
1013 : * The sort is particularly useful if you first want to connect with
1014 : * IPv6 addresses instead of IPv4 (which is the current expected behavior
1015 : * of your services and tools).
1016 : *
1017 : * \todo
1018 : * The ALLOW_COMMENT_HASH and ALLOW_COMMENT_SEMICOLON currently gives the
1019 : * user a way to comment with any type of separator (commas, spaces or new
1020 : * lines). I think that in the following, everything after the # should be
1021 : * viewed as a comment. Right now, the 3rd IP:port is viewed as a valid entry:
1022 : * \todo
1023 : * \code
1024 : * 127.0.0.1,#10.0.0.1,192.168.0.1
1025 : * \endcode
1026 : * \todo
1027 : * So the result is: 127.0.0.1 and 192.168.0.1. Some day, though, that
1028 : * line would be viewed as just 127.0.0.1.
1029 : *
1030 : * \param[in] in The input string to be parsed.
1031 : *
1032 : * \return A vector of address ranges, see has_errors() to determine whether
1033 : * errors occurred while parsing the input.
1034 : *
1035 : * \sa has_errors()
1036 : */
1037 132078 : addr_range::vector_t addr_parser::parse(std::string const & in)
1038 : {
1039 132078 : addr_range::vector_t result;
1040 132078 : bool new_lines_allowed(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_NEWLINES));
1041 :
1042 132078 : std::string separators;
1043 132078 : if(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_COMMAS))
1044 : {
1045 39 : separators += ',';
1046 : }
1047 132078 : if(get_allow(allow_t::ALLOW_MULTI_ADDRESSES_SPACES))
1048 : {
1049 26 : separators += ' ';
1050 : }
1051 132078 : if(new_lines_allowed)
1052 : {
1053 : // TBD: consider supporting '\r'?
1054 : //
1055 26 : separators += '\n';
1056 : }
1057 :
1058 132078 : std::string const comment_chars(
1059 528312 : std::string(get_allow(allow_t::ALLOW_COMMENT_HASH) ? "#" : "")
1060 264156 : + (get_allow(allow_t::ALLOW_COMMENT_SEMICOLON) ? ";" : ""));
1061 :
1062 132078 : if(separators.empty())
1063 : {
1064 132036 : std::string::size_type ec(in.length());
1065 132036 : std::string::size_type s(0);
1066 132066 : while(s < in.length() && isspace(in[s]))
1067 : {
1068 30 : ++s;
1069 : }
1070 132036 : if(!comment_chars.empty())
1071 : {
1072 16 : auto const comment(std::find_first_of(in.begin() + s, in.end(), comment_chars.begin(), comment_chars.end()));
1073 16 : if(comment != in.end())
1074 : {
1075 12 : ec = comment - in.begin();
1076 : }
1077 : }
1078 132050 : while(ec > 0 && isspace(in[ec - 1]))
1079 : {
1080 14 : --ec;
1081 : }
1082 :
1083 132036 : if(s != 0
1084 132036 : || ec != in.length())
1085 : {
1086 22 : parse_cidr(in.substr(s, ec - s), result);
1087 : }
1088 : else
1089 : {
1090 132014 : parse_cidr(in, result);
1091 : }
1092 : }
1093 : else
1094 : {
1095 42 : std::string::size_type s(0);
1096 198 : while(s < in.length())
1097 : {
1098 156 : auto const it(std::find_first_of(in.begin() + s, in.end(), separators.begin(), separators.end()));
1099 156 : std::string::size_type e(it - in.begin());
1100 156 : if(e > s)
1101 : {
1102 142 : std::string::size_type ec(e);
1103 142 : if(!comment_chars.empty())
1104 : {
1105 57 : auto const comment(std::find_first_of(in.begin() + s, in.begin() + ec, comment_chars.begin(), comment_chars.end()));
1106 57 : if(comment != in.begin() + ec)
1107 : {
1108 17 : ec = comment - in.begin();
1109 : }
1110 57 : if(new_lines_allowed
1111 48 : && e < in.length()
1112 105 : && in[e] != '\n')
1113 : {
1114 8 : auto const nl(std::find(in.begin() + e, in.end(), '\n'));
1115 8 : e = nl - in.begin();
1116 : }
1117 : }
1118 :
1119 : // ignore lines with just a comment
1120 : //
1121 142 : if(ec > s)
1122 : {
1123 134 : parse_cidr(in.substr(s, ec - s), result);
1124 : }
1125 : }
1126 156 : s = e + 1;
1127 : }
1128 : }
1129 :
1130 : // run a normal sort first then attempt a merge if requested
1131 : //
1132 132078 : if((f_sort & (SORT_FULL | SORT_MERGE)) != 0)
1133 : {
1134 7 : std::stable_sort(result.begin(), result.end());
1135 : }
1136 :
1137 132078 : if((f_sort & SORT_MERGE) != 0)
1138 : {
1139 6 : std::size_t max(result.size());
1140 6 : if(max > 1)
1141 : {
1142 44 : for(std::size_t idx(0); idx < max - 1; ++idx)
1143 : {
1144 38 : addr_range const r(result[idx].union_if_possible(result[idx + 1]));
1145 38 : if(r.is_defined()
1146 38 : && !r.is_empty())
1147 : {
1148 : // merge worked, update the vector
1149 : //
1150 6 : result[idx] = r;
1151 6 : result.erase(result.begin() + idx + 1);
1152 6 : --max;
1153 6 : --idx;
1154 : }
1155 38 : }
1156 : }
1157 : }
1158 :
1159 : // move IPv4 or IPv6 first (should be IPv6 in newer systems)
1160 : //
1161 132078 : if((f_sort & SORT_IPV4_FIRST) != 0)
1162 : {
1163 2 : std::stable_sort(
1164 : result.begin()
1165 : , result.end()
1166 35 : , [](auto const & a, auto const & b)
1167 : {
1168 35 : switch(a.compare(b))
1169 : {
1170 10 : case compare_t::COMPARE_IPV4_VS_IPV6:
1171 : case compare_t::COMPARE_FIRST:
1172 10 : return true;
1173 :
1174 25 : default:
1175 25 : return false;
1176 :
1177 : }
1178 : });
1179 : }
1180 132076 : else if((f_sort & SORT_IPV6_FIRST) != 0)
1181 : {
1182 5 : std::stable_sort(
1183 : result.begin()
1184 : , result.end()
1185 68 : , [](auto const & a, auto const & b)
1186 : {
1187 68 : switch(a.compare(b))
1188 : {
1189 21 : case compare_t::COMPARE_IPV6_VS_IPV4:
1190 : case compare_t::COMPARE_FIRST:
1191 21 : return true;
1192 :
1193 47 : default:
1194 47 : return false;
1195 :
1196 : }
1197 : });
1198 : }
1199 :
1200 264156 : return result;
1201 132078 : }
1202 :
1203 :
1204 : /** \brief Check one address.
1205 : *
1206 : * This function checks one address, although if it is a name, it could
1207 : * represent multiple IP addresses.
1208 : *
1209 : * This function separate the address:port from the mask if the mask is
1210 : * allowed. Then it parses the address:port part and the mask separately.
1211 : *
1212 : * \param[in] in The address to parse.
1213 : * \param[in,out] result The list of resulting addresses.
1214 : */
1215 132170 : void addr_parser::parse_cidr(std::string const & in, addr_range::vector_t & result)
1216 : {
1217 132170 : std::string address(snapdev::trim_string(in));
1218 132170 : if(get_allow(allow_t::ALLOW_MASK))
1219 : {
1220 : // check whether there is a mask
1221 : //
1222 547 : std::string mask;
1223 :
1224 547 : std::string::size_type const p(address.find('/'));
1225 547 : if(p != std::string::npos)
1226 : {
1227 454 : mask = address.substr(p + 1);
1228 454 : address = address.substr(0, p);
1229 : }
1230 :
1231 547 : int const errcnt(f_error_count);
1232 :
1233 : // handle the address first
1234 : //
1235 547 : addr_range::vector_t addr_mask;
1236 547 : bool const is_ipv4(parse_address(address, mask, addr_mask));
1237 :
1238 : // now check for the mask
1239 : //
1240 1137 : for(auto & am : addr_mask)
1241 : {
1242 590 : std::string m(mask);
1243 590 : if(m.empty())
1244 : {
1245 : // the mask was not defined in the input, then adapt it to
1246 : // the type of address we got in 'am'
1247 : //
1248 116 : if(is_ipv4)
1249 : {
1250 77 : m = f_default_mask4;
1251 : }
1252 39 : else if(!f_default_mask6.empty())
1253 : {
1254 : // parse_mask() expects '[...]' around IPv6 addresses
1255 : // we remove them when set_mask() is called
1256 : //
1257 : // however, we have to make sure that we do not add
1258 : // brackets around a simple decimal number (i.e. a
1259 : // CIDR opposed to an IPv6 address)
1260 : //
1261 37 : if(f_default_mask6.find(':') != std::string::npos)
1262 : {
1263 25 : m = "[" + f_default_mask6 + "]";
1264 : }
1265 : }
1266 : }
1267 :
1268 : // TODO: the am.get_from() may be wrong since now we support
1269 : // ranges so it could require am.get_to() instead
1270 : //
1271 590 : parse_mask(m, am.get_from(), is_ipv4 && am.get_from().is_ipv4());
1272 590 : }
1273 :
1274 : // now append the list to the result if no errors occurred
1275 : //
1276 547 : if(errcnt == f_error_count)
1277 : {
1278 450 : result.insert(result.end(), addr_mask.begin(), addr_mask.end());
1279 : }
1280 547 : }
1281 : else
1282 : {
1283 : // no mask allowed, if there is one, this call will fail
1284 : //
1285 131623 : parse_address(address, std::string(), result);
1286 : }
1287 264340 : }
1288 :
1289 :
1290 : /** \brief Parse one address.
1291 : *
1292 : * This function is called with one address. It determines whether we
1293 : * are dealing with an IPv4 or an IPv6 address and call the
1294 : * corresponding sub-function.
1295 : *
1296 : * An address is considered an IPv6 address if it starts with a '['
1297 : * character.
1298 : *
1299 : * \note
1300 : * The input cannot include a mask. It has to already have been
1301 : * removed.
1302 : *
1303 : * \note
1304 : * The mask parameter is only used to determine whether this function
1305 : * is being called with an IPv6 or not. It is otherwise ignored.
1306 : *
1307 : * \param[in] in The input address eventually including a port.
1308 : * \param[in] mask The mask used to determine whether we are dealing with
1309 : * an IPv6 or not.
1310 : * \param[in,out] result The list of resulting addresses.
1311 : *
1312 : * \return true if the address was parsed as an IPv4 address, false if it
1313 : * was determined to be an IPv6 address.
1314 : */
1315 132170 : bool addr_parser::parse_address(
1316 : std::string const & in
1317 : , std::string const & mask
1318 : , addr_range::vector_t & result)
1319 : {
1320 : // if the number of colons is 2 or more, the address has to be an
1321 : // IPv6 address so we have a very special case at the start for that
1322 : //
1323 132170 : std::ptrdiff_t const colons(std::count(in.begin(), in.end(), ':'));
1324 132170 : if(colons >= 2LL)
1325 : {
1326 65881 : parse_address6(in, colons, result);
1327 65881 : return false;
1328 : }
1329 :
1330 66289 : if(in.empty()
1331 66289 : || in[0] == ':') // if it start with ':' then there is no address
1332 : {
1333 : // if the address is empty, then use the mask to determine the
1334 : // type of IP address (note: if the address starts with ':'
1335 : // it is considered empty since an IPv6 would have a '[' at
1336 : // the start)
1337 : //
1338 326 : if(!mask.empty())
1339 : {
1340 115 : if(mask[0] == '[')
1341 : {
1342 : // IPv6 parsing
1343 : //
1344 5 : parse_address6(in, colons, result);
1345 5 : return false;
1346 : }
1347 : else
1348 : {
1349 : // if the number is 33 or more, it has to be IPv6, otherwise
1350 : // we cannot know...
1351 : //
1352 110 : int mask_count(0);
1353 372 : for(char const * s(mask.c_str()); *s != '\0'; ++s)
1354 : {
1355 276 : if(*s >= '0' && *s <= '9')
1356 : {
1357 267 : mask_count = mask_count * 10 + *s - '0';
1358 267 : if(mask_count > 1000)
1359 : {
1360 : // not valid
1361 : //
1362 5 : mask_count = -1;
1363 5 : break;;
1364 : }
1365 : }
1366 : else
1367 : {
1368 : // not a valid decimal number
1369 : //
1370 9 : mask_count = -1;
1371 9 : break;
1372 : }
1373 : }
1374 110 : if(mask_count > 32)
1375 : {
1376 96 : parse_address6(in, colons, result);
1377 96 : return false;
1378 : }
1379 : else
1380 : {
1381 14 : parse_address4(in, result);
1382 14 : return true;
1383 : }
1384 : }
1385 : }
1386 : else
1387 : {
1388 211 : if(f_default_address4.empty()
1389 211 : && !f_default_address6.empty())
1390 : {
1391 102 : parse_address6(in, colons, result);
1392 102 : return false;
1393 : }
1394 : else
1395 : {
1396 109 : parse_address4(in, result);
1397 109 : return true;
1398 : }
1399 : }
1400 : }
1401 : else
1402 : {
1403 : // if an address has a ']' then it is IPv6 even if the '['
1404 : // is missing, that being said, it is still considered
1405 : // invalid as per our processes
1406 : //
1407 65963 : if(in[0] == '['
1408 65963 : || in.find(']') != std::string::npos)
1409 : {
1410 2 : parse_address6(in, colons, result);
1411 2 : return false;
1412 : }
1413 : else
1414 : {
1415 : // if there is no port, then a ':' can be viewed as an IPv6
1416 : // address because there is no other ':', but if there are
1417 : // '.' before the ':' then we assume that it is IPv4 still
1418 : //
1419 65961 : if(!get_allow(allow_t::ALLOW_PORT)
1420 65961 : && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
1421 : {
1422 4 : std::string::size_type const p(in.find(':'));
1423 4 : if(p != std::string::npos
1424 4 : && in.find('.') > p)
1425 : {
1426 1 : parse_address6(in, colons, result);
1427 1 : return false;
1428 : }
1429 : else
1430 : {
1431 3 : parse_address4(in, result);
1432 3 : return true;
1433 : }
1434 : }
1435 : else
1436 : {
1437 65957 : parse_address4(in, result);
1438 65957 : return true;
1439 : }
1440 : }
1441 : }
1442 : }
1443 :
1444 :
1445 : /** \brief Parse one IPv4 address.
1446 : *
1447 : * This function checks the input parameter \p in and extracts the
1448 : * address and port. There is a port if the input strings includes
1449 : * a `':'` character.
1450 : *
1451 : * If this function detects that a port is not allowed and yet
1452 : * a `':'` character is found, then it generates an error and
1453 : * returns without adding anything to `result`.
1454 : *
1455 : * \param[in] in The input string with the address and optional port.
1456 : * \param[in,out] result The list of resulting addresses.
1457 : */
1458 66083 : void addr_parser::parse_address4(std::string const & in, addr_range::vector_t & result)
1459 : {
1460 66083 : std::string address;
1461 66083 : std::string port_str;
1462 :
1463 66083 : std::string::size_type const p(in.find(':'));
1464 :
1465 66083 : if(get_allow(allow_t::ALLOW_PORT)
1466 66083 : || get_allow(allow_t::ALLOW_REQUIRED_PORT))
1467 : {
1468 : // the address can include a port
1469 : //
1470 66079 : if(p != std::string::npos)
1471 : {
1472 65933 : address = in.substr(0, p);
1473 65933 : port_str = in.substr(p + 1);
1474 : }
1475 : else
1476 : {
1477 146 : address = in;
1478 : }
1479 : }
1480 4 : else if(p == std::string::npos)
1481 : {
1482 3 : address = in;
1483 : }
1484 : else
1485 : {
1486 1 : emit_error("Port not allowed (" + in + ").");
1487 1 : return;
1488 : }
1489 :
1490 66082 : parse_address_range_port(address, port_str, result, false);
1491 66084 : }
1492 :
1493 :
1494 : /** \brief Parse one IPv6 address.
1495 : *
1496 : * This function checks the input parameter \p in and extracts the
1497 : * address and port. There is a port if the input strings includes
1498 : * a `':'` character after the closing square bracket (`']'`).
1499 : *
1500 : * If this function detects that a port is not allowed and yet
1501 : * a `':'` character is found, then it generates an error and
1502 : * returns without adding anything to `result`.
1503 : *
1504 : * \note
1505 : * This function is expected to be called with an IPv6.
1506 : *
1507 : * \param[in] in The input string with the address and optional port.
1508 : * \param[in] colons The number of colons in \p in.
1509 : * \param[in,out] result The list of resulting addresses.
1510 : */
1511 66087 : void addr_parser::parse_address6(std::string const & in, std::size_t const colons, addr_range::vector_t & result)
1512 : {
1513 66087 : std::string address;
1514 66087 : std::string port_str;
1515 :
1516 : // remove the square brackets if present
1517 : //
1518 66087 : if(!in.empty()
1519 66087 : && in[0] == '[')
1520 : {
1521 65779 : std::string::size_type p(in.find(']'));
1522 :
1523 65779 : if(p == std::string::npos)
1524 : {
1525 1 : emit_error("IPv6 is missing the ']' (" + in + ").");
1526 1 : return;
1527 : }
1528 :
1529 65778 : address = in.substr(1, p - 1);
1530 :
1531 65778 : ++p;
1532 65778 : if(p < in.length())
1533 : {
1534 65770 : if(in[p] != ':')
1535 : {
1536 1 : emit_error("The IPv6 address \"" + in + "\" is followed by unknown data.");
1537 1 : return;
1538 : }
1539 :
1540 65769 : if(!get_allow(allow_t::ALLOW_PORT)
1541 65769 : && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
1542 : {
1543 : // even just a ':' is no allowed in this case
1544 : //
1545 1 : emit_error("Port not allowed (" + in + ").");
1546 1 : return;
1547 : }
1548 :
1549 65768 : port_str = in.substr(p + 1);
1550 : }
1551 : }
1552 308 : else if(colons == 1)
1553 : {
1554 : // this usually happens when only a port was specified
1555 : // (so here p == 0 will be true 99% of the time)
1556 : //
1557 202 : std::string::size_type const p(in.find(':'));
1558 202 : if(p == std::string::npos)
1559 : {
1560 : throw logic_error("colons == 1 & we did not find it!"); // LCOV_EXCL_LINE
1561 : }
1562 202 : address = in.substr(0, p);
1563 202 : port_str = in.substr(p + 1);
1564 : }
1565 : else
1566 : {
1567 106 : address = in;
1568 : }
1569 :
1570 66084 : parse_address_range_port(address, port_str, result, true);
1571 66090 : }
1572 :
1573 :
1574 : /** \brief Parse an address range and a port.
1575 : *
1576 : * This function checks whether the address part includes a dash, if so, it
1577 : * is considered an address range and the function transforms it in a "from"
1578 : * and a "to" set of addresses.
1579 : *
1580 : * This function emits an error if the address is just a dash (-). If you
1581 : * want to get the default IP address, use the empty string instead.
1582 : *
1583 : * \note
1584 : * The address range has to be enabled for it to be active. If a range is
1585 : * not allowed, then any '-' is ignored. That also allows you to use domain
1586 : * names that may otherwise include a dash character (i.e. "bad-domain.com").
1587 : *
1588 : * \param[in] addresses One or two addresses separated by a dash (-).
1589 : * \param[in] port_str The port as a string.
1590 : * \param[out] result The vector where the results get saved.
1591 : * \param[in] ipv6 Whether the parser needs to use AF_INET or AF_INET6.
1592 : */
1593 132166 : void addr_parser::parse_address_range_port(
1594 : std::string const & addresses
1595 : , std::string const & port_str
1596 : , addr_range::vector_t & result
1597 : , bool ipv6)
1598 : {
1599 132166 : std::string::size_type p(std::string::npos);
1600 132166 : if(get_allow(allow_t::ALLOW_ADDRESS_RANGE))
1601 : {
1602 118 : p = addresses.find('-');
1603 : }
1604 132166 : if(p == std::string::npos)
1605 : {
1606 132112 : parse_address_port(addresses, port_str, result, ipv6);
1607 132112 : return;
1608 : }
1609 :
1610 54 : std::string const from(addresses.substr(0, p));
1611 54 : std::string const to(addresses.substr(p + 1));
1612 :
1613 54 : if(from.empty()
1614 54 : && to.empty())
1615 : {
1616 6 : emit_error("An address range requires at least one of the \"from\" or \"to\" addresses.");
1617 2 : return;
1618 : }
1619 :
1620 52 : addr_range::vector_t from_result;
1621 52 : if(!from.empty())
1622 : {
1623 49 : parse_address_port_ignore_duplicates(from, port_str, from_result, ipv6);
1624 49 : if(from_result.size() > 1)
1625 : {
1626 : emit_error("The \"from\" of an address range must be exactly one address."); // LCOV_EXCL_LINE
1627 : return; // LCOV_EXCL_LINE
1628 : }
1629 49 : if(from_result.empty())
1630 : {
1631 : // parse_address_port_ignore_duplicates() failed
1632 : //
1633 1 : return;
1634 : }
1635 : }
1636 :
1637 51 : addr_range::vector_t to_result;
1638 51 : if(!to.empty())
1639 : {
1640 : // our parse_address_port_ignore_duplicates() function sees the input address as
1641 : // the "from" address; the following moves that result to the
1642 : // "to" address of our own result (assuming the parsing worked
1643 : // as expected)
1644 : //
1645 49 : parse_address_port_ignore_duplicates(to, port_str, to_result, ipv6);
1646 49 : if(to_result.size() > 1)
1647 : {
1648 : emit_error("The \"to\" of an address range must be exactly one address."); // LCOV_EXCL_LINE
1649 : return; // LCOV_EXCL_LINE
1650 : }
1651 49 : if(to_result.empty())
1652 : {
1653 : // parse_address_port_ignore_duplicates() failed
1654 : //
1655 1 : return;
1656 : }
1657 48 : to_result[0].swap_from_to();
1658 : }
1659 :
1660 50 : if(!from_result.empty()
1661 50 : && !to_result.empty())
1662 : {
1663 46 : from_result[0].set_to(to_result[0].get_to());
1664 92 : if((f_sort & SORT_NO_EMPTY) == 0
1665 46 : || !from_result[0].is_empty())
1666 : {
1667 40 : result.push_back(from_result[0]);
1668 : }
1669 : }
1670 4 : else if(!from_result.empty())
1671 : {
1672 2 : result.push_back(from_result[0]);
1673 : }
1674 : else //if(!to_result.empty())
1675 : {
1676 2 : result.push_back(to_result[0]);
1677 : }
1678 61 : }
1679 :
1680 :
1681 : /** \brief Parse the address and port.
1682 : *
1683 : * This function receives an address and a port string and
1684 : * convert them in an addr object which gets saved in
1685 : * the specified result range vector.
1686 : *
1687 : * The address can be an IPv4 or an IPv6 address.
1688 : *
1689 : * The port may be numeric or a name such as `"http"`.
1690 : *
1691 : * \note
1692 : * When this function gets called with an empty string as the address or
1693 : * the port, then it makes use of the user defined default unless that
1694 : * default string is also empty in which case it uses a system default.
1695 : *
1696 : * \param[in] address The address to convert to binary.
1697 : * \param[in] port_str The port as a string.
1698 : * \param[out] result The range where we save the results.
1699 : * \param[in] ipv6 Use the default IPv6 address if the address is empty.
1700 : */
1701 132210 : void addr_parser::parse_address_port(
1702 : std::string address
1703 : , std::string port_str
1704 : , addr_range::vector_t & result
1705 : , bool ipv6)
1706 : {
1707 : // make sure the port is good
1708 : //
1709 132210 : bool const defined_port(!port_str.empty());
1710 132210 : if(!defined_port)
1711 : {
1712 334 : if(get_allow(allow_t::ALLOW_REQUIRED_PORT))
1713 : {
1714 18 : emit_error("Required port is missing.");
1715 6 : return;
1716 : }
1717 328 : if(f_default_port != -1)
1718 : {
1719 69 : port_str = std::to_string(f_default_port);
1720 : }
1721 : }
1722 :
1723 : // make sure the address is good
1724 : //
1725 132204 : if(address.empty())
1726 : {
1727 326 : if(get_allow(allow_t::ALLOW_REQUIRED_ADDRESS))
1728 : {
1729 9 : emit_error("Required address is missing.");
1730 3 : return;
1731 : }
1732 : // internal default if no address was defined
1733 : //
1734 323 : if(ipv6)
1735 : {
1736 203 : if(f_default_address6.empty())
1737 : {
1738 101 : address = "::";
1739 : }
1740 : else
1741 : {
1742 102 : address = f_default_address6;
1743 : }
1744 : }
1745 : else
1746 : {
1747 120 : if(f_default_address4.empty())
1748 : {
1749 15 : address = "0.0.0.0";
1750 : }
1751 : else
1752 : {
1753 105 : address = f_default_address4;
1754 : }
1755 : }
1756 : }
1757 :
1758 : // prepare hints for the the getaddrinfo() function
1759 : //
1760 132201 : addrinfo hints = {};
1761 132201 : hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
1762 132201 : hints.ai_family = AF_UNSPEC;
1763 :
1764 132201 : switch(f_protocol)
1765 : {
1766 66190 : case IPPROTO_TCP:
1767 66190 : hints.ai_socktype = SOCK_STREAM;
1768 66190 : hints.ai_protocol = IPPROTO_TCP;
1769 66190 : break;
1770 :
1771 65832 : case IPPROTO_UDP:
1772 65832 : hints.ai_socktype = SOCK_DGRAM;
1773 65832 : hints.ai_protocol = IPPROTO_UDP;
1774 65832 : break;
1775 :
1776 : }
1777 :
1778 : // convert address to binary
1779 : //
1780 132201 : if(get_allow(allow_t::ALLOW_ADDRESS_LOOKUP))
1781 : {
1782 132098 : addrinfo * addrlist(nullptr);
1783 : {
1784 132098 : errno = 0;
1785 132098 : char const * service(port_str.c_str());
1786 132098 : if(port_str.empty())
1787 : {
1788 192 : service = "0"; // fallback to port 0 when unspecified
1789 : }
1790 132098 : int const r(getaddrinfo(address.c_str(), service, &hints, &addrlist));
1791 132098 : if(r != 0)
1792 : {
1793 : // break on invalid addresses
1794 : //
1795 9 : int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
1796 18 : emit_error(
1797 : "Invalid address in \""
1798 18 : + address
1799 36 : + (port_str.empty() ? "" : ":")
1800 36 : + port_str
1801 36 : + "\" error "
1802 36 : + std::to_string(r)
1803 36 : + " -- "
1804 36 : + gai_strerror(r)
1805 45 : + (e == 0
1806 38 : ? ""
1807 : : " (errno: "
1808 16 : + std::to_string(e)
1809 23 : + " -- "
1810 23 : + strerror(e)
1811 : + ")."));
1812 9 : return;
1813 : }
1814 : }
1815 132089 : std::shared_ptr<addrinfo> ai(addrlist, addrinfo_deleter);
1816 :
1817 132089 : bool first(true);
1818 264332 : while(addrlist != nullptr)
1819 : {
1820 : // go through the addresses and create ranges and save that in the result
1821 : //
1822 132243 : if(addrlist->ai_family == AF_INET)
1823 : {
1824 66125 : if(addrlist->ai_addrlen != sizeof(sockaddr_in))
1825 : {
1826 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
1827 : + std::to_string(addrlist->ai_addrlen) // LCOV_EXCL_LINE
1828 : + ", expected" // LCOV_EXCL_LINE
1829 : + std::to_string(sizeof(sockaddr_in)) // LCOV_EXCL_LINE
1830 : + ")."); // LCOV_EXCL_LINE
1831 : }
1832 : else
1833 : {
1834 66125 : addr a(*reinterpret_cast<sockaddr_in *>(addrlist->ai_addr));
1835 66125 : a.set_hostname(address);
1836 : // in most cases we do not get a protocol from
1837 : // the getaddrinfo() function...
1838 66125 : if(addrlist->ai_protocol != -1)
1839 : {
1840 66125 : a.set_protocol(addrlist->ai_protocol);
1841 : }
1842 66125 : a.set_port_defined(defined_port);
1843 66125 : addr_range r;
1844 66125 : r.set_from(a);
1845 66125 : result.push_back(r);
1846 66125 : }
1847 : }
1848 66118 : else if(addrlist->ai_family == AF_INET6)
1849 : {
1850 66118 : if(addrlist->ai_addrlen != sizeof(sockaddr_in6))
1851 : {
1852 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
1853 : + std::to_string(addrlist->ai_addrlen) // LCOV_EXCL_LINE
1854 : + ", expected " // LCOV_EXCL_LINE
1855 : + std::to_string(sizeof(sockaddr_in6)) // LCOV_EXCL_LINE
1856 : + ")."); // LCOV_EXCL_LINE
1857 : }
1858 : else
1859 : {
1860 66118 : addr a(*reinterpret_cast<sockaddr_in6 *>(addrlist->ai_addr));
1861 66118 : a.set_hostname(address);
1862 66118 : if(addrlist->ai_protocol != -1)
1863 : {
1864 66118 : a.set_protocol(addrlist->ai_protocol);
1865 : }
1866 66118 : a.set_port_defined(defined_port);
1867 66118 : addr_range r;
1868 66118 : r.set_from(a);
1869 66118 : result.push_back(r);
1870 66118 : }
1871 : }
1872 : else if(first) // LCOV_EXCL_LINE
1873 : {
1874 : // ignore errors from other unsupported addresses
1875 : //
1876 : first = false; // LCOV_EXCL_LINE
1877 :
1878 : emit_error("Unsupported address family " // LCOV_EXCL_LINE
1879 : + std::to_string(addrlist->ai_family) // LCOV_EXCL_LINE
1880 : + "."); // LCOV_EXCL_LINE
1881 : }
1882 :
1883 132243 : addrlist = addrlist->ai_next;
1884 : }
1885 132089 : }
1886 : else
1887 : {
1888 103 : std::int64_t port(0);
1889 103 : if(get_allow(allow_t::ALLOW_REQUIRED_PORT)
1890 103 : || !port_str.empty())
1891 : {
1892 38 : bool const valid_port(advgetopt::validator_integer::convert_string(port_str, port));
1893 38 : if(!valid_port
1894 29 : || port < 0
1895 29 : || port > 65535)
1896 : {
1897 18 : emit_error("invalid port in \""
1898 18 : + port_str
1899 36 : + "\" (no service name lookup allowed).");
1900 9 : return;
1901 : }
1902 :
1903 29 : if(!get_allow(allow_t::ALLOW_PORT)
1904 29 : && !get_allow(allow_t::ALLOW_REQUIRED_PORT))
1905 : {
1906 : // TBD: this is probably a logic error because as far as I
1907 : // know it can only happen if the programmer defined
1908 : // a default port and also told the parser that no
1909 : // port is allowed
1910 : //
1911 2 : emit_error("Found a port (\""
1912 2 : + port_str
1913 4 : + "\") when it is not allowed.");
1914 1 : return;
1915 : }
1916 : }
1917 93 : sockaddr_in in;
1918 93 : if(inet_pton(AF_INET, address.c_str(), &in.sin_addr) == 1)
1919 : {
1920 52 : in.sin_family = AF_INET;
1921 52 : in.sin_port = htons(port);
1922 52 : memset(in.sin_zero, 0, sizeof(in.sin_zero)); // probably useless
1923 :
1924 52 : addr a(in);
1925 52 : a.set_hostname(address);
1926 52 : if(f_protocol != -1)
1927 : {
1928 1 : a.set_protocol(f_protocol);
1929 : }
1930 52 : a.set_port_defined(defined_port);
1931 52 : addr_range r;
1932 52 : r.set_from(a);
1933 52 : result.push_back(r);
1934 52 : }
1935 : else
1936 : {
1937 41 : sockaddr_in6 in6;
1938 41 : if(inet_pton(AF_INET6, address.c_str(), &in6.sin6_addr) == 1)
1939 : {
1940 35 : in6.sin6_family = AF_INET6;
1941 35 : in6.sin6_port = htons(port);
1942 35 : in6.sin6_flowinfo = 0;
1943 35 : in6.sin6_scope_id = 0;
1944 :
1945 35 : addr a(in6);
1946 35 : a.set_hostname(address);
1947 35 : if(f_protocol != -1)
1948 : {
1949 1 : a.set_protocol(f_protocol);
1950 : }
1951 35 : a.set_port_defined(defined_port);
1952 35 : addr_range r;
1953 35 : r.set_from(a);
1954 35 : result.push_back(r);
1955 35 : }
1956 : else
1957 : {
1958 12 : emit_error("Unknown address in \""
1959 12 : + address
1960 24 : + "\" (no DNS lookup was allowed).");
1961 : }
1962 : }
1963 : }
1964 : }
1965 :
1966 :
1967 : /** \brief Parse the address and port and ignore duplicates.
1968 : *
1969 : * The default system search finds one address per protocol. So if the
1970 : * address matches TCP, UDP, and other protocols, then we get one
1971 : * address for each protocol.
1972 : *
1973 : * This function removes those duplicates which are an issue in a list
1974 : * of IPs when working with ranges (i.e. 192.168.1.1-192.168.1.254 would
1975 : * fail because we find at least a TCP and a UDP protocol for those
1976 : * two addresses).
1977 : *
1978 : * \param[in] address The address to convert to binary.
1979 : * \param[in] port_str The port as a string.
1980 : * \param[out] result The range where we save the results.
1981 : * \param[in] ipv6 Use the default IPv6 address if the address is empty.
1982 : */
1983 98 : void addr_parser::parse_address_port_ignore_duplicates(
1984 : std::string address
1985 : , std::string port_str
1986 : , addr_range::vector_t & result
1987 : , bool ipv6)
1988 : {
1989 98 : parse_address_port(address, port_str, result, ipv6);
1990 98 : if(result.size() > 1)
1991 : {
1992 : // the following works only on a single set of addresses
1993 : // (i.e. if you called parse_address_port() multiple times,
1994 : // then it will not properly reduce all the equivalent IPs)
1995 : //
1996 : // note that since this is used for ranges, if we return multiple
1997 : // addresses (as in 192.168.1.1 and 192.168.3.1) then the range
1998 : // definition fails anyway, so the fact that we do not properly
1999 : // reduce the second set of addresses is not relevant here
2000 : //
2001 4 : addr first(result[0].get_from());
2002 12 : while(result.size() > 1)
2003 : {
2004 8 : addr next(result[1].get_from());
2005 8 : next.set_protocol(first.get_protocol());
2006 8 : if(first != next)
2007 : {
2008 : // I'm not sure of how to get two IPs in a locally running unit test...
2009 : break; // LCOV_EXCL_LINE
2010 : }
2011 8 : result.erase(result.begin() + 1);
2012 8 : }
2013 4 : }
2014 98 : }
2015 :
2016 :
2017 : /** \brief Parse a mask.
2018 : *
2019 : * If the input string is a decimal number, then use that as the
2020 : * number of bits to clear.
2021 : *
2022 : * If the mask is not just one decimal number, try to convert it
2023 : * as an address.
2024 : *
2025 : * If the string is neither a decimal number nor a valid IP address
2026 : * then the parser adds an error string to the f_error variable.
2027 : *
2028 : * The \p is_ipv4 flag is used to know whether the size of the CIDR is
2029 : * 0 to 128 or 0 to 32 and fix the mask accordingly.
2030 : *
2031 : * \bug
2032 : * Note that this flag* is bogus when the input is a domain name and lookup
2033 : * are allowed. This is because in this case the address may be IPv4 or
2034 : * IPv6, which means the mask would be bogus anyway. So I think we are fine.
2035 : * The bug comes from the user in this case.
2036 : *
2037 : * \param[in] mask The mask to transform to binary.
2038 : * \param[in,out] cidr The address to which the mask will be added.
2039 : * \param[in] is_ipv4 Whether the address was parsed as an IPv4 (true) or
2040 : * an IPv6 (false).
2041 : */
2042 590 : void addr_parser::parse_mask(
2043 : std::string const & mask
2044 : , addr & cidr
2045 : , bool const is_ipv4)
2046 : {
2047 : // no mask?
2048 : //
2049 590 : if(mask.empty())
2050 : {
2051 : // however, the algorithm below expects that 'mask' is not
2052 : // empty (otherwise we get the case of 0 even though it
2053 : // may not be correct.)
2054 : //
2055 41 : return;
2056 : }
2057 :
2058 : // the mask may be a decimal number or an address, if just one number
2059 : // then it's not an address, so test that first
2060 : //
2061 549 : std::uint8_t mask_bits[16] = { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 };
2062 :
2063 : // convert the mask to an integer, if possible
2064 : //
2065 549 : int mask_count(0);
2066 : {
2067 549 : std::string m(mask);
2068 1620 : for(char const * s(m.c_str()); *s != '\0'; ++s)
2069 : {
2070 1325 : if(*s >= '0' && *s <= '9')
2071 : {
2072 1086 : mask_count = mask_count * 10 + *s - '0';
2073 1086 : if(mask_count > 10000)
2074 : {
2075 30 : emit_error("Mask size too large ("
2076 30 : + mask
2077 60 : + ", expected a maximum of 128).");
2078 15 : return;
2079 : }
2080 : }
2081 : else
2082 : {
2083 239 : mask_count = -1;
2084 239 : break;
2085 : }
2086 : }
2087 549 : }
2088 :
2089 : // the conversion to an integer worked if mask_count != -1
2090 : //
2091 534 : if(mask_count != -1)
2092 : {
2093 295 : if(is_ipv4)
2094 : {
2095 52 : if(mask_count > 32)
2096 : {
2097 10 : emit_error("Unsupported mask size ("
2098 10 : + std::to_string(mask_count)
2099 20 : + ", expected 32 at the most for an IPv4).");
2100 5 : return;
2101 : }
2102 47 : mask_count = 32 - mask_count;
2103 : }
2104 : else
2105 : {
2106 243 : if(mask_count > 128)
2107 : {
2108 10 : emit_error("Unsupported mask size ("
2109 10 : + std::to_string(mask_count)
2110 20 : + ", expected 128 at the most for an IPv6).");
2111 5 : return;
2112 : }
2113 238 : mask_count = 128 - mask_count;
2114 : }
2115 :
2116 : // clear a few bits at the bottom of mask_bits
2117 : //
2118 285 : int idx(15);
2119 1934 : for(; mask_count > 8; mask_count -= 8, --idx)
2120 : {
2121 1649 : mask_bits[idx] = 0;
2122 : }
2123 285 : mask_bits[idx] = 255 << mask_count;
2124 : }
2125 : else //if(mask_count < 0)
2126 : {
2127 239 : if(!get_allow(allow_t::ALLOW_ADDRESS_MASK))
2128 : {
2129 102 : emit_error("Address like mask not allowed (/"
2130 102 : + mask
2131 204 : + "), try with a simple number instead.");
2132 51 : return;
2133 : }
2134 :
2135 : // prepare hints for the the getaddrinfo() function
2136 : //
2137 188 : addrinfo hints = {};
2138 188 : hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV | AI_ADDRCONFIG | AI_V4MAPPED;
2139 188 : hints.ai_family = AF_UNSPEC;
2140 :
2141 188 : switch(cidr.get_protocol())
2142 : {
2143 92 : case IPPROTO_TCP:
2144 92 : hints.ai_socktype = SOCK_STREAM;
2145 92 : hints.ai_protocol = IPPROTO_TCP;
2146 92 : break;
2147 :
2148 92 : case IPPROTO_UDP:
2149 92 : hints.ai_socktype = SOCK_DGRAM;
2150 92 : hints.ai_protocol = IPPROTO_UDP;
2151 92 : break;
2152 :
2153 : }
2154 :
2155 188 : std::string const port_str(std::to_string(cidr.get_port()));
2156 :
2157 : // if the mask is an IPv6, then it has to have the '[...]'
2158 188 : std::string m(mask);
2159 188 : if(is_ipv4)
2160 : {
2161 99 : if(mask[0] == '[')
2162 : {
2163 3 : emit_error("The address uses the IPv4 syntax, the mask cannot use IPv6.");
2164 1 : return;
2165 : }
2166 : }
2167 : else //if(!is_ipv4)
2168 : {
2169 89 : if(mask[0] != '[')
2170 : {
2171 3 : emit_error("The address uses the IPv6 syntax, the mask cannot use IPv4.");
2172 1 : return;
2173 : }
2174 88 : if(mask.back() != ']')
2175 : {
2176 1 : emit_error("The IPv6 mask is missing the ']' (" + mask + ").");
2177 1 : return;
2178 : }
2179 :
2180 : // note that we know that mask.length() >= 2 here since
2181 : // we at least have a '[' and ']'
2182 : //
2183 87 : m = mask.substr(1, mask.length() - 2);
2184 87 : if(m.empty())
2185 : {
2186 : // an empty mask is valid, it just means keep the default
2187 : // (getaddrinfo() fails on an empty string)
2188 : //
2189 1 : return;
2190 : }
2191 : }
2192 :
2193 : // if negative, we may have a full address here, so call the
2194 : // getaddrinfo() on this other string
2195 : //
2196 184 : addrinfo * masklist(nullptr);
2197 184 : errno = 0;
2198 184 : int const r(getaddrinfo(m.c_str(), port_str.c_str(), &hints, &masklist));
2199 184 : if(r != 0)
2200 : {
2201 : // break on invalid addresses
2202 : //
2203 15 : int const e(errno); // if r == EAI_SYSTEM, then 'errno' is consistent here
2204 30 : emit_error("Invalid mask in \"/"
2205 30 : + mask
2206 60 : + "\", error "
2207 60 : + std::to_string(r)
2208 60 : + " -- "
2209 60 : + gai_strerror(r)
2210 60 : + " (errno: "
2211 60 : + std::to_string(e)
2212 60 : + " -- "
2213 60 : + strerror(e)
2214 60 : + ").");
2215 15 : return;
2216 : }
2217 169 : std::shared_ptr<addrinfo> mask_ai(masklist, addrinfo_deleter);
2218 :
2219 169 : if(is_ipv4)
2220 : {
2221 88 : if(masklist->ai_family != AF_INET)
2222 : {
2223 : // this one happens when the user does not put the '[...]'
2224 : // around an IPv6 address
2225 : //
2226 3 : emit_error("Incompatible address between the address and"
2227 : " mask address (first was an IPv4 second an IPv6).");
2228 1 : return;
2229 : }
2230 87 : if(masklist->ai_addrlen != sizeof(sockaddr_in))
2231 : {
2232 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
2233 : + std::to_string(masklist->ai_addrlen) // LCOV_EXCL_LINE
2234 : + ", expected" // LCOV_EXCL_LINE
2235 : + std::to_string(sizeof(sockaddr_in)) // LCOV_EXCL_LINE
2236 : + ")."); // LCOV_EXCL_LINE
2237 : return; // LCOV_EXCL_LINE
2238 : }
2239 87 : memcpy(mask_bits + 12, &reinterpret_cast<sockaddr_in *>(masklist->ai_addr)->sin_addr.s_addr, 4); // last 4 bytes are the IPv4 address, keep the rest as 1s
2240 : }
2241 : else //if(!is_ipv4)
2242 : {
2243 81 : if(masklist->ai_family != AF_INET6)
2244 : {
2245 : // this one happens if the user puts the '[...]'
2246 : // around an IPv4 address
2247 : //
2248 3 : emit_error("Incompatible address between the address"
2249 : " and mask address (first was an IPv6 second an IPv4).");
2250 1 : return;
2251 : }
2252 80 : if(masklist->ai_addrlen != sizeof(sockaddr_in6))
2253 : {
2254 : emit_error("Unsupported address size (" // LCOV_EXCL_LINE
2255 : + std::to_string(masklist->ai_addrlen) // LCOV_EXCL_LINE
2256 : + ", expected " // LCOV_EXCL_LINE
2257 : + std::to_string(sizeof(sockaddr_in6)) // LCOV_EXCL_LINE
2258 : + ")."); // LCOV_EXCL_LINE
2259 : return; // LCOV_EXCL_LINE
2260 : }
2261 80 : memcpy(mask_bits, &reinterpret_cast<sockaddr_in6 *>(masklist->ai_addr)->sin6_addr.s6_addr, 16);
2262 : }
2263 : }
2264 :
2265 452 : cidr.set_mask(mask_bits);
2266 : }
2267 :
2268 :
2269 : /** \brief Transform a string into an `addr` object.
2270 : *
2271 : * This function converts the string \p a in an IP address saved in
2272 : * the returned addr object or throws an error if the conversion
2273 : * fails.
2274 : *
2275 : * The \p default_address parameter string can be set to an address
2276 : * which is returned if the input in \p a does not include an
2277 : * address such as in ":123".
2278 : *
2279 : * The \p port parameter can be specified or set to -1. If -1, then
2280 : * there is no default port. Either way, the port can be defined in
2281 : * \p a.
2282 : *
2283 : * The protocol can be specified, as a string. For example, you can
2284 : * use "tcp". The default is no specific protocol which means any
2285 : * type of IP address can be returned. Note that if more than one
2286 : * result is returned when the protocol was not specified, the
2287 : * results will be filtered to only keep the address that uses the
2288 : * TCP protocol. If as a result we have a single address, then that
2289 : * result gets returned.
2290 : *
2291 : * \note
2292 : * This function does not allow for address or port ranges. It is
2293 : * expected to return exactly one address. You can allow a \p mask
2294 : * by setting that parameter to true.
2295 : *
2296 : * \exception addr_invalid_argument
2297 : * If the parsed address is not returning a valid `addr` object, then
2298 : * this function fails by throwing an error. If you would prefer to
2299 : * handle the error mechanism, you want to create your own addr_parser
2300 : * and then call the addr_parser::parse() function. This will allow
2301 : * you to get error messages instead of an exception.
2302 : *
2303 : * \param[in] a The address string to be converted.
2304 : * \param[in] default_addrress The default address or an empty string.
2305 : * \param[in] default_port The default port or -1
2306 : * \param[in] protocol The protocol the address has to be of, or the
2307 : * empty string to allow any protocol.
2308 : * \param[in] mask Whether to allow a mask (true) or not (false).
2309 : *
2310 : * \return The address converted in an `addr` object.
2311 : *
2312 : * \sa addr_parser::parse()
2313 : */
2314 15 : addr string_to_addr(
2315 : std::string const & a
2316 : , std::string const & default_address
2317 : , int default_port
2318 : , std::string const & protocol
2319 : , bool mask)
2320 : {
2321 15 : addr_parser p;
2322 :
2323 15 : if(!default_address.empty())
2324 : {
2325 10 : p.set_default_address(default_address);
2326 : }
2327 :
2328 15 : if(default_port != -1)
2329 : {
2330 8 : p.set_default_port(default_port);
2331 : }
2332 :
2333 15 : if(!protocol.empty())
2334 : {
2335 7 : p.set_protocol(protocol);
2336 : }
2337 :
2338 15 : p.set_allow(allow_t::ALLOW_MASK, mask);
2339 :
2340 15 : addr_range::vector_t result(p.parse(a));
2341 :
2342 15 : if(result.size() != 1)
2343 : {
2344 : // when the protocol is not specified, this happens like all the
2345 : // time, we search for an entry with protocol TCP by default
2346 : // because in most cases that's what people want
2347 : //
2348 9 : if(protocol.empty())
2349 : {
2350 40 : result.erase(
2351 16 : std::remove_if(
2352 : result.begin()
2353 : , result.end()
2354 21 : , [](auto const it)
2355 : {
2356 21 : return it.has_from() && it.get_from().get_protocol() != IPPROTO_TCP;
2357 : })
2358 16 : , result.end());
2359 : }
2360 9 : if(result.size() != 1)
2361 : {
2362 : // an invalid protocol is caught by the set_protocol()
2363 : // function, but a totally invalid address or domain name
2364 : // will get us here with an empty list (result.empty() == true)
2365 : //
2366 : throw addr_invalid_argument(
2367 : "the address \""
2368 4 : + a
2369 8 : + "\" could not be converted to a single address in string_to_addr(), found "
2370 8 : + std::to_string(result.size())
2371 6 : + " entries instead.");
2372 : }
2373 : }
2374 :
2375 : // at the moment, we can only get a "from" so the following exceptions
2376 : // just cannot happen, which is why we have an LCOV_EXCL_LINE
2377 : //
2378 13 : if(result[0].has_to()
2379 13 : || result[0].is_range())
2380 : {
2381 : throw addr_invalid_argument("string_to_addr() does not support ranges."); // LCOV_EXCL_LINE
2382 : }
2383 :
2384 13 : if(!result[0].has_from())
2385 : {
2386 : throw addr_invalid_argument("string_to_addr() has no 'from' address."); // LCOV_EXCL_LINE
2387 : }
2388 :
2389 26 : return result[0].get_from();
2390 17 : }
2391 :
2392 :
2393 :
2394 : }
2395 : // namespace addr
2396 : // vim: ts=4 sw=4 et
|