address_formatter.py 3.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. # Copyright (C) 2013 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. import socket
  16. class AddressFormatter:
  17. """
  18. A utility class to convert an IP address with a port number to a
  19. string.
  20. It takes a tuple (or list) containing and address string and a port
  21. number, and optionally a family.
  22. If the family is IPv4, the __str__ output will be
  23. <address>:<port>
  24. If the family is IPv6, the __str__ output will be
  25. [<address>]:<port>
  26. If family is not given, the __str__ method will try to figure it out
  27. itself, by checking for the ':' character in the address string.
  28. This class is designed to delay the conversion until it's explicitly
  29. requested, so the conversion doesn't happen if the corresponding log
  30. message is suppressed because of its log level (which is often the case
  31. for debug messages).
  32. Note: this optimization comes with the cost of instantiating the
  33. formatter object itself. It's not really clear which overhead is
  34. heavier, and we may conclude it's actually better to just generate
  35. the strings unconditionally. Alternatively, we can make the stored
  36. address of this object replaceable so that this object can be reused.
  37. Right now this is an open issue.
  38. See also ClientFormatter in the ddns.logger code, which does something
  39. similar but based on other criteria (and optional extra value).
  40. """
  41. def __init__(self, addr, family=None):
  42. self.__addr = addr
  43. self.__family = family
  44. def __addr_v4(self):
  45. return self.__addr[0] + ':' + str(self.__addr[1])
  46. def __addr_v6(self):
  47. return '[' + self.__addr[0] + ']:' + str(self.__addr[1])
  48. def __format_addr(self):
  49. # Some basic sanity checks, should we leave this out for efficiency?
  50. # (especially strings produce unexpected results)
  51. if isinstance(self.__addr, str) or\
  52. not hasattr(self.__addr, "__getitem__"):
  53. raise ValueError("Address must be a list or tuple")
  54. if self.__family is not None:
  55. if self.__family == socket.AF_INET6:
  56. return self.__addr_v6()
  57. elif self.__family == socket.AF_INET:
  58. return self.__addr_v4()
  59. else:
  60. raise ValueError("Unknown address family: " +
  61. str(self.__family))
  62. else:
  63. if self.__addr[0].find(':') >= 0:
  64. return self.__addr_v6()
  65. else:
  66. return self.__addr_v4()
  67. def __str__(self):
  68. return self.__format_addr()