database.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. # -*- coding: utf-8 -*-
  2. """
  3. :copyright: ©2018 by IPIP.net
  4. """
  5. import ipaddress
  6. import json
  7. import sys
  8. from .util import bytes2long
  9. from .exceptions import NoSupportIPv4Error, NoSupportIPv6Error, NoSupportLanguageError, DatabaseError, IPNotFound
  10. class MetaData(object):
  11. def __init__(self, **kwargs):
  12. self.fields = kwargs['fields']
  13. self.node_count = kwargs['node_count']
  14. self.total_size = kwargs['total_size']
  15. self.build = kwargs['build']
  16. self.languages = kwargs['languages']
  17. self.ip_version = kwargs['ip_version']
  18. class Reader:
  19. _meta = {}
  20. data = b""
  21. _file_size = 0
  22. _v4offset = 0
  23. _v6offsetCache = {}
  24. def __init__(self, name):
  25. file = open(name, "rb")
  26. self.data = file.read()
  27. self._file_size = len(self.data)
  28. file.close()
  29. meta_length = bytes2long(self.data[0], self.data[1], self.data[2], self.data[3])
  30. if sys.version_info < (3,0):
  31. meta = json.loads(str(self.data[4:meta_length + 4]))
  32. else:
  33. meta = json.loads(str(self.data[4:meta_length + 4], 'utf-8'))
  34. self._meta = MetaData(**meta)
  35. if len(self._meta.languages) == 0 or len(self._meta.fields) == 0:
  36. raise DatabaseError("database meta error")
  37. if self._file_size != (4 + meta_length + self._meta.total_size):
  38. raise DatabaseError("database size error")
  39. self.data = self.data[4+meta_length:]
  40. def _read_node(self, node, idx):
  41. off = idx * 4 + node * 8
  42. return bytes2long(self.data[off], self.data[off + 1], self.data[off + 2], self.data[off + 3])
  43. def _find_node(self, ip):
  44. if ip.version == 6:
  45. bit_count = 128
  46. else:
  47. bit_count = 32
  48. idx = 0
  49. node = 0
  50. key = ip.packed[0:2]
  51. if bit_count == 32:
  52. if self._v4offset == 0:
  53. i = 0
  54. while i < 96:
  55. if i >= 80:
  56. node = self._read_node(node, 1)
  57. else:
  58. node = self._read_node(node, 0)
  59. i += 1
  60. self._v4offset = node
  61. else:
  62. node = self._v4offset
  63. else:
  64. val = self._v6offsetCache.get(key, -1)
  65. if val > -1:
  66. idx = 16
  67. node = val
  68. packed = bytearray(ip.packed)
  69. while idx < bit_count:
  70. if node > self._meta.node_count:
  71. break
  72. node = self._read_node(node, (1 & (packed[idx >> 3] >> 7 - (idx % 8))))
  73. idx += 1
  74. if idx == 16:
  75. self._v6offsetCache[key] = node
  76. if node > self._meta.node_count:
  77. return node
  78. raise IPNotFound("ip not found")
  79. def _resolve(self, node):
  80. resolved = node - self._meta.node_count + self._meta.node_count * 8
  81. size = bytes2long(0, 0, self.data[resolved], self.data[resolved + 1])
  82. if (resolved+2+size) > len(self.data):
  83. raise DatabaseError("database is error")
  84. return self.data[resolved+2:resolved+2+size]
  85. def find(self, addr, language):
  86. off = self._meta.languages.get(language)
  87. if off is None:
  88. raise NoSupportLanguageError(language + " is not support")
  89. ipv = ipaddress.ip_address(addr)
  90. if ipv.version == 6:
  91. if self.is_support_ipv6() is False:
  92. raise NoSupportIPv6Error("database is not support ipv6")
  93. elif ipv.version == 4:
  94. if self.is_support_ipv4() is False:
  95. raise NoSupportIPv4Error("database is not support ipv4")
  96. node = self._find_node(ipv)
  97. if node is None:
  98. return None
  99. bs = self._resolve(node)
  100. if bs is None:
  101. return None
  102. tmp = bs.decode("utf-8").split("\t")
  103. end = off + len(self._meta.fields)
  104. if len(tmp) < end:
  105. raise DatabaseError("database is error")
  106. return tmp[off:off+len(self._meta.fields)]
  107. def find_map(self, addr, language):
  108. loc = self.find(addr, language)
  109. if loc is None:
  110. return None
  111. m = {}
  112. for idx, value in enumerate(self._meta.fields):
  113. m[value] = loc[idx]
  114. return m
  115. def get_meta_data(self):
  116. return self._meta
  117. def support_languages(self):
  118. ls = []
  119. for p in self._meta.languages:
  120. ls.append(p)
  121. return ls
  122. def support_fields(self):
  123. return self._meta.fields
  124. def is_support_ipv4(self):
  125. return (self._meta.ip_version & 0x01) == 0x01
  126. def is_support_ipv6(self):
  127. return (self._meta.ip_version & 0x02) == 0x02
  128. def build_utc_time(self):
  129. return self._meta.build