2 # -*- coding: utf-8 -*-
4 # For the people of Smubworld!
13 __program__
= 'blockfinder'
14 __url__
= 'http://github.com/ioerror/blockfinder/'
15 __author__
= 'Jacob Appelbaum <jacob@appelbaum.net>'
16 __copyright__
= 'Copyright (c) 2009'
17 __license__
= 'See LICENSE for licensing information'
21 from future
import antigravity
25 def update_progress_bar(percent_done
, caption
=""):
26 """Write a progress bar to the console"""
27 rows
, columns
= map(int, os
.popen('stty size', 'r').read().split())
28 width
= columns
- 4 - len(caption
)
29 sys
.stdout
.write("[%s>%s] %s\x1b[G" % (
30 "=" * int(percent_done
*width
),
31 "." * (width
- int(percent_done
* width
)),
35 # XXX TODO:allow the use of a proxy
36 # Set up a proper Request object, set the user agent and if desired, a proxy
37 def fetch(url
,useragent
):
38 """ Fetch (with progress meter) and return the contents of a url. """
39 req
=urllib2
.Request(url
)
40 req
.add_header('User-agent', useragent
)
41 #req.set_proxy(host, type)
42 fetcher
= urllib2
.urlopen(req
)
43 length
= int(fetcher
.headers
.get("content-length"))
45 raise Exception("Missing content-length header in reply from server.")
46 print "Fetching ", str (round(float(length
/1024),2)) , " kilobytes"
50 t_delta
= time
.time() - t_start
52 float(len(ret
)) / length
,
53 "%.2f K/s" % (len(ret
) / 1024 / t_delta
) )
54 tmp
= fetcher
.read(1024)
56 if len(ret
) != length
:
57 raise Exception("Expected %s bytes, only received %s" % (
63 def cache_delegation(cache_dir
, delegation_url
, useragent
):
64 """ Attempt to cache the contents of a delegation url in our cache dir. """
70 print "Initializing the cache directory..."
75 print "Fetching " + delegation_url
76 delegation
= fetch(delegation_url
,useragent
)
77 tmp
= delegation_url
.split('/')
78 delegation_file
= str(cache_dir
) + str(tmp
[-1])
80 f
= open(delegation_file
, 'w')
88 def cache_is_dated(cache_dir
, cached_files
):
89 """ Returns True if the mtime of any files in cache dir is > 24hrs."""
93 print "\nDid you initialize the cache directory?\n"
95 for file in cached_files
:
96 fstat
= os
.stat(cache_dir
+ file)
97 if (time
.time() - fstat
.st_mtime
) > 86400:
101 def create_sql_database(cache_dir
):
102 """creates a sqlite database and if there is already an existing one it deletes it.
103 ftp://ftp.arin.net/pub/stats/arin/README"""
105 os
.remove(cache_dir
+"sqlitedb")
108 conn
= sqlite3
.connect(cache_dir
+"sqlitedb")
109 cursor
= conn
.cursor()
110 cursor
.execute("""create table asn(registry text, cc text, start text, value INTEGER, date text, status text)""")
111 cursor
.execute("""create table ipv4(registry text, cc text, start text, value INTEGER, date text, status text)""")
112 cursor
.execute("""create table ipv6(registry text, cc text, start text, value INTEGER, date text, status text)""")
116 def insert_into_sql_database(delegations
,cache_dir
):
117 """ inserts delegations into the sqlite database"""
118 conn
= sqlite3
.connect(cache_dir
+"sqlitedb")
119 cursor
= conn
.cursor()
121 for delegation
in delegations
:
122 for entry
in delegation
:
123 registry
= str(entry
['registry'])
124 if registry
.isdigit() ==False and str (entry
['cc']) !="*":
125 if entry
['type'] == "ipv6":
127 if entry
['type'] == "ipv4":
129 if entry
['type'] == "asn":
131 text
= """INSERT INTO """ + table
+ """ ( registry, cc, start, value, date,status) VALUES (?,?,?,?,?,?)"""
132 data
= [entry
['registry'], entry
['cc'], entry
['start'], entry
['value'], entry
['date'], entry
['status'] ]
133 cursor
.execute(text
, data
)
137 def get_total_delegations_from_db(cache_dir
):
138 """returns count of the n.o. of entries in the ipv4 +ipv6 + asn tables"""
139 conn
= sqlite3
.connect(cache_dir
+"sqlitedb")
140 cursor
= conn
.cursor()
142 table_names
=["ipv4", "ipv6", "asn"]
143 for table
in table_names
:
144 cursor
.execute("""select count (*) from """ + table
)
145 count
= count
+ int (cursor
.fetchone()[0] )
149 def get_possible_match_entries(cc
,cache_dir
):
150 """ get the count of 'possible' matching delegation entries"""
151 conn
= sqlite3
.connect(cache_dir
+"sqlitedb")
152 cursor
= conn
.cursor()
154 table_names
=["ipv4", "ipv6", "asn"]
155 for table
in table_names
:
156 cursor
.execute("""select count (*) from """ + table
+ """ where cc=?""",cc
)
157 count
= count
+ int (cursor
.fetchone()[0] )
161 def use_sql_database(request
, cc
, cache_dir
):
162 """now with added magic!"""
163 conn
= sqlite3
.connect(cache_dir
+"sqlitedb")
164 cursor
= conn
.cursor()
166 print "We have %d entries in our delegation cache." %get
_total
_delegations
_from
_db
(cache_dir
)
167 text
="""select start,value from """ + request
+ """ where cc=?"""
169 cursor
.execute(text
,cc
)
171 if request
== "ipv4":
172 print str(row
[0]) + "/" + str(calculate_ipv4_subnet(int(row
[1])))
173 elif request
== "ipv6":
174 print str(row
[0]) + "/" + str(int(row
[1]))
176 print str(int(row
[0]) )
178 print "We found %d possible entries in our delegation cache." % get_possible_match_entries(cc
, cache_dir
)
179 cursor
.execute("""select count(*) from """ + request
+ """ where cc=?""", cc
)
180 print "We found %d matching entries in our delegation cache." % int (cursor
.fetchone()[0] )
183 def get_md5_from_delegation_md5_file(cache_dir
, delegation_file
):
184 """ Returns the md5sum from the delegation md5 file (if it exists)"""
187 f
= open(cache_dir
+ delegation_file
+".md5", "r")
190 if delegation_file
== "delegated-afrinic-latest":
191 pos
= checksum
.find(" ")
192 assert pos
<len(checksum
)
193 checksum
= str ( checksum
[:pos
] )
195 pos
= checksum
.find("=") +2
196 assert pos
<len(checksum
)
197 checksum
= str ( checksum
[pos
:-1] )
202 def verify_delegation_file(cache_dir
, delegation_file
):
203 """compares the delegation file md5sum to that of the provided md5sum
204 returns True if they match otherwise returns False"""
208 f
= open(cache_dir
+ delegation_file
, "rb")
209 checksum_of_file
= str (hashlib
.md5(f
.read()).hexdigest() )
213 checksum
= get_md5_from_delegation_md5_file(cache_dir
,delegation_file
)
214 if checksum
!= checksum_of_file
:
216 if checksum
== checksum_of_file
and checksum
!="":
219 def verify_cache(cache_dir
, delegation_files
):
220 """ if in verbose mode prints the result of checking the checksum of the
222 for file in delegation_files
:
224 print "verifying " + file
225 if verify_delegation_file(cache_dir
,file):
227 print "the md5 checksum of " + file + " *matches* the provided checksum"
230 print "the md5 checksum of " + file + " does *not* match the provided checksum"
232 def update_delegation_cache(cache_dir
, delegation_urls
, useragent
):
233 """ Fetch multiple delegation urls and cache the contents. """
234 print "Updating delegation cache..."
235 for url
in delegation_urls
.split():
236 cache_delegation(cache_dir
, url
+".md5",useragent
)
237 if verify_delegation_file(cache_dir
, url
.rpartition('/')[-1]):
240 cache_delegation(cache_dir
, url
,useragent
)
243 def load_delegation(delegation_file
):
244 """ Load, parse and store the delegation file contents as a list. """
245 keys
= "registry cc type start value date status"
247 f
= open(delegation_file
, "r")
248 delegations
= [ dict((k
,v
) for k
,v
in zip(keys
.split(), line
.split("|")))
249 for line
in f
.readlines() if not line
.startswith("#")]
255 def load_all_delegations(cache_dir
, delegation_urls
):
256 """ Load all delegations into memory. """
258 for url
in delegation_urls
.split():
259 filename
= url
.rpartition('/')[-1]
261 print "Attempting to load delegation file into memory: " + filename
262 delegations
.append(load_delegation(cache_dir
+ filename
))
265 def calculate_ipv4_subnet(host_count
):
266 return 32 - int(floor(log(host_count
,2)))
269 """ Print usage information. """
270 print >> sys
.stderr
, """
271 blockfinder [-c DIR] -i
272 blockfinder [options] -t COUNTRY
274 The first form initializes the local cache. The second form queries it.
276 Understood options (not all of which are implemented yet):
277 -h, --help Show this help and exit
279 -c, --cachedir DIR Set the cache directory
283 -4, --ipv4 Search IPv4 allocations
284 -6, --ipv6 Search IPv6 allocation
285 -a, --asn Search ASN allocations
286 -t, --nationstate CC Set the country to search (given as a two-letter code)
288 At least one of -t or -i is required, and when in -t mode, at least one of -4,
289 -6, and -a is required in order to do anything sensible.
293 """ Where the magic starts. """
295 opts
, args
= getopt
.getopt(sys
.argv
[1:],
297 ["verbose", "help", "cachedir=", "useragent=", "progress",
298 "silent", "output=", "ipv4", "ipv6", "asn", "country=",
299 "initialize-delegation"])
300 except getopt
.GetoptError
, err
:
309 cache_dir
= str(os
.path
.expanduser('~')) + "/.blockfinder/"
310 update_delegations
= False
312 ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest
313 ftp://ftp.ripe.net/ripe/stats/delegated-ripencc-latest
314 ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest
315 ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest
316 ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest
319 for url
in delegation_urls
.split():
320 filename
= url
.rpartition('/')
321 delegation_files
.append(filename
[-1])
322 update_delegations
= False
325 useragent
= "Mozilla/5.0"
329 elif o
in ("-h", "--help"):
332 elif o
in ("-c", "--cachedir"):
334 elif o
in ("-u", "--useragent"):
336 elif o
in ("-p", "--progress"):
338 elif o
in ("-s", "--silent"):
340 elif o
in ("-o", "--output"):
342 elif o
in ("-4", "--ipv4"):
343 requests
.append("ipv4")
344 elif o
in ("-6", "--ipv6"):
345 requests
.append("ipv6")
346 elif o
in ("-a", "--asn"):
347 requests
.append("asn")
348 # XXX TODO: This should be a positional argument as it's the only manditory one...
349 elif o
in ("-t", "--nation-state"):
351 elif o
in ("-i", "--initialize-delegations"):
352 update_delegations
= True
354 assert False, "Unhandled option; Sorry!"
357 if update_delegations
:
358 update_delegation_cache(cache_dir
,delegation_urls
,useragent
)
360 verify_cache(cache_dir
, delegation_files
)
361 delegations
= load_all_delegations(cache_dir
, delegation_urls
)
362 create_sql_database(cache_dir
)
363 insert_into_sql_database(delegations
, cache_dir
)
366 print "Nothing to do. Have you requested anything?"
367 print "Example usage: blockfinder -v --ipv4 -t mm"
369 # Check our cache age and warn if it's aged
370 total_delegations
= 0
371 if cache_is_dated(cache_dir
, delegation_files
) and verbose
:
372 print "Your delegation cache is older than 24 hours; you probably want to update it."
373 for request
in requests
:
374 use_sql_database(request
, country
, cache_dir
)
376 if __name__
== "__main__":