Line data Source code
1 : // Copyright (c) 2012-2021 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 unix class.
27 : *
28 : * This file includes the implementation of the unix class. The one that
29 : * deals with low level Unix addresses.
30 : */
31 :
32 : // self
33 : //
34 : #include "libaddr/unix.h"
35 : #include "libaddr/addr_exception.h"
36 :
37 :
38 : // snapdev lib
39 : //
40 : #include <snapdev/join_strings.h>
41 : #include <snapdev/raii_generic_deleter.h>
42 : #include <snapdev/tokenize_string.h>
43 :
44 :
45 : // libutf8 library
46 : //
47 : #include <libutf8/libutf8.h>
48 :
49 :
50 : // C++ library
51 : //
52 : #include <iostream>
53 :
54 :
55 : // C library
56 : //
57 : #include <sys/stat.h>
58 :
59 :
60 : // last include
61 : //
62 : #include <snapdev/poison.h>
63 :
64 :
65 :
66 :
67 :
68 : namespace addr
69 : {
70 :
71 :
72 :
73 :
74 :
75 :
76 : /** \brief Create a unix object that represents an unnamed socket.
77 : *
78 : * The default is to create an unnamed address. Note that you can later
79 : * change an address with functions such as the set_abstract() function.
80 : */
81 89 : unix::unix()
82 : {
83 89 : }
84 :
85 :
86 : /** \brief Create a unix object from a binary Unix address structure.
87 : *
88 : * This function initializes this Unix object with the specified sockaddr_un
89 : * address.
90 : *
91 : * \param[in] in The Unix address.
92 : */
93 113 : unix::unix(sockaddr_un const & un)
94 : {
95 113 : set_un(un);
96 61 : }
97 :
98 :
99 : /** \brief Create an addr object from a binary IPv6 address.
100 : *
101 : * This function initializes this addr object with the specified IPv6
102 : * address. The is_ipv4() function will return false.
103 : *
104 : * \note
105 : * In this case, the \p address parameter is expected to be a bare
106 : * address. If you have a URI address, make sure to create the
107 : * unix address and then call the set_uri() function.
108 : *
109 : * \param[in] address A Unix address in a string.
110 : * \param[in] abstract Whether this address is considered abstract.
111 : */
112 332 : unix::unix(std::string const & address, bool abstract)
113 : {
114 332 : if(abstract)
115 : {
116 128 : set_abstract(address);
117 : }
118 204 : else if(!address.empty())
119 : {
120 203 : set_file(address);
121 : }
122 : //else -- this is the default so we don't need to call that function
123 : //{
124 : // make_unnamed();
125 : //}
126 244 : }
127 :
128 :
129 : /** \brief Save a Unix address in this unix object.
130 : *
131 : * This function saves the specified Unix address in this unix object.
132 : *
133 : * Any type of address can be saved. The function determines what type
134 : * this address represents (i.e. file, abstract, or unnamed).
135 : *
136 : * \warning
137 : * This function does not just copy the input address in this unix
138 : * object. It applies our detection mechanism to detect which type
139 : * of address you are supplying and then call one of the set_file(),
140 : * set_abstract(), or make_unnamed() functions. Therefore, the function
141 : * may throw because the address is not considered compatible (invalid
142 : * UTF-8, includes control characters, etc.) Also, in the case of an
143 : * abstract name, if it includes '\0' characters, then it won't work
144 : * as expected. These will not be detected properly.
145 : *
146 : * \todo
147 : * I think that to properly support the abstract name we should offer
148 : * a size input parameter so we know of the exact size of the address.
149 : *
150 : * \exception addr_invalid_argument
151 : * The input address must be of type AF_UNIX or AF_LOCAL.
152 : *
153 : * \param[in] in The Unix address to save in this unix object.
154 : */
155 168 : void unix::set_un(struct sockaddr_un const & un)
156 : {
157 168 : if(un.sun_family != AF_UNIX)
158 : {
159 100 : throw addr_invalid_structure("unix::set_un(): the input address does not represent a Unix address (family is not AF_UNIX).");
160 : }
161 :
162 68 : if(un.sun_path[0] != '\0')
163 : {
164 33 : if(strnlen(un.sun_path, sizeof(un.sun_path)) == sizeof(un.sun_path))
165 : {
166 2 : throw addr_invalid_argument("unix::set_un(): the input address filename is too long.");
167 : }
168 :
169 : // this is considered a file address
170 : // (we do not support a missing '\0' at the end of the name, but
171 : // the input could be missing it so we have to make sure by creating
172 : // a temp version of the name)
173 : //
174 31 : char temp[sizeof(un.sun_path) + 1];
175 31 : memcpy(temp, un.sun_path, sizeof(temp) - 1);
176 31 : temp[sizeof(temp) - 1] = '\0';
177 31 : set_file(un.sun_path);
178 : }
179 35 : else if(un.sun_path[1] != '\0')
180 : {
181 33 : std::size_t const len(strnlen(un.sun_path + 1, sizeof(un.sun_path) - 1));
182 33 : if(len == sizeof(un.sun_path) - 1)
183 : {
184 2 : throw addr_invalid_argument("unix::set_un(): the input abstract name is too long.");
185 : }
186 :
187 : // in case we are missing a '\0' at the end
188 : // (we are not compatible in that way too)
189 : //
190 : //char temp[sizeof(un.sun_path) + 1];
191 : //for(int idx(0); idx < sizeof(temp) - 1 && un.sun_path[idx + 1] != '\0'; ++idx)
192 : //{
193 : // temp[idx] = un.sun_path[idx + 1];
194 : //}
195 : ////memcpy(temp, un.sun_path + 1, sizeof(temp) - 1);
196 : //temp[sizeof(temp) - 1] = '\0';
197 31 : set_abstract(std::string(un.sun_path + 1, len));
198 : }
199 : else
200 : {
201 2 : make_unnamed();
202 : }
203 64 : }
204 :
205 :
206 : /** \brief Change this Unix address in an unnamed Unix address.
207 : *
208 : * Transform this Unix address in an unnamed Unix address.
209 : *
210 : * \note
211 : * Linux accepts an abstract address as having the first sun_path
212 : * character set to '\0' and all the other characters set to anything
213 : * including all '\0'. We handle our abstract addresses as not supporting
214 : * zeroes. Therefore, we do not 100% match Linux.
215 : */
216 97 : void unix::make_unnamed()
217 : {
218 97 : memset(f_address.sun_path, 0, sizeof(f_address.sun_path));
219 97 : }
220 :
221 :
222 : /** \brief Set the this Unix address to the specified filename.
223 : *
224 : * This function changes address to the specified filename.
225 : *
226 : * The path does not need to be a full path. However, relative paths are
227 : * often considered dangerous.
228 : *
229 : * \exception addr_invalid_argument
230 : * If the input doesn't look like a valid filename, then the function
231 : * raises this exception.
232 : *
233 : * \param[in] file The path to a file.
234 : */
235 332 : void unix::set_file(std::string const & file)
236 : {
237 576 : std::string const address(verify_path(file, false));
238 :
239 244 : memcpy(f_address.sun_path, address.c_str(), address.length());
240 244 : if(address.length() < sizeof(f_address.sun_path))
241 : {
242 : // clear the rest
243 : //
244 488 : memset(
245 244 : f_address.sun_path + address.length()
246 : , 0
247 244 : , sizeof(f_address.sun_path) - address.length());
248 : }
249 244 : }
250 :
251 :
252 : /** \brief Set the address to an Abstract Unix Address.
253 : *
254 : * This function saves the specified \p abstract path as an Abstract Unix
255 : * Address in this object.
256 : *
257 : * Note that we check the \p abstract path as if it were a file path. We
258 : * forbid control characters, and it has to be at least one character and
259 : * at most 106 characters.
260 : *
261 : * The function generates an abstract path. That means the file won't be
262 : * created on disk. You should choose a path which is not going to conflict
263 : * with any other system. It is conventional, in the dbus area, to make use
264 : * of your domain name in reverse order (Java-like). For example:
265 : *
266 : * \code
267 : * u.set_abstract("/net/snapwebsite/settings");
268 : * \endcode
269 : *
270 : * \note
271 : * The Linux OS supports abstract paths which include any byte. However,
272 : * there are tools to look at those paths as strings, so limiting these
273 : * path to valid strings is something we think makes sense. It also
274 : * makes it possible for us to distinguish between unnamed and abstract
275 : * addresses without the need for another parameter such as an address
276 : * size.
277 : *
278 : * \exception addr_invalid_argument
279 : * If the input \p abstract path is not considered valid, then this
280 : * exception is raised.
281 : *
282 : * \param[in] abstract The abstract path to save in this Unix address.
283 : */
284 254 : void unix::set_abstract(std::string const & abstract)
285 : {
286 485 : std::string const address(verify_path(abstract, true));
287 :
288 231 : f_address.sun_path[0] = '\0';
289 231 : strncpy(f_address.sun_path + 1, address.c_str(), sizeof(f_address.sun_path) - 1);
290 231 : if(address.length() < sizeof(f_address.sun_path) - 2)
291 : {
292 : // clear the rest
293 : //
294 462 : memset(
295 231 : f_address.sun_path + address.length() + 2
296 : , 0
297 231 : , sizeof(f_address.sun_path) - address.length() - 2);
298 : }
299 231 : }
300 :
301 :
302 : /** \brief Allow for URI like notation to set a Unix address.
303 : *
304 : * Since we often use a preference setting which is a string to define our
305 : * addresses, having a notation that clearly distinguishes between the
306 : * Unix address types seems to make sense.
307 : *
308 : * We support the following basic URI syntax:
309 : *
310 : * \code
311 : * <type>:[<path>][?<mode>]
312 : * \endcode
313 : *
314 : * Where `\<type>` has to be one of:
315 : *
316 : * * `unix:` -- this is the only scheme we currently support
317 : *
318 : * Where `\<path>` is the path to the file or abstract filename. For unnamed
319 : * sockets, it must be an empty string. If it starts with more than one
320 : * slash, extraneous slashes are removed (actually, any extraneous slashes
321 : * within the name are removed).
322 : *
323 : * Where `\<mode>` must be one of:
324 : *
325 : * * `?unnamed` -- create an unnamed address, the `\<path>` must be empty
326 : * * `?file` -- create a file based address, the `\<path>` cannot be empty
327 : * * `?abstract` -- create an abstract address
328 : *
329 : * By default, when the `\<mode>` is not specified, the function creates
330 : * a file based address unless the address is empty in which case it
331 : * creates an unnamed address. The only way to create an abstract name
332 : * is by specifying the `?abstract` query string.
333 : *
334 : * \note
335 : * We do not use/support the `file:///` scheme because this is a reference
336 : * to a specific file, not a socket. We think that `unix:` is better adapted
337 : * to our situation.
338 : *
339 : * \warning
340 : * At the moment the parsing of the string is very basic. You must make sure
341 : * not to include anything more than what is allowed as presented above.
342 : *
343 : * \todo
344 : * Use the snap_uri once we extracted that in its own library.
345 : *
346 : * \exception addr_invalid_argument
347 : * If the URI can't be properly parsed, this function raises this exception.
348 : *
349 : * \param[in] uri The URI to save in this address.
350 : */
351 252 : void unix::set_uri(std::string const & uri)
352 : {
353 : enum class uri_force_t
354 : {
355 : URI_FORCE_NONE,
356 : URI_FORCE_UNNAMED,
357 : URI_FORCE_FILE,
358 : URI_FORCE_ABSTRACT,
359 : };
360 :
361 252 : std::string::size_type const scheme_pos(uri.find(':'));
362 252 : if(scheme_pos == std::string::npos)
363 : {
364 1 : throw addr_invalid_argument("invalid URI for a Unix address, scheme not found (':' missing)");
365 : }
366 :
367 502 : std::string const scheme(uri.substr(0, scheme_pos));
368 :
369 502 : std::string address;
370 502 : std::string query;
371 :
372 251 : std::string::size_type query_pos(uri.find('?', scheme_pos + 1));
373 251 : if(query_pos == std::string::npos)
374 : {
375 88 : address = uri.substr(scheme_pos + 1);
376 : }
377 : else
378 : {
379 163 : address = uri.substr(scheme_pos + 1, query_pos - scheme_pos - 1);
380 163 : query = uri.substr(query_pos + 1);
381 : }
382 :
383 251 : uri_force_t force(uri_force_t::URI_FORCE_NONE);
384 251 : if(!query.empty())
385 : {
386 163 : if(query == "unnamed")
387 : {
388 39 : force = uri_force_t::URI_FORCE_UNNAMED;
389 : }
390 124 : else if(query == "file")
391 : {
392 37 : force = uri_force_t::URI_FORCE_FILE;
393 : }
394 87 : else if(query == "abstract")
395 : {
396 86 : force = uri_force_t::URI_FORCE_ABSTRACT;
397 : }
398 : else
399 : {
400 : throw addr_invalid_argument(
401 : "\""
402 2 : + query
403 3 : + "\" is not a supported URI query string for a Unix address;"
404 3 : " supported query strings are one of: \"unnamed\", \"file\" and \"abstract\".");
405 : }
406 : }
407 :
408 250 : if(scheme != "unix")
409 : {
410 : throw addr_invalid_argument(
411 : "\""
412 2 : + scheme
413 3 : + "\" is not a supported URI scheme for a Unix address;"
414 3 : " supported scheme are: \"stream\", \"dgram\" and \"seqpacket\".");
415 : }
416 :
417 249 : switch(force)
418 : {
419 88 : case uri_force_t::URI_FORCE_NONE:
420 88 : if(address.empty())
421 : {
422 37 : make_unnamed();
423 : }
424 : else
425 : {
426 51 : set_file(address);
427 : }
428 76 : break;
429 :
430 39 : case uri_force_t::URI_FORCE_UNNAMED:
431 39 : if(!address.empty())
432 : {
433 : throw addr_invalid_argument(
434 : "address \""
435 2 : + address
436 3 : + "\" must be empty to represent an unnamed Unix address.");
437 : }
438 38 : make_unnamed();
439 38 : break;
440 :
441 37 : case uri_force_t::URI_FORCE_FILE:
442 37 : set_file(address);
443 37 : break;
444 :
445 85 : case uri_force_t::URI_FORCE_ABSTRACT:
446 85 : set_abstract(address);
447 74 : break;
448 :
449 : }
450 225 : }
451 :
452 :
453 : /** \brief Define this address given an open socket.
454 : *
455 : * This function tries to retrieve the address of a socket. If the function
456 : * succeeds, then it returns true meaning that the address is considered
457 : * valid.
458 : *
459 : * The function verifies that the address is indeed a Unix address. If not,
460 : * then it fails with EADDRNOTAVAIL and this object is not modified.
461 : *
462 : * \warning
463 : * The function makes sure that the name of the socket fits a
464 : * sockaddr_un.sun_path as per our rules (i.e. we force the presence
465 : * of the '\0' terminator). So this function may throw an exception
466 : * on Linux which supports socket names without the '\0'. Further,
467 : * if the name is not considered compatible with our verify_path()
468 : * function.
469 : *
470 : * \return true if the address was successfully retrieved.
471 : */
472 5 : bool unix::set_from_socket(int s)
473 : {
474 : // WARNING: the sockaddr (or sockaddr_storage) structure that the
475 : // getsockname() expects is not large enough for a Unix
476 : // socket so we use a sockaddr_un instead
477 : //
478 5 : sockaddr_un address;
479 5 : socklen_t length(sizeof(address));
480 5 : if(getsockname(s, reinterpret_cast<sockaddr *>(&address), &length) != 0)
481 : {
482 1 : return false;
483 : }
484 :
485 4 : if(address.sun_family != AF_UNIX)
486 : {
487 1 : errno = EADDRNOTAVAIL;
488 1 : return false;
489 : }
490 :
491 3 : if(length < sizeof(address))
492 : {
493 3 : memset(
494 : reinterpret_cast<char *>(&address) + length
495 : , 0
496 : , sizeof(address) - length);
497 : }
498 :
499 3 : set_un(address);
500 :
501 3 : return true;
502 : }
503 :
504 :
505 : /** \brief Check whether this address represents file based Unix address.
506 : *
507 : * The function checks whether the path starts with a character other
508 : * than '\0'.
509 : *
510 : * \return true if this addr represents a file based Unix address.
511 : */
512 1042 : bool unix::is_file() const
513 : {
514 1042 : return f_address.sun_path[0] != '\0';
515 : }
516 :
517 :
518 : /** \brief Check whether this address represents an abstract Unix address.
519 : *
520 : * This function checks whether this Unix address represet an abstract
521 : * Unix address.
522 : *
523 : * An abstract Unix address has the first byte of the path set to '\0'
524 : * and the second not set to '\0'.
525 : *
526 : * \return true if this address represents an abstract Unix address.
527 : */
528 1120 : bool unix::is_abstract() const
529 : {
530 1120 : return f_address.sun_path[0] == '\0'
531 1120 : && f_address.sun_path[1] != '\0';
532 : }
533 :
534 :
535 : /** \brief Check whether this address represents an unnamed Unix address.
536 : *
537 : * This function checks whether the path represents and unnamed Unix address.
538 : *
539 : * An unnamed Unix address has all the bytes of the `sun_path` string set
540 : * to '\0', but we really only need to test the first two.
541 : *
542 : * \return true if the address represents an unnamed Unix address.
543 : */
544 1120 : bool unix::is_unnamed() const
545 : {
546 1120 : return f_address.sun_path[0] == '\0'
547 1120 : && f_address.sun_path[1] == '\0';
548 : }
549 :
550 :
551 : /** \brief Retrieve a copy of this Unix address.
552 : *
553 : * This function returns the Unix address as currently defined in this
554 : * unix object.
555 : *
556 : * The address is distinguished between an unnamed address and an
557 : * abstract name by the fact that `sun_path[1] != '\0'` for the
558 : * abstract path.
559 : *
560 : * \param[out] un The structure where the address gets saved.
561 : */
562 562 : void unix::get_un(sockaddr_un & un) const
563 : {
564 562 : memcpy(&un, &f_address, sizeof(un));
565 562 : }
566 :
567 :
568 : /** \brief Return the path of this Unix address.
569 : *
570 : * This function returns the path of the Unix address. Note that you can know
571 : * if it was an unnamed address, the string will be empty. However, you can't
572 : * distinguish between a file or abstract name with this function. You can
573 : * always use the is_file() and is_abstract() to know.
574 : *
575 : * \return the path of this Unix address.
576 : */
577 560 : std::string unix::to_string() const
578 : {
579 560 : if(is_abstract())
580 : {
581 230 : return f_address.sun_path + 1;
582 : }
583 :
584 330 : return f_address.sun_path;
585 : }
586 :
587 :
588 : /** \brief Get the network type string
589 : *
590 : * Translate the network type into a string, which can be really useful
591 : * to log that information.
592 : *
593 : * Note that PUBLIC is the same as UNKNOWN, this function returns
594 : * "Unknown" in that case, though.
595 : *
596 : * \return The string representing the type of network.
597 : */
598 560 : std::string unix::to_uri() const
599 : {
600 560 : std::string result;
601 560 : result.reserve(125);
602 :
603 560 : result = "unix:";
604 :
605 560 : if(!is_unnamed())
606 : {
607 461 : char const * path(nullptr);
608 922 : std::string query;
609 461 : if(is_file())
610 : {
611 231 : path = f_address.sun_path;
612 : }
613 : else
614 : {
615 230 : path = f_address.sun_path + 1;
616 230 : query = "?abstract";
617 : }
618 :
619 461 : if(path[0] == '/')
620 : {
621 179 : result += "//";
622 : }
623 461 : result += path;
624 461 : result += query;
625 : }
626 :
627 560 : return result;
628 : }
629 :
630 :
631 : /** \brief Delete the socket file.
632 : *
633 : * This function will delete the socket file if it exists.
634 : *
635 : * The function does nothing if the address is not representing a file.
636 : * In that case, the function returns 0 and does not modify the errno
637 : * variable.
638 : *
639 : * \note
640 : * This function does not verify whether the file is in use. That means
641 : * you may delete a file that should not be deleted. It is your
642 : * responsibility to verify the current state of the socket. The
643 : * ed::local_stream_server_connection implementation takes care of
644 : * that for you if you want to have it easy.
645 : *
646 : * \return 0 if the unlink worked, -1 on error and errno is set.
647 : */
648 21 : int unix::unlink()
649 : {
650 21 : if(is_file())
651 : {
652 10 : return ::unlink(f_address.sun_path);
653 : }
654 :
655 11 : return 0;
656 : }
657 :
658 :
659 : /** \brief Check whether two addresses are equal.
660 : *
661 : * This function compares the left hand side (this) and the right
662 : * hand side (rhs) for equality. If both represent the same IP
663 : * address, then the function returns true.
664 : *
665 : * \warning
666 : * The function only compares the address itself. The family, port,
667 : * flow info, scope identifier, protocol are all ignored.
668 : *
669 : * \param[in] rhs The other Unix address to compare against.
670 : *
671 : * \return true if \p this is equal to \p rhs.
672 : */
673 6 : bool unix::operator == (unix const & rhs) const
674 : {
675 6 : return f_address == rhs.f_address;
676 : }
677 :
678 :
679 : /** \brief Check whether two addresses are not equal.
680 : *
681 : * This function compares the left hand side (this) and the right
682 : * hand side (rhs) for inequality. If both represent the same IP
683 : * address, then the function returns false.
684 : *
685 : * \warning
686 : * The function only compares the address itself. The family, port,
687 : * flow info, scope identifier, protocol are all ignored.
688 : *
689 : * \param[in] rhs The other Unix address to compare against.
690 : *
691 : * \return true if \p this is not equal to \p rhs.
692 : */
693 3 : bool unix::operator != (unix const & rhs) const
694 : {
695 3 : return f_address != rhs.f_address;
696 : }
697 :
698 :
699 : /** \brief Compare two addresses to know which one is smaller.
700 : *
701 : * This function compares the left hand side (this) and the right
702 : * hand side (rhs) to know which one is the smallest. If both
703 : * are equal or the left hand side is larger than the right hand
704 : * side, then it returns false, otherwise it returns true.
705 : *
706 : * \warning
707 : * The function only compares the address itself. The family, port,
708 : * flow info, scope identifier, protocol are all ignored.
709 : *
710 : * \param[in] rhs The other Unix address to compare against.
711 : *
712 : * \return true if \p this is smaller than \p rhs.
713 : */
714 3 : bool unix::operator < (unix const & rhs) const
715 : {
716 3 : return f_address < rhs.f_address;
717 : }
718 :
719 :
720 : /** \brief Compare two addresses to know which one is smaller or equal.
721 : *
722 : * This function compares the left hand side (this) and the right
723 : * hand side (rhs) to know whether the left hand side is smaller or
724 : * equal to thr right handside.
725 : *
726 : * \warning
727 : * The function only compares the address itself. The family, port,
728 : * flow info, scope identifier, protocol are all ignored.
729 : *
730 : * \param[in] rhs The other Unix address to compare against.
731 : *
732 : * \return true if \p this is smaller than \p rhs.
733 : */
734 3 : bool unix::operator <= (unix const & rhs) const
735 : {
736 3 : return f_address <= rhs.f_address;
737 : }
738 :
739 :
740 : /** \brief Compare two addresses to know which one is smaller.
741 : *
742 : * This function compares the left hand side (this) and the right
743 : * hand side (rhs) to know which one is the smallest. If both
744 : * are equal or the left hand side is larger than the right hand
745 : * side, then it returns false, otherwise it returns true.
746 : *
747 : * \warning
748 : * The function only compares the address itself. The family, port,
749 : * flow info, scope identifier, protocol are all ignored.
750 : *
751 : * \param[in] rhs The other Unix address to compare against.
752 : *
753 : * \return true if \p this is smaller than \p rhs.
754 : */
755 3 : bool unix::operator > (unix const & rhs) const
756 : {
757 3 : return f_address > rhs.f_address;
758 : }
759 :
760 :
761 : /** \brief Compare two addresses to know which one is smaller.
762 : *
763 : * This function compares the left hand side (this) and the right
764 : * hand side (rhs) to know which one is the smallest. If both
765 : * are equal or the left hand side is larger than the right hand
766 : * side, then it returns false, otherwise it returns true.
767 : *
768 : * \warning
769 : * The function only compares the address itself. The family, port,
770 : * flow info, scope identifier, protocol are all ignored.
771 : *
772 : * \param[in] rhs The other Unix address to compare against.
773 : *
774 : * \return true if \p this is smaller than \p rhs.
775 : */
776 3 : bool unix::operator >= (unix const & rhs) const
777 : {
778 3 : return f_address >= rhs.f_address;
779 : }
780 :
781 :
782 : /** \brief Check whether the specified \p path is a valid path.
783 : *
784 : * Although there are even less restriction on the path of an abstract
785 : * socket, we force you to have a valid UTF-8 string without any control
786 : * characters (especially not a '\0' character).
787 : *
788 : * \exception addr_invalid_argument
789 : * If \p path is empty or any invalid character is found within the string,
790 : * then this exception is raised.
791 : *
792 : * \exception libutf8::libutf8_exception_decoding
793 : * If an invalid UTF-8 character is found, then this exception is raised.
794 : *
795 : * \param[in] path The path to be validated.
796 : * \param[in] abstract Whether this is an abstract path (true) or not.
797 : *
798 : * \return A canonicalized version of \p path.
799 : */
800 586 : std::string unix::verify_path(std::string const & path, bool abstract)
801 : {
802 586 : if(path.empty())
803 : {
804 : throw addr_invalid_argument(
805 2 : std::string(abstract ? "an abstract" : "a Unix")
806 3 : + " filename can't be empty;"
807 3 : " use make_empty() if you want to use an unnamed socket.");
808 : }
809 :
810 585 : std::size_t const max_length(abstract
811 585 : ? sizeof(f_address.sun_path) - 1
812 : : sizeof(f_address.sun_path));
813 :
814 1170 : std::vector<std::string> segments;
815 585 : snapdev::tokenize_string(segments, path, "/", true);
816 585 : std::string p(snapdev::join_strings(segments, "/"));
817 585 : if(path[0] == '/')
818 : {
819 181 : p.insert(0, 1, '/');
820 : }
821 :
822 585 : if(p.length() >= max_length)
823 : {
824 : throw addr_invalid_argument(
825 88 : std::string(abstract ? "an abstract" : "a Unix")
826 132 : + " filename is limited to "
827 176 : + std::to_string(max_length)
828 132 : + " characters.");
829 : }
830 :
831 1081 : std::u32string u32(libutf8::to_u32string(p));
832 21154 : for(auto const & c : u32)
833 : {
834 20678 : if(c <= 0x1F // controls
835 20647 : || (c >= 0x7F && c <= 0x9F)) // graphic controls
836 : {
837 : throw addr_invalid_argument(
838 : "path \""
839 128 : + path
840 192 : + "\" is not a valid UTF-8 string (it includes controls).");
841 : }
842 : }
843 :
844 476 : if(p == "/")
845 : {
846 : throw addr_invalid_argument(
847 1 : "the root path (\"/\") is not a valid socket filename.");
848 : }
849 :
850 950 : return p;
851 : }
852 :
853 :
854 :
855 6 : }
856 : // namespace addr
857 : // vim: ts=4 sw=4 et
|