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