Skip to content

Commit de2c39c

Browse files
committed
Initial commit.
0 parents  commit de2c39c

File tree

10 files changed

+934
-0
lines changed

10 files changed

+934
-0
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
IP2Location <support@ip2location.com>

COPYING

Lines changed: 340 additions & 0 deletions
Large diffs are not rendered by default.

ChangeLog

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1.0.0 2016-12-15
2+
* Initial release

IP2Proxy.py

Lines changed: 333 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,333 @@
1+
import sys
2+
import struct
3+
import socket
4+
5+
if sys.version < '3':
6+
def u(x):
7+
return x.decode('utf-8')
8+
def b(x):
9+
return str(x)
10+
else:
11+
def u(x):
12+
if isinstance(x, bytes):
13+
return x.decode()
14+
return x
15+
def b(x):
16+
if isinstance(x, bytes):
17+
return x
18+
return x.encode('ascii')
19+
20+
# Windows version of Python does not provide it
21+
# for compatibility with older versions of Windows.
22+
if not hasattr(socket, 'inet_pton'):
23+
def inet_pton(t, addr):
24+
import ctypes
25+
a = ctypes.WinDLL('ws2_32.dll')
26+
in_addr_p = ctypes.create_string_buffer(b(addr))
27+
if t == socket.AF_INET:
28+
out_addr_p = ctypes.create_string_buffer(4)
29+
elif t == socket.AF_INET6:
30+
out_addr_p = ctypes.create_string_buffer(16)
31+
n = a.inet_pton(t, in_addr_p, out_addr_p)
32+
if n == 0:
33+
raise ValueError('Invalid address')
34+
return out_addr_p.raw
35+
socket.inet_pton = inet_pton
36+
37+
_VERSION = '1.0.0'
38+
_FIELD_NOT_SUPPORTED = 'NOT SUPPORTED'
39+
_INVALID_IP_ADDRESS = 'INVALID IP ADDRESS'
40+
41+
class IP2ProxyRecord:
42+
''' IP2Proxy record with all fields from the database '''
43+
ip = None
44+
country_short = _FIELD_NOT_SUPPORTED
45+
country_long = _FIELD_NOT_SUPPORTED
46+
region = _FIELD_NOT_SUPPORTED
47+
city = _FIELD_NOT_SUPPORTED
48+
isp = _FIELD_NOT_SUPPORTED
49+
proxy_type = _FIELD_NOT_SUPPORTED
50+
51+
def __str__(self):
52+
return str(self.__dict__)
53+
54+
def __repr__(self):
55+
return repr(self.__dict__)
56+
57+
_COUNTRY_POSITION = (0, 2, 3, 3, 3)
58+
_REGION_POSITION = (0, 0, 0, 4, 4)
59+
_CITY_POSITION = (0, 0, 0, 5, 5)
60+
_ISP_POSITION = (0, 0, 0, 0, 6)
61+
_PROXYTYPE_POSITION = (0, 0, 2, 2, 2)
62+
63+
class IP2Proxy(object):
64+
''' IP2Proxy database '''
65+
66+
def __init__(self, filename=None):
67+
''' Creates a database object and opens a file if filename is given '''
68+
if filename:
69+
self.open(filename)
70+
71+
def __enter__(self):
72+
if not hasattr(self, '_f') or self._f.closed:
73+
raise ValueError("Cannot enter context with closed file")
74+
return self
75+
76+
def __exit__(self, exc_type, exc_value, traceback):
77+
self.close()
78+
79+
def open(self, filename):
80+
''' Opens a database file '''
81+
# Ensure old file is closed before opening a new one
82+
self.close()
83+
84+
self._f = open(filename, 'rb')
85+
self._dbtype = struct.unpack('B', self._f.read(1))[0]
86+
self._dbcolumn = struct.unpack('B', self._f.read(1))[0]
87+
self._dbyear = 2000 + struct.unpack('B', self._f.read(1))[0]
88+
self._dbmonth = struct.unpack('B', self._f.read(1))[0]
89+
self._dbday = struct.unpack('B', self._f.read(1))[0]
90+
self._ipv4dbcount = struct.unpack('<I', self._f.read(4))[0]
91+
self._ipv4dbaddr = struct.unpack('<I', self._f.read(4))[0]
92+
self._ipv6dbcount = struct.unpack('<I', self._f.read(4))[0]
93+
self._ipv6dbaddr = struct.unpack('<I', self._f.read(4))[0]
94+
self._ipv4indexbaseaddr = struct.unpack('<I', self._f.read(4))[0]
95+
self._ipv6indexbaseaddr = struct.unpack('<I', self._f.read(4))[0]
96+
97+
def close(self):
98+
if hasattr(self, '_f'):
99+
# If there is file close it.
100+
self._f.close()
101+
del self._f
102+
103+
def get_module_version(self):
104+
return _VERSION
105+
106+
def get_package_version(self):
107+
return str(self._dbtype)
108+
109+
def get_database_version(self):
110+
return str(self._dbyear) + "." + str(self._dbmonth) + "." + str(self._dbday)
111+
112+
def get_country_short(self, ip):
113+
''' Get country_short '''
114+
try:
115+
rec = self._get_record(ip)
116+
country_short = rec.country_short
117+
except:
118+
country_short = _INVALID_IP_ADDRESS
119+
return country_short
120+
121+
def get_country_long(self, ip):
122+
''' Get country_long '''
123+
try:
124+
rec = self._get_record(ip)
125+
country_long = rec.country_long
126+
except:
127+
country_long = _INVALID_IP_ADDRESS
128+
return country_long
129+
130+
def get_region(self, ip):
131+
''' Get region '''
132+
try:
133+
rec = self._get_record(ip)
134+
region = rec.region
135+
except:
136+
region = _INVALID_IP_ADDRESS
137+
return region
138+
139+
def get_city(self, ip):
140+
''' Get city '''
141+
try:
142+
rec = self._get_record(ip)
143+
city = rec.city
144+
except:
145+
city = _INVALID_IP_ADDRESS
146+
return city
147+
148+
def get_isp(self, ip):
149+
''' Get isp '''
150+
try:
151+
rec = self._get_record(ip)
152+
isp = rec.isp
153+
except:
154+
isp = _INVALID_IP_ADDRESS
155+
return isp
156+
157+
def get_proxy_type(self, ip):
158+
''' Get usage_type '''
159+
try:
160+
rec = self._get_record(ip)
161+
proxy_type = rec.proxy_type
162+
except:
163+
proxy_type = _INVALID_IP_ADDRESS
164+
return proxy_type
165+
166+
def is_proxy(self, ip):
167+
''' Determine whether is a proxy '''
168+
try:
169+
rec = self._get_record(ip)
170+
if self._dbtype == 1:
171+
isproxy = 0 if (rec.country_short == '-') else 1
172+
else:
173+
isproxy = 0 if (rec.proxy_type == '-') else ( 2 if (rec.proxy_type == 'DCH') else 1)
174+
except:
175+
isproxy = -1
176+
return isproxy
177+
178+
def get_all(self, ip):
179+
''' Get the whole record with all fields read from the file '''
180+
try:
181+
rec = self._get_record(ip)
182+
country_short = rec.country_short
183+
country_long = rec.country_long
184+
region = rec.region
185+
city = rec.city
186+
isp = rec.isp
187+
proxy_type = rec.proxy_type
188+
189+
if self._dbtype == 1:
190+
isproxy = 0 if (rec.country_short == '-') else 1
191+
else:
192+
isproxy = 0 if (rec.proxy_type == '-') else ( 2 if (rec.proxy_type == 'DCH') else 1)
193+
except:
194+
country_short = _INVALID_IP_ADDRESS
195+
country_long = _INVALID_IP_ADDRESS
196+
region = _INVALID_IP_ADDRESS
197+
city = _INVALID_IP_ADDRESS
198+
isp = _INVALID_IP_ADDRESS
199+
proxy_type = _INVALID_IP_ADDRESS
200+
isproxy = -1
201+
202+
results = {}
203+
results['isproxy'] = isproxy
204+
results['proxy_type'] = proxy_type
205+
results['country_short'] = country_short
206+
results['country_long'] = country_long
207+
results['region'] = region
208+
results['city'] = city
209+
results['isp'] = isp
210+
return results
211+
212+
def _reads(self, offset):
213+
self._f.seek(offset - 1)
214+
n = struct.unpack('B', self._f.read(1))[0]
215+
return u(self._f.read(n))
216+
# return self._f.read(n).decode('iso-8859-1').encode('utf-8')
217+
218+
def _readi(self, offset):
219+
self._f.seek(offset - 1)
220+
return struct.unpack('<I', self._f.read(4))[0]
221+
222+
def _readip(self, offset, ipv):
223+
if ipv == 4:
224+
return self._readi(offset)
225+
elif ipv == 6:
226+
a, b, c, d = self._readi(offset), self._readi(offset + 4), self._readi(offset + 8), self._readi(offset + 12)
227+
return (d << 96) | (c << 64) | (b << 32) | a
228+
229+
def _readips(self, offset, ipv):
230+
if ipv == 4:
231+
return socket.inet_ntoa(struct.pack('!L', self._readi(offset)))
232+
elif ipv == 6:
233+
return str(self._readip(offset, ipv))
234+
235+
def _read_record(self, mid, ipv):
236+
rec = IP2ProxyRecord()
237+
238+
if ipv == 4:
239+
off = 0
240+
baseaddr = self._ipv4dbaddr
241+
elif ipv == 6:
242+
off = 12
243+
baseaddr = self._ipv6dbaddr
244+
245+
rec.ip = self._readips(baseaddr + (mid) * self._dbcolumn * 4, ipv)
246+
247+
def calc_off(what, mid):
248+
return baseaddr + mid * (self._dbcolumn * 4 + off) + off + 4 * (what[self._dbtype]-1)
249+
250+
if _COUNTRY_POSITION[self._dbtype] != 0:
251+
rec.country_short = self._reads(self._readi(calc_off(_COUNTRY_POSITION, mid)) + 1)
252+
rec.country_long = self._reads(self._readi(calc_off(_COUNTRY_POSITION, mid)) + 4)
253+
254+
if _REGION_POSITION[self._dbtype] != 0:
255+
rec.region = self._reads(self._readi(calc_off(_REGION_POSITION, mid)) + 1)
256+
257+
if _CITY_POSITION[self._dbtype] != 0:
258+
rec.city = self._reads(self._readi(calc_off(_CITY_POSITION, mid)) + 1)
259+
260+
if _ISP_POSITION[self._dbtype] != 0:
261+
rec.isp = self._reads(self._readi(calc_off(_ISP_POSITION, mid)) + 1)
262+
263+
if _PROXYTYPE_POSITION[self._dbtype] != 0:
264+
rec.proxy_type = self._reads(self._readi(calc_off(_PROXYTYPE_POSITION, mid)) + 1)
265+
266+
return rec
267+
268+
def __iter__(self):
269+
low, high = 0, self._ipv4dbcount
270+
while low <= high:
271+
yield self._read_record(low, 4)
272+
low += 1
273+
274+
low, high = 0, self._ipv6dbcount
275+
while low <= high:
276+
yield self._read_record(low, 6)
277+
low += 1
278+
279+
def _parse_addr(self, addr):
280+
''' Parses address and returns IP version. Raises exception on invalid argument '''
281+
ipv = 0
282+
try:
283+
socket.inet_pton(socket.AF_INET6, addr)
284+
# Convert ::FFFF:x.y.z.y to IPv4
285+
if addr.lower().startswith('::ffff:'):
286+
try:
287+
socket.inet_pton(socket.AF_INET, addr)
288+
ipv = 4
289+
except:
290+
ipv = 6
291+
else:
292+
ipv = 6
293+
except:
294+
socket.inet_pton(socket.AF_INET, addr)
295+
ipv = 4
296+
return ipv
297+
298+
def _get_record(self, ip):
299+
low = 0
300+
ipv = self._parse_addr(ip)
301+
if ipv == 4:
302+
ipno = struct.unpack('!L', socket.inet_pton(socket.AF_INET, ip))[0]
303+
off = 0
304+
baseaddr = self._ipv4dbaddr
305+
high = self._ipv4dbcount
306+
if self._ipv4indexbaseaddr > 0:
307+
indexpos = ((ipno >> 16) << 3) + self._ipv4indexbaseaddr
308+
low = self._readi(indexpos)
309+
high = self._readi(indexpos + 4)
310+
311+
elif ipv == 6:
312+
a, b = struct.unpack('!QQ', socket.inet_pton(socket.AF_INET6, ip))
313+
ipno = (a << 64) | b
314+
off = 12
315+
baseaddr = self._ipv6dbaddr
316+
high = self._ipv6dbcount
317+
if self._ipv6indexbaseaddr > 0:
318+
indexpos = ((ipno >> 112) << 3) + self._ipv6indexbaseaddr
319+
low = self._readi(indexpos)
320+
high = self._readi(indexpos + 4)
321+
322+
while low <= high:
323+
mid = int((low + high) / 2)
324+
ipfrom = self._readip(baseaddr + (mid) * (self._dbcolumn * 4 + off), ipv)
325+
ipto = self._readip(baseaddr + (mid + 1) * (self._dbcolumn * 4 + off), ipv)
326+
327+
if ipfrom <= ipno < ipto:
328+
return self._read_record(mid, ipv)
329+
else:
330+
if ipno < ipfrom:
331+
high = mid - 1
332+
else:
333+
low = mid + 1

0 commit comments

Comments
 (0)