Update doc string, fix looping error
[blockfinder.git] / blockfinder
blob8b28ab5d0d3b406f735a50bcb986667c485e3cde
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 verbose = False
26 # XXX TODO: Set the user agent and allow the use of a proxy
27 # Set up a proper Request object, set the user agent and if desired, a proxy
28 def fetch_delegation(delegation_url):
29 """ Fetch (with progress meter) and return the contents of a delegation. """
30 rows, columns = os.popen('stty size', 'r').read().split()
31 fetcher = urllib2.urlopen(delegation_url)
32 length = int(fetcher.headers.get("content-length"))
33 if not length:
34 raise Exception("Missing content-length header in reply from server.")
35 print "Fetching " + str(length) + " bytes"
36 chunk = int(int(length)/(int(columns) -4))
37 counter = 0
38 page = ""
39 # XXX TODO: we need to redraw the screen entirely, each run
40 # XXX TODO: Currently, if the user changes the screen, we draw incorrectly
41 sys.stdout.write("[" + "." * (int(columns) - 2) + "]")
42 sys.stdout.flush()
43 sys.stdout.write('\b' * (int(columns) - 2))
44 while True:
45 page += fetcher.read(chunk)
46 counter += chunk
47 sys.stdout.write('\b')
48 sys.stdout.write("=")
49 sys.stdout.write(">")
50 sys.stdout.flush()
51 if counter >= length:
52 sys.stdout.write('\n')
53 sys.stdout.flush()
54 break
55 if length == len(page):
56 return page
57 else:
58 raise Exception("Error fetching the right number of bytes!")
60 def cache_delegation(delegation_url, cache_dir):
61 """ Attempt to cache the contents of a delegation url in our cache dir. """
62 try:
63 os.stat(cache_dir)
64 except:
65 if verbose:
66 print "Initializing the cache directory..."
67 try:
68 os.mkdir(cache_dir)
69 except:
70 print "Unable to initialize cache directory. Sorry!"
71 try:
72 delegation = ""
73 print "Fetching " + delegation_url
74 delegation = fetch_delegation(delegation_url)
75 tmp = delegation_url.split('/')
76 delegation_file = str(cache_dir) + str(tmp[-1])
77 f = open(delegation_file, 'w')
78 f.write(delegation)
79 f.close()
80 return True
81 except:
82 print "Unable to fetch or cache delegation"
83 return False
85 def cache_age_check(cache_dir, cached_files):
86 """ Returns True if the mtime of any files in cache dir is > 24hrs."""
87 try:
88 os.stat(cache_dir)
89 except:
90 print "Did you initialize the cache directory?"
91 sys.exit(2)
92 for file in cached_files.split(","):
93 fstat = os.stat(cache_dir + file)
94 if (time.time() - fstat.st_mtime) > 86400:
95 return True
96 return False
98 def update_delegation_cache(delegation_urls, cache_dir):
99 """ Fetch multiple delegation urls and cache the contents. """
100 print "Updating delegation cache"
101 for url in delegation_urls.split():
102 try:
103 cache_delegation(url, cache_dir)
104 except:
105 print "Unable to update delegation for " + url
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 os.stat(delegation_file)
112 f = open(delegation_file, "r")
113 delegations = [ dict((k,v) for k,v in zip(keys.split(","), line.split("|")))
114 for line in f.readlines()if not line.startswith("#")]
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: \n" + delegation_urls
124 for url in delegation_urls.split():
125 filename = url.rpartition('/')
126 if verbose:
127 print "Attempting to load delegation file: " + filename[-1]
128 delegations.append(load_delegation(cache_dir + filename[-1]))
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. 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 output = None
177 verbose = False
178 silent = True
179 cache_dir = str(os.path.expanduser('~')) + "/.blockfinder/"
180 update_delegations = False
181 delegation_urls="""
182 ftp://ftp.arin.net/pub/stats/arin/delegated-arin-latest
183 ftp://ftp.ripe.net/ripe/stats/delegated-ripencc-latest
184 ftp://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-latest
185 ftp://ftp.apnic.net/pub/stats/apnic/delegated-apnic-latest
186 ftp://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-latest
188 delegation_files=""
189 for url in delegation_urls.split():
190 filename = url.rpartition('/')
191 delegation_files += filename[-1] + ","
192 delegation_fmt="rir,cc,type,n,nn,asn,status"
193 iso_codes="http://www.iso.org/iso/list-en1-semic-3.txt"
194 user_agent = "blockfinder"
195 update_delegations = False
196 requests = []
197 country = ""
198 for o, a in opts:
199 if o == "-v":
200 verbose = True
201 elif o in ("-h", "--help"):
202 usage()
203 sys.exit()
204 elif o in ("-c", "--cachedir"):
205 cache_dir = a
206 elif o in ("-u", "--useragent"):
207 pass
208 elif o in ("-p", "--progress"):
209 progress = True
210 elif o in ("-s", "--silent"):
211 silent = True
212 elif o in ("-o", "--output"):
213 output = a
214 elif o in ("-4", "--ipv4"):
215 requests.append("ipv4")
216 elif o in ("-6", "--ipv6"):
217 requests.append("ipv6")
218 elif o in ("-a", "--asn"):
219 requests.append("asn")
220 # XXX TODO: This should be a positional argument as it's the only manditory one...
221 elif o in ("-t", "--nation-state"):
222 country = a.upper()
223 elif o in ("-i", "--initialize-delegations"):
224 update_delegations = True
225 else:
226 assert False, "Unhandled option; Sorry!"
228 # Update and quit of requested
229 if update_delegations:
230 update_delegation_cache(delegation_urls, cache_dir)
231 sys.exit(0)
232 if not requests:
233 print "Nothing to do. Have you requested anything?"
234 print "Example usage: blockfinder -v --ipv4 -t mm"
235 sys.exit(1)
236 # Check our cache age and warn if it's aged
237 total_delegations = 0
238 if cache_age_check(cache_dir, delegation_files) and verbose:
239 print "Your delegation cache is older than 24 hours; you probably want to update it."
240 delegations = load_all_delegations(cache_dir, delegation_urls)
241 for delegation in delegations:
242 total_delegations += len(delegation)
243 if verbose:
244 print "We have %d entries in our delegation cache." % total_delegations
245 tmp = []
246 resultnum = 0
247 matchnum = 0
248 for delegation in delegations:
249 try:
250 results = lookup_country(country, delegation)
251 resultnum += len(results)
252 for result in results:
253 for request in requests:
254 if result['type'] == request:
255 matchnum += 1
256 print result['n']
257 except:
258 print sys.exc_info()
259 pass
260 if verbose:
261 print "We found approx possible %d entries in our delegation cache." % resultnum
262 print "We found %d matching entries in our delegation cache." % matchnum
264 if __name__ == "__main__":
265 main()