ip.py 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. from re import compile
  4. from os import name as os_name, popen
  5. from socket import socket, getaddrinfo, gethostname, AF_INET, AF_INET6, SOCK_DGRAM
  6. from logging import debug, error
  7. from .util.http import request
  8. # 模块级别的SSL验证配置,默认使用auto模式
  9. ssl_verify = "auto"
  10. # IPV4正则
  11. IPV4_REG = r"((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])"
  12. # IPV6正则
  13. # https://community.helpsystems.com/forums/intermapper/miscellaneous-topics/5acc4fcf-fa83-e511-80cf-0050568460e4
  14. IPV6_REG = r"((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))" # noqa: E501
  15. # 公网IPv4 API列表,按优先级排序
  16. PUBLIC_IPV4_APIS = [
  17. "https://api.ipify.cn",
  18. "https://api.ipify.org",
  19. "https://4.ipw.cn/",
  20. "https://ipinfo.io/ip",
  21. "https://api-ipv4.ip.sb/ip",
  22. "http://checkip.amazonaws.com",
  23. ]
  24. # 公网IPv6 API列表,按优先级排序
  25. PUBLIC_IPV6_APIS = [
  26. "https://api6.ipify.org/",
  27. "https://6.ipw.cn/",
  28. "https://api-ipv6.ip.sb/ip",
  29. "http://ipv6.icanhazip.com",
  30. ]
  31. def default_v4(): # 默认连接外网的ipv4
  32. s = socket(AF_INET, SOCK_DGRAM)
  33. s.connect(("1.1.1.1", 53))
  34. ip = s.getsockname()[0]
  35. s.close()
  36. return ip
  37. def default_v6(): # 默认连接外网的ipv6
  38. s = socket(AF_INET6, SOCK_DGRAM)
  39. s.connect(("1:1:1:1:1:1:1:1", 8))
  40. ip = s.getsockname()[0]
  41. s.close()
  42. return ip
  43. def local_v6(i=0): # 本地ipv6地址
  44. info = getaddrinfo(gethostname(), 0, AF_INET6)
  45. debug(info)
  46. return info[int(i)][4][0]
  47. def local_v4(i=0): # 本地ipv4地址
  48. info = getaddrinfo(gethostname(), 0, AF_INET)
  49. debug(info)
  50. return info[int(i)][-1][0]
  51. def _open(url, reg):
  52. try:
  53. debug("open: %s", url)
  54. # IP 模块重试3次
  55. response = request("GET", url, verify=ssl_verify, retries=2)
  56. res = response.body
  57. debug("response: %s", res)
  58. match = compile(reg).search(res)
  59. if match:
  60. return match.group()
  61. error("No match found in response: %s", res)
  62. except Exception as e:
  63. error(e)
  64. def _try_multiple_apis(api_list, reg, ip_type):
  65. """
  66. Try multiple API endpoints until one succeeds
  67. """
  68. for url in api_list:
  69. try:
  70. debug("Trying %s API: %s", ip_type, url)
  71. result = _open(url, reg)
  72. if result:
  73. debug("Successfully got %s from %s: %s", ip_type, url, result)
  74. return result
  75. else:
  76. debug("No valid IP found from %s", url)
  77. except Exception as e:
  78. debug("Failed to get %s from %s: %s", ip_type, url, e)
  79. return None
  80. def public_v4(url=None, reg=IPV4_REG): # 公网IPV4地址
  81. if url:
  82. # 使用指定URL
  83. return _open(url, reg)
  84. else:
  85. # 使用多个API自动重试
  86. return _try_multiple_apis(PUBLIC_IPV4_APIS, reg, "IPv4")
  87. def public_v6(url=None, reg=IPV6_REG): # 公网IPV6地址
  88. if url:
  89. # 使用指定URL
  90. return _open(url, reg)
  91. else:
  92. # 使用多个API自动重试
  93. return _try_multiple_apis(PUBLIC_IPV6_APIS, reg, "IPv6")
  94. def _ip_regex_match(parrent_regex, match_regex):
  95. ip_pattern = compile(parrent_regex)
  96. matcher = compile(match_regex)
  97. if os_name == "nt": # windows:
  98. cmd = "ipconfig"
  99. else:
  100. cmd = "ip address || ifconfig 2>/dev/null"
  101. for s in popen(cmd).readlines():
  102. addr = ip_pattern.search(s)
  103. if addr and matcher.match(addr.group(1)):
  104. return addr.group(1)
  105. def regex_v4(reg): # ipv4 正则提取
  106. if os_name == "nt": # Windows: IPv4 xxx: 192.168.1.2
  107. regex_str = r"IPv4 .*: ((?:\d{1,3}\.){3}\d{1,3})\W"
  108. else:
  109. regex_str = r"inet (?:addr\:)?((?:\d{1,3}\.){3}\d{1,3})[\s/]"
  110. return _ip_regex_match(regex_str, reg)
  111. def regex_v6(reg): # ipv6 正则提取
  112. if os_name == "nt": # Windows: IPv4 xxx: ::1
  113. regex_str = r"IPv6 .*: ([\:\dabcdef]*)?\W"
  114. else:
  115. regex_str = r"inet6 (?:addr\:\s*)?([\:\dabcdef]*)?[\s/%]"
  116. return _ip_regex_match(regex_str, reg)