Verbose should be global
[blockfinder.git] / blockfinder
blobeb8dc487d3cd1ef1ad65ee094ee32ecdd140ced0
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 # For the people of Smubworld!
6 import urllib2
7 import os
8 import time
9 import getopt
10 import sys
12 __program__ = 'blockfinder'
13 __url__ = 'http://github.com/ioerror/blockfinder/'
14 __author__ = 'Jacob Appelbaum <jacob@appelbaum.net>'
15 __copyright__ = 'Copyright (c) 2009'
16 __license__ = 'See LICENSE for licensing information'
17 __version__ = '3.1'
19 try:
20 from future import antigravity
21 except ImportError:
22 antigravity = None
24 # XXX TODO: Set the user agent and allow the use of a proxy
25 # Set up a proper Request object, set the user agent and if desired, a proxy
26 def fetch_delegation(delegation_url):
27 """ Fetch (with progress meter) and return the contents of a delegation. """
28 rows, columns = os.popen('stty size', 'r').read().split()
29 fetcher = urllib2.urlopen(delegation_url)
30 length = int(fetcher.headers.get("content-length"))
31 if not length:
32 raise Exception("Missing content-length header in reply from server.")
33 print "Fetching " + str(length) + " bytes"
34 chunk = int(int(length)/(int(columns) -4))
35 counter = 0
36 page = ""
37 # XXX TODO: we need to redraw the screen entirely, each run
38 # XXX TODO: Currently, if the user changes the screen, we draw incorrectly
39 sys.stdout.write("[" + "." * (int(columns) - 2) + "]")
40 sys.stdout.flush()
41 sys.stdout.write('\b' * (int(columns) - 2))
42 while True:
43 page += fetcher.read(chunk)
44 counter += chunk
45 sys.stdout.write('\b')
46 sys.stdout.write("=")
47 sys.stdout.write(">")
48 sys.stdout.flush()
49 if counter >= length:
50 sys.stdout.write('\n')
51 sys.stdout.flush()
52 break
53 if length == len(page):
54 return page
55 else:
56 raise Exception("Error fetching the right number of bytes!")
58 def cache_delegation(delegation_url, cache_dir):
59 """ Attempt to cache the contents of a delegation url in our cache dir. """
60 try:
61 os.stat(cache_dir)
62 except:
63 if verbose:
64 print "Initializing the cache directory..."
65 try:
66 os.mkdir(cache_dir)
67 except:
68 print "Unable to initialize cache directory. Sorry!"
69 try:
70 delegation = ""
71 print "Fetching " + delegation_url
72 delegation = fetch_delegation(delegation_url)
73 tmp = delegation_url.split('/')
74 delegation_file = str(cache_dir) + str(tmp[-1])
75 f = open(delegation_file, 'w')
76 f.write(delegation)
77 f.close()
78 return True
79 except:
80 print "Unable to fetch or cache delegation"
81 return False
83 def cache_is_dated(cache_dir, cached_files):
84 """ Returns True if the mtime of any files in cache dir is > 24hrs."""
85 try:
86 os.stat(cache_dir)
87 except:
88 print "Did you initialize the cache directory?"
89 sys.exit(2)
90 for file in cached_files.split(","):
91 fstat = os.stat(cache_dir + file)
92 if (time.time() - fstat.st_mtime) > 86400:
93 return True
94 return False
96 def update_delegation_cache(cache_dir, delegation_urls):
97 """ Fetch multiple delegation urls and cache the contents. """
98 print "Updating delegation cache..."
99 for url in delegation_urls.split():
100 try:
101 cache_delegation(url, cache_dir)
102 return True
103 except:
104 print "Unable to update delegation for " + url
105 return False
107 def load_delegation(delegation_file):
108 """ Load, parse and store the delegation file contents as a list. """
109 keys = "rir,cc,type,n,nn,asn,status"
110 try:
111 f = open(delegation_file, "r")
112 delegations = [ dict((k,v) for k,v in zip(keys.split(","), line.split("|")))
113 for line in f.readlines() if not line.startswith("#")]
114 f.close()
115 return delegations
116 except:
117 print "It appears that we are missing " + delegation_file
119 def load_all_delegations(cache_dir, delegation_urls):
120 """ Load all delegations into memory. """
121 delegations = []
122 if verbose:
123 print "Attempting to load the following urls: " + delegation_urls
124 for url in delegation_urls.split():
125 filename = url.rpartition('/')[-1]
126 if verbose:
127 print "Attempting to load delegation file: " + filename
128 delegations.append(load_delegation(cache_dir + filename))
129 return delegations
131 def lookup_country(cc, delegation):
132 """ Return data about a given county code in a supplied delegation. """
133 return [d for d in delegation if d['cc'] == cc]
135 def lookup_type(type, delegation):
136 """ Return data about a given type in a supplied delegation. """
137 return [d for d in delegation if d['type'] == type]
139 def usage():
140 """ Print usage information. """
141 print >> sys.stderr, """
142 blockfinder [-c DIR] -i
143 blockfinder [options] -t COUNTRY
145 The first form initializes the local cache. The second form queries it.
147 Understood options (not all of which are implemented yet):
148 -h, --help Show this help and exit
149 -v Be verbose
150 -c, --cachedir DIR Set the cache directory
151 -u, --useragent
152 -p, --progress
153 -o, --output FILE
154 -4, --ipv4 Search IPv4 allocations
155 -6, --ipv6 Search IPv6 allocation
156 -a, --asn Search ASN allocations
157 -t, --nationstate CC Set the country to search (given as a two-letter code)
159 At least one of -t or -i is required, and when in -t mode, at least one of -4,
160 -6, and -a is required in order to do anything sensible.
163 def main():
164 """ Where the magic starts. """
165 try:
166 opts, args = getopt.getopt(sys.argv[1:],
167 "vhc:u:pso:46at:i",
168 ["verbose", "help", "cachedir=", "useragent=", "progress",
169 "silent", "output=", "ipv4", "ipv6", "asn", "country=",
170 "initialize-delegation"])
171 except getopt.GetoptError, err:
172 print str(err)
173 usage()
174 sys.exit(2)
176 global verbose
177 verbose = False
178 output = None
179 silent = True
180 cache_dir = str(os.path.expanduser('~')) + "/.blockfinder/"
181 update_delegations = False
182 delegation_urls="""
183 ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest
184 ftp://ftp.ripe.net/ripe/stats/delegated-ripencc-latest
185 ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest
186 ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest
187 ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest
189 delegation_files=""
190 for url in delegation_urls.split():
191 filename = url.rpartition('/')
192 delegation_files += filename[-1] + ","
193 update_delegations = False
194 requests = []
195 country = ""
196 for o, a in opts:
197 if o == "-v":
198 verbose = True
199 elif o in ("-h", "--help"):
200 usage()
201 sys.exit()
202 elif o in ("-c", "--cachedir"):
203 cache_dir = a
204 elif o in ("-u", "--useragent"):
205 pass
206 elif o in ("-p", "--progress"):
207 progress = True
208 elif o in ("-s", "--silent"):
209 silent = True
210 elif o in ("-o", "--output"):
211 output = a
212 elif o in ("-4", "--ipv4"):
213 requests.append("ipv4")
214 elif o in ("-6", "--ipv6"):
215 requests.append("ipv6")
216 elif o in ("-a", "--asn"):
217 requests.append("asn")
218 # XXX TODO: This should be a positional argument as it's the only manditory one...
219 elif o in ("-t", "--nation-state"):
220 country = a.upper()
221 elif o in ("-i", "--initialize-delegations"):
222 update_delegations = True
223 else:
224 assert False, "Unhandled option; Sorry!"
226 # Update and quit of requested
227 if update_delegations:
228 update_delegation_cache(cache_dir, delegation_urls)
229 sys.exit(0)
230 if not requests:
231 print "Nothing to do. Have you requested anything?"
232 print "Example usage: blockfinder -v --ipv4 -t mm"
233 sys.exit(1)
234 # Check our cache age and warn if it's aged
235 total_delegations = 0
236 if cache_is_dated(cache_dir, delegation_files) and verbose:
237 print "Your delegation cache is older than 24 hours; you probably want to update it."
238 delegations = load_all_delegations(cache_dir, delegation_urls)
239 for delegation in delegations:
240 total_delegations += len(delegation)
241 if verbose:
242 print "We have %d entries in our delegation cache." % total_delegations
243 tmp = []
244 resultnum = 0
245 matchnum = 0
246 for delegation in delegations:
247 try:
248 results = lookup_country(country, delegation)
249 resultnum += len(results)
250 for result in results:
251 for request in requests:
252 if result['type'] == request:
253 matchnum += 1
254 print result['n']
255 except:
256 print sys.exc_info()
257 pass
258 if verbose:
259 print "We found approx possible %d entries in our delegation cache." % resultnum
260 print "We found %d matching entries in our delegation cache." % matchnum
262 if __name__ == "__main__":
263 main()