iprangeparse.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. # Written by John Hoffman
  2. # see LICENSE.txt for license information
  3. from bisect import bisect, insort
  4. try:
  5. True
  6. except:
  7. True = 1
  8. False = 0
  9. bool = lambda x: not not x
  10. def to_long_ipv4(ip):
  11. ip = ip.split('.')
  12. if len(ip) != 4:
  13. raise ValueError, "bad address"
  14. b = 0L
  15. for n in ip:
  16. b *= 256
  17. b += int(n)
  18. return b
  19. def to_long_ipv6(ip):
  20. if ip == '':
  21. raise ValueError, "bad address"
  22. if ip == '::': # boundary handling
  23. ip = ''
  24. elif ip[:2] == '::':
  25. ip = ip[1:]
  26. elif ip[0] == ':':
  27. raise ValueError, "bad address"
  28. elif ip[-2:] == '::':
  29. ip = ip[:-1]
  30. elif ip[-1] == ':':
  31. raise ValueError, "bad address"
  32. b = []
  33. doublecolon = False
  34. for n in ip.split(':'):
  35. if n == '': # double-colon
  36. if doublecolon:
  37. raise ValueError, "bad address"
  38. doublecolon = True
  39. b.append(None)
  40. continue
  41. if n.find('.') >= 0: # IPv4
  42. n = n.split('.')
  43. if len(n) != 4:
  44. raise ValueError, "bad address"
  45. for i in n:
  46. b.append(int(i))
  47. continue
  48. n = ('0'*(4-len(n))) + n
  49. b.append(int(n[:2],16))
  50. b.append(int(n[2:],16))
  51. bb = 0L
  52. for n in b:
  53. if n is None:
  54. for i in xrange(17-len(b)):
  55. bb *= 256
  56. continue
  57. bb *= 256
  58. bb += n
  59. return bb
  60. ipv4addrmask = 65535L*256*256*256*256
  61. class IP_List:
  62. def __init__(self, entrylist=None):
  63. self.ipv4list = [] # starts of ranges
  64. self.ipv4dict = {} # start: end of ranges
  65. self.ipv6list = [] # "
  66. self.ipv6dict = {} # "
  67. if entrylist:
  68. l4 = []
  69. l6 = []
  70. for b,e in entrylist:
  71. assert b <= e
  72. if b.find(':') < 0: # IPv4
  73. b = to_long_ipv4(b)
  74. e = to_long_ipv4(e)
  75. l4.append((b,e))
  76. else:
  77. b = to_long_ipv6(b)
  78. e = to_long_ipv6(e)
  79. bb = b % (256*256*256*256)
  80. if bb == ipv4addrmask:
  81. b -= bb
  82. e -= bb
  83. l4.append((b,e))
  84. else:
  85. l6.append((b,e))
  86. self._import_ipv4(l4)
  87. self._import_ipv6(l6)
  88. def __nonzero__(self):
  89. return bool(self.ipv4list or self.ipv6list)
  90. def append(self, ip_beg, ip_end = None):
  91. if ip_end is None:
  92. ip_end = ip_beg
  93. else:
  94. assert ip_beg <= ip_end
  95. if ip_beg.find(':') < 0: # IPv4
  96. ip_beg = to_long_ipv4(ip_beg)
  97. ip_end = to_long_ipv4(ip_end)
  98. l = self.ipv4list
  99. d = self.ipv4dict
  100. else:
  101. ip_beg = to_long_ipv6(ip_beg)
  102. ip_end = to_long_ipv6(ip_end)
  103. bb = ip_beg % (256*256*256*256)
  104. if bb == ipv4addrmask:
  105. ip_beg -= bb
  106. ip_end -= bb
  107. l = self.ipv4list
  108. d = self.ipv4dict
  109. else:
  110. l = self.ipv6list
  111. d = self.ipv6dict
  112. p = bisect(l,ip_beg)-1
  113. if p >= 0:
  114. while p < len(l):
  115. range_beg = l[p]
  116. if range_beg > ip_end+1:
  117. done = True
  118. break
  119. range_end = d[range_beg]
  120. if range_end < ip_beg-1:
  121. p += 1
  122. if p == len(l):
  123. done = True
  124. break
  125. continue
  126. # if neither of the above conditions is true, the ranges overlap
  127. ip_beg = min(ip_beg, range_beg)
  128. ip_end = max(ip_end, range_end)
  129. del l[p]
  130. del d[range_beg]
  131. break
  132. insort(l,ip_beg)
  133. d[ip_beg] = ip_end
  134. def _import_ipv4(self, entrylist): #entrylist = sorted list of pairs of ipv4s converted to longs
  135. assert not self.ipv4list
  136. if not entrylist:
  137. return
  138. entrylist.sort()
  139. l = []
  140. b1,e1 = entrylist[0]
  141. for b2,e2 in entrylist:
  142. if e1+1 >= b2:
  143. e1 = max(e1,e2)
  144. else:
  145. l.append((b1,e1))
  146. b1 = b2
  147. e1 = e2
  148. l.append((b1,e1))
  149. self.ipv4list = [b for b,e in l]
  150. for b,e in l:
  151. self.ipv4dict[b] = e
  152. def _import_ipv6(self, entrylist): #entrylist = sorted list of pairs of ipv6s converted to longs
  153. assert not self.ipv6list
  154. if not entrylist:
  155. return
  156. entrylist.sort()
  157. l = []
  158. b1,e1 = entrylist[0]
  159. for b2,e2 in entrylist:
  160. if e1+1 >= b2:
  161. e1 = max(e1,e2)
  162. else:
  163. l.append((b1,e1))
  164. b1 = b2
  165. e1 = e2
  166. l.append((b1,e1))
  167. self.ipv6list = [b for b,e in l]
  168. for b,e in l:
  169. self.ipv6dict[b] = e
  170. def includes(self, ip):
  171. if not (self.ipv4list or self.ipv6list):
  172. return False
  173. if ip.find(':') < 0: # IPv4
  174. ip = to_long_ipv4(ip)
  175. l = self.ipv4list
  176. d = self.ipv4dict
  177. else:
  178. ip = to_long_ipv6(ip)
  179. bb = ip % (256*256*256*256)
  180. if bb == ipv4addrmask:
  181. ip -= bb
  182. l = self.ipv4list
  183. d = self.ipv4dict
  184. else:
  185. l = self.ipv6list
  186. d = self.ipv6dict
  187. for ip_beg in l[bisect(l,ip)-1:]:
  188. if ip == ip_beg:
  189. return True
  190. ip_end = d[ip_beg]
  191. if ip > ip_beg and ip <= ip_end:
  192. return True
  193. return False
  194. # reads a list from a file in the format 'whatever:whatever:ip-ip'
  195. # (not IPv6 compatible at all)
  196. def read_rangelist(self, file):
  197. l = []
  198. f = open(file, 'r')
  199. while True:
  200. line = f.readline()
  201. if not line:
  202. break
  203. line = line.strip()
  204. if not line or line[0] == '#':
  205. continue
  206. line = line.split(':')[-1]
  207. try:
  208. ip1,ip2 = line.split('-')
  209. except:
  210. ip1 = line
  211. ip2 = line
  212. try:
  213. ip1 = to_long_ipv4(ip1)
  214. ip2 = to_long_ipv4(ip2)
  215. assert ip1 <= ip2
  216. except:
  217. print '*** WARNING *** could not parse IP range: '+line
  218. l.append((ip1,ip2))
  219. f.close()
  220. self._import_ipv4(l)
  221. def is_ipv4(ip):
  222. return ip.find(':') < 0
  223. def is_valid_ip(ip):
  224. try:
  225. if is_ipv4(ip):
  226. a = ip.split('.')
  227. assert len(a) == 4
  228. for i in a:
  229. chr(int(i))
  230. return True
  231. to_long_ipv6(ip)
  232. return True
  233. except:
  234. return False