6
6
# Generate seeds.txt from Pieter's DNS seeder
7
7
#
8
8
9
+ import argparse
9
10
import re
10
11
import sys
11
- import dns .resolver
12
12
import collections
13
13
from typing import List , Dict , Union
14
14
15
+ from asmap import ASMap
16
+
15
17
NSEEDS = 512
16
18
17
19
MAX_SEEDS_PER_ASN = {
@@ -123,34 +125,8 @@ def filtermultiport(ips: List[Dict]) -> List[Dict]:
123
125
hist [ip ['sortkey' ]].append (ip )
124
126
return [value [0 ] for (key ,value ) in list (hist .items ()) if len (value )== 1 ]
125
127
126
- def lookup_asn (net : str , ip : str ) -> Union [int , None ]:
127
- """ Look up the asn for an `ip` address by querying cymru.com
128
- on network `net` (e.g. ipv4 or ipv6).
129
-
130
- Returns in integer ASN or None if it could not be found.
131
- """
132
- try :
133
- if net == 'ipv4' :
134
- ipaddr = ip
135
- prefix = '.origin'
136
- else : # http://www.team-cymru.com/IP-ASN-mapping.html
137
- res = str () # 2001:4860:b002:23::68
138
- for nb in ip .split (':' )[:4 ]: # pick the first 4 nibbles
139
- for c in nb .zfill (4 ): # right padded with '0'
140
- res += c + '.' # 2001 4860 b002 0023
141
- ipaddr = res .rstrip ('.' ) # 2.0.0.1.4.8.6.0.b.0.0.2.0.0.2.3
142
- prefix = '.origin6'
143
-
144
- asn = int ([x .to_text () for x in dns .resolver .resolve ('.' .join (
145
- reversed (ipaddr .split ('.' ))) + prefix + '.asn.cymru.com' ,
146
- 'TXT' ).response .answer ][0 ].split ('\" ' )[1 ].split (' ' )[0 ])
147
- return asn
148
- except Exception as e :
149
- sys .stderr .write (f'ERR: Could not resolve ASN for "{ ip } ": { e } \n ' )
150
- return None
151
-
152
128
# Based on Greg Maxwell's seed_filter.py
153
- def filterbyasn (ips : List [Dict ], max_per_asn : Dict , max_per_net : int ) -> List [Dict ]:
129
+ def filterbyasn (asmap : ASMap , ips : List [Dict ], max_per_asn : Dict , max_per_net : int ) -> List [Dict ]:
154
130
""" Prunes `ips` by
155
131
(a) trimming ips to have at most `max_per_net` ips from each net (e.g. ipv4, ipv6); and
156
132
(b) trimming ips to have at most `max_per_asn` ips from each asn in each net.
@@ -173,13 +149,14 @@ def filterbyasn(ips: List[Dict], max_per_asn: Dict, max_per_net: int) -> List[Di
173
149
# do not add this ip as we already too many
174
150
# ips from this network
175
151
continue
176
- asn = lookup_asn (ip [ 'net' ], ip ['ip' ])
177
- if asn is None or asn_count [asn ] == max_per_asn [ip ['net' ]]:
152
+ asn = asmap . lookup_asn (ip ['ip' ])
153
+ if asn is None or asn_count [ip [ 'net' ], asn ] == max_per_asn [ip ['net' ]]:
178
154
# do not add this ip as we already have too many
179
155
# ips from this ASN on this network
180
156
continue
181
- asn_count [asn ] += 1
157
+ asn_count [ip [ 'net' ], asn ] += 1
182
158
net_count [ip ['net' ]] += 1
159
+ ip ['asn' ] = asn
183
160
result .append (ip )
184
161
185
162
# Add back Onions (up to max_per_net)
@@ -195,7 +172,18 @@ def ip_stats(ips: List[Dict]) -> str:
195
172
196
173
return f"{ hist ['ipv4' ]:6d} { hist ['ipv6' ]:6d} { hist ['onion' ]:6d} "
197
174
175
+ def parse_args ():
176
+ argparser = argparse .ArgumentParser (description = 'Generate a list of bitcoin node seed ip addresses.' )
177
+ argparser .add_argument ("-a" ,"--asmap" , help = 'the location of the asmap asn database file (required)' , required = True )
178
+ return argparser .parse_args ()
179
+
198
180
def main ():
181
+ args = parse_args ()
182
+
183
+ print (f'Loading asmap database "{ args .asmap } "…' , end = '' , file = sys .stderr , flush = True )
184
+ asmap = ASMap (args .asmap )
185
+ print ('Done.' , file = sys .stderr )
186
+
199
187
lines = sys .stdin .readlines ()
200
188
ips = [parseline (line ) for line in lines ]
201
189
@@ -230,15 +218,18 @@ def main():
230
218
ips = filtermultiport (ips )
231
219
print (f'{ ip_stats (ips ):s} Filter out hosts with multiple bitcoin ports' , file = sys .stderr )
232
220
# Look up ASNs and limit results, both per ASN and globally.
233
- ips = filterbyasn (ips , MAX_SEEDS_PER_ASN , NSEEDS )
221
+ ips = filterbyasn (asmap , ips , MAX_SEEDS_PER_ASN , NSEEDS )
234
222
print (f'{ ip_stats (ips ):s} Look up ASNs and limit results per ASN and per net' , file = sys .stderr )
235
223
# Sort the results by IP address (for deterministic output).
236
224
ips .sort (key = lambda x : (x ['net' ], x ['sortkey' ]))
237
225
for ip in ips :
238
226
if ip ['net' ] == 'ipv6' :
239
- print ('[%s]:%i' % ( ip ['ip' ], ip ['port' ]) )
227
+ print (f"[ { ip ['ip' ]} ]: { ip ['port' ]} " , end = "" )
240
228
else :
241
- print ('%s:%i' % (ip ['ip' ], ip ['port' ]))
229
+ print (f"{ ip ['ip' ]} :{ ip ['port' ]} " , end = "" )
230
+ if 'asn' in ip :
231
+ print (f" # AS{ ip ['asn' ]} " , end = "" )
232
+ print ()
242
233
243
234
if __name__ == '__main__' :
244
235
main ()
0 commit comments