~ubuntu-branches/ubuntu/raring/sshfp/raring-proposed

« back to all changes in this revision

Viewing changes to sshfp

  • Committer: Bazaar Package Importer
  • Author(s): Julien Valroff
  • Date: 2010-10-16 18:35:10 UTC
  • mfrom: (1.1.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20101016183510-z7u34dvt8os5qw30
Tags: 1.1.6-1
* New upstream release
* Drop patches merged upstream
* Add build-depends on xmlto 
* Add patch to use the manpage from the orig tarball 
* Pass --unapply-patches to dpkg-source before build

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
 
7
7
import os
8
8
import sys
 
9
import subprocess
 
10
import optparse
9
11
import getopt
10
12
import base64
11
13
import commands
15
17
        import dns.resolver
16
18
        import dns.query
17
19
        import dns.zone
18
 
except:
19
 
        print "sshfp requires the python-dns package from http://www.pythondns.org/"
20
 
        print "Fedora: yum install python-dns"
21
 
        print "Debian: apt-get install python-dnspython   (NOT python-dns!)"
22
 
        sys.exit()
 
20
except ImportError:
 
21
        print >>sys.stderr, "sshfp requires the python-dns package from http://www.pythondns.org/"
 
22
        print >>sys.stderr, "Fedora: yum install python-dns"
 
23
        print >>sys.stderr, "Debian: apt-get install python-dnspython   (NOT python-dns!)"
 
24
        sys.exit(1)
23
25
 
24
26
try:
25
27
        import hashlib
26
28
        digest = hashlib.sha1
27
 
except:
 
29
except ImportError:
28
30
        import sha
29
31
        digest = sha.new
30
32
 
31
33
 
32
34
global all_hosts
33
35
global khfile
34
 
global version
35
36
global hostnames
36
37
global trailing
37
 
global nameserver
38
38
global quiet
39
39
global port
40
40
global timeout
41
41
global algo
42
42
 
43
 
try:
44
 
        from subprocess import Popen, PIPE
45
 
        use_subprocess = True
46
 
except:
47
 
        use_subprocess = False
48
 
 
49
 
def usage():
50
 
 
51
 
        print "usage: sshfp -k [-d] [-t algo] [-T timeout] [ knownhosts_file ] [-o output] [ [-a] | host1 [host2 ... ] ]"
52
 
        print "       sshfp -s [-d] [-t algo] [-T timeout] [ [-p port] [-a domainname] [-o output] | host1 [host2 .. ] ] [@ns]"
53
 
        print "examples:"
54
 
        print "          sshfp -k www.xelerance.com"
55
 
        print "          sshfp -k ~paul/.ssh/known_hosts -a "
56
 
        print "          sshfp -s www.openswan.org"
57
 
        print "          sshfp -s -p 2222 xelerance.com"
58
 
        print "          sshfp -s -T 30 -t rsa -d -a -o sshfp.txt xelerance.com @ns0.xelerance.net"
59
 
 
60
 
def create_sshfp(hostname,keytype,keyblob):
61
 
        global trailing
 
43
VERSION = "1.1.6"
 
44
DEFAULT_KNOWN_HOSTS_FILE = "~/.ssh/known_hosts"
 
45
 
 
46
def show_version():
 
47
        print >>sys.stderr, "sshfp version: " + VERSION
 
48
        print >>sys.stderr, "Source : http://www.xelerance.com/software/sshfp/"
 
49
        print >>sys.stderr, "Authors:\n Paul Wouters <paul@xelerance.com>\n Jake Appelbaum <jacob@appelbaum.net>"
 
50
        print >>sys.stderr, " James Brown <jbrown@yelp.com>"
 
51
 
 
52
def create_sshfp(hostname, keytype, keyblob):
 
53
        """Creates an SSH fingerprint"""
 
54
 
62
55
        if keytype == "ssh-rsa":
63
56
                keytype = "1"
64
57
        else:
69
62
        try:
70
63
                rawkey = base64.b64decode(keyblob)
71
64
        except TypeError:
72
 
                print "FAILED on hostname "+hostname+" with keyblob "+keyblob
 
65
                print >>sys.stderr, "FAILED on hostname "+hostname+" with keyblob "+keyblob
73
66
                return "ERROR"
74
 
        fpsha1 = digest(rawkey).hexdigest()
 
67
        fpsha1 = digest(rawkey).hexdigest().upper()
75
68
        # check for Reverse entries
76
69
        reverse = 1
77
 
        parts = hostname.split(".",3)
 
70
        parts = hostname.split(".", 3)
78
71
        if parts[0] != hostname:
79
72
                for octet in parts:
80
73
                        if not octet.isdigit():
88
81
                        hostname = hostname + "."
89
82
        return hostname + " IN SSHFP " + keytype + " 1 " + fpsha1
90
83
 
91
 
def sshfpFromFile(khfile,wantedHosts):
 
84
def get_known_host_entry(known_hosts, host):
 
85
        """Get a single entry out of a known_hosts file
 
86
 
 
87
        Uses the ssh-keygen utility."""
 
88
        cmd = ["ssh-keygen", "-f", known_hosts, "-F", host]
 
89
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
90
        (stdout, stderr) = process.communicate()
 
91
        if not quiet and stderr:
 
92
                print >>sys.stderr, stderr
 
93
        outputl = []
 
94
        for e in stdout.split("\n"):
 
95
                if not e.startswith("#"):
 
96
                        outputl.append(e)
 
97
        return "\n".join(outputl)
 
98
 
 
99
def sshfp_from_file(khfile,wantedHosts):
 
100
        global all_hosts
 
101
        global algo
 
102
 
92
103
        # ok, let's do it
93
104
        known_hosts = os.path.expanduser(khfile)
94
105
        try:
95
106
                khfp = open(known_hosts)
96
107
        except IOError:
97
 
                print "Failed to open file "+ known_hosts
98
 
                sys.exit()
99
 
        entries = khfp.read()
 
108
                print >>sys.stderr, "ERROR: failed to open file "+ known_hosts
 
109
                sys.exit(1)
 
110
        hashed_known_hosts = False
 
111
        if khfp.readline().startswith("|1|"):
 
112
                hashed_known_hosts = True
100
113
        khfp.close()
101
 
        return processRaw(entries,wantedHosts)
102
 
 
103
 
def processRaw(entries,wantedHosts):
104
 
        global algo
105
 
        global all_hosts
106
 
        allrecords = []
107
 
        for line in entries.split("\n"):
108
 
                if line != "" and line[0] != "#" and line[0] != "|" and line !="\n":
109
 
                        records = line.split(" ")
110
 
                        hosts = records[0].split(",")
111
 
                        for hostname in hosts:
112
 
                                if all_hosts or (hostname in wantedHosts) or (hostname == wantedHosts):
113
 
                                        try:
114
 
                                                keytype = records[1]
115
 
                                                # note that ssh-keyscan and known_hosts don't use the same string
116
 
                                                # for all algos, eg ssh-dss vs -t dsa, match on all but last char 
117
 
                                                if (algo != "dsa,rsa") and keytype[:-1] != "ssh-%s"%algo[:-1]:
118
 
                                                        return
119
 
                                                key64blob = records[2]
120
 
                                                record = create_sshfp(hostname,keytype,key64blob)
121
 
                                                if record:
122
 
                                                        allrecords.append(record)
123
 
                                        except IndexError:
124
 
                                                pass
125
 
        if allrecords != []:
126
 
                allrecords.sort()
127
 
                # join records, dnssigner wants a newline at end of file, so add one.
128
 
                return "\n".join(allrecords)+"\n"
129
 
        else:
130
 
                return 0
131
 
 
132
 
def getRecord(domain,type):
 
114
 
 
115
        fingerprints = []
 
116
        if hashed_known_hosts and all_hosts:
 
117
                print >>sys.stderr, "ERROR: %s is hashed, cannot use with -a" % known_hosts
 
118
                sys.exit(1)
 
119
        elif hashed_known_hosts: #only looking for some known hosts
 
120
                for host in wantedHosts:
 
121
                        fingerprints.append(process_record(get_known_host_entry(known_hosts, host), host))
 
122
        else:
 
123
                try:
 
124
                        khfp = open(known_hosts)
 
125
                except IOError:
 
126
                        print >>sys.stderr, "ERROR: failed to open file "+ known_hosts
 
127
                        sys.exit(1)
 
128
                data = khfp.read()
 
129
                khfp.close()
 
130
                fingerprints.append(process_records(data, wantedHosts))
 
131
        return "\n".join(fingerprints)
 
132
 
 
133
def check_keytype(keytype):
 
134
        global algos
 
135
        for algo in algos:
 
136
                if "ssh-%s"%algo[:-1] == keytype[:-1]:
 
137
                        return True
 
138
        if not quiet:
 
139
                print >>sys.stderr, "Could only find key type %s for %s" % (keytype, hostname)
 
140
        return False
 
141
 
 
142
def process_record(record, hostname):
 
143
        (host, keytype, key) = record.split(" ")
 
144
        key = key.rstrip()
 
145
        if check_keytype(keytype):
 
146
                record = create_sshfp(hostname, keytype, key)
 
147
                return record
 
148
        return ""
 
149
 
 
150
def process_records(data, hostnames):
 
151
        """Process all records in a string.
 
152
 
 
153
        If the global "all_hosts" is True, then return SSHFP entries
 
154
        for all records with the allowed key types.
 
155
 
 
156
        If "all_hosts is False and hostnames is non-empty, return
 
157
        only the items in hostnames
 
158
        """
 
159
        all_records = []
 
160
        for record in data.split("\n"):
 
161
                if record.startswith("#") or not record:
 
162
                        continue
 
163
                try:
 
164
                        (host, keytype, key) = record.split(" ")
 
165
                except ValueError:
 
166
                        if not quiet:
 
167
                                print >>sys.stdout, "Print unable to read record '%s'" % record
 
168
                        continue
 
169
                if "," in host:
 
170
                        host = host.split(",")[0]
 
171
                if all_hosts or host in hostnames or host == hostnames:
 
172
                        if not check_keytype(keytype):
 
173
                                continue
 
174
                        all_records.append(create_sshfp(host, keytype, key))
 
175
        if all_records:
 
176
                all_records.sort()
 
177
                return "\n".join(all_records)
 
178
        else:
 
179
                return ""
 
180
 
 
181
def get_record(domain, qtype):
133
182
        try:
134
 
                answers = dns.resolver.query(domain, type)
 
183
                answers = dns.resolver.query(domain, qtype)
135
184
        except dns.resolver.NXDOMAIN:
136
185
                #print "NXdomain: "+domain
137
186
                return 0
140
189
                return 0
141
190
        for rdata in answers:
142
191
                # just return first entry we got, answers[0].target does not work
143
 
                if type == "A":
 
192
                if qtype == "A":
144
193
                        return rdata
145
 
                if type == "NS":
 
194
                if qtype == "NS":
146
195
                        return str(rdata.target)
147
196
                else:
148
 
                        print "error in getRecord, unknown type "+type
149
 
                        sys.exit()
 
197
                        print >>sys.stderr, "ERROR: error in get_record, unknown type " + qtype
 
198
                        sys.exit(1)
150
199
 
151
 
def getAXFRrecord(domain,ns):
 
200
def get_axfr_record(domain, nameserver):
152
201
        try:
153
 
                zone = dns.zone.from_xfr(dns.query.xfr(ns,domain))
 
202
                zone = dns.zone.from_xfr(dns.query.xfr(nameserver, domain, rdtype=dns.rdatatype.AXFR))
154
203
        except dns.exception.FormError:
155
204
                raise dns.exception.FormError, domain
156
205
        else:
157
206
                return  zone
158
207
 
159
 
def sshfpFromAXFR(domain,nameserver):
160
 
        if domain[-1] == " ":
161
 
                domain = domain[:-1]
 
208
def sshfp_from_axfr(domain, nameserver):
162
209
        if " " in domain:
163
 
                print "error: space in domain '"+domain+"' can't be right, aborted"
164
 
                sys.exit()
 
210
                print >>sys.stderr, "ERROR: space in domain '"+domain+"' can't be right, aborted"
 
211
                sys.exit(1)
165
212
        if not nameserver:
166
 
                nameserver = getRecord(domain,"NS")
 
213
                nameserver = get_record(domain, "NS")
167
214
                if not nameserver:
168
 
                        print "warning: no NS record found for domain "+domain+". trying as host record instead"
 
215
                        print >>sys.stderr, "WARNING: no NS record found for domain "+domain+". trying as host record instead"
169
216
                        # better then nothing
170
 
                        return sshfpFromDNS(domain)
171
 
        hosts = ""
 
217
                        return sshfp_from_dns([domain])
 
218
        hosts = []
172
219
        #print "nameserver:" + str(ns)
173
220
        try:
174
221
                # print "trying axfr for "+domain+"@"+nameserver
175
 
                axfr = getAXFRrecord(domain,nameserver)
 
222
                axfr = get_axfr_record(domain,nameserver)
176
223
        except dns.exception.FormError, badDomain:
177
 
                print "AXFR error: " + nameserver + " - No permission or not authorative for " + badDomain + "; aborting"
178
 
                sys.exit()
 
224
                print >>sys.stderr, "AXFR error: %s - No permission or not authorative for %s; aborting" % (nameserver, badDomain)
 
225
                sys.exit(1)
179
226
 
180
227
        for (name, ttl, rdata) in axfr.iterate_rdatas('A'):
181
228
                #print "name:" +str(name) +", ttl:"+ str(ttl)+ ", rdata:"+str(rdata)
182
229
                if "@" in str(name): 
183
 
                        hosts = hosts + " " + domain + "."
 
230
                        hosts.append(domain + ".")
184
231
                else:
185
232
                        if not str(name) == "localhost":
186
 
                                hosts = hosts + " " + str(name) + "." + domain + "."
187
 
        return sshfpFromDNS(hosts)
 
233
                                hosts.append("%s.%s." % (name, domain))
 
234
        return sshfp_from_dns(hosts)
188
235
 
189
 
def sshfpFromDNS(hosts):
 
236
def sshfp_from_dns(hosts):
190
237
        global quiet
191
238
        global port
192
 
        if hosts[-1] == " ":
193
 
                hosts = hosts[:-1]
194
239
        global timeout
195
 
        global algo
196
 
        cmd = "ssh-keyscan -p %s -T %s -t %s %s" % (port, timeout, algo, hosts)
197
 
        if quiet:
198
 
                cmd = cmd + " 2>/dev/null"
199
 
 
200
 
        if use_subprocess:
201
 
                p = Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE,
202
 
                        stderr=PIPE, close_fds=True)
203
 
                tochild, fromchild, childerror = (p.stdin, p.stdout, p.stderr)
204
 
        else:
205
 
                tochild, fromchild, childerror = os.popen3(cmd, 'r')
206
 
 
207
 
        err = childerror.readlines()
208
 
        khdns = "\n".join(fromchild.readlines())
209
 
        for e in err:
210
 
                if e[0] != "#":
211
 
                        print >>sys.stderr, e
212
 
        return processRaw(khdns,hosts)
 
240
        global algos
 
241
        cmd = ["ssh-keyscan", "-p", str(port), "-T", str(timeout), "-t", ",".join(algos)] + hosts
 
242
        process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
 
243
        (stdout, stderr) = process.communicate()
 
244
        if not quiet:
 
245
                print >>sys.stderr, stderr
 
246
        khdns = stdout
 
247
        return process_records(khdns,hosts)
213
248
        
214
 
def main(argv=None):
 
249
def main():
215
250
        global all_hosts
216
251
        global trailing
217
 
        global nameserver
218
252
        global quiet
219
253
        global port
220
254
        global timeout
221
 
        global algo
 
255
        global algos
222
256
 
223
 
        if argv is None:
224
 
                argv = sys.argv
225
 
        try:
226
 
                opts, args = getopt.getopt(argv[1:], "qhdvsT:t:a:o:k:p:", ["quiet","help","trailing-dot","version","scan","timeout:","type:","all:","output:","knownhosts:", "port:"])
227
 
        except getopt.error, msg:
228
 
                #print >>sys.stderr, err.msg
229
 
                print >>sys.stderr, "ERROR parsing options"
230
 
                usage()
231
 
                sys.exit(2) 
 
257
        parser = optparse.OptionParser()
 
258
        parser.add_option("-k", "--knownhosts", "--known-hosts", 
 
259
                        action="store",
 
260
                        dest="known_hosts",
 
261
                        metavar="KNOWN_HOSTS_FILE",
 
262
                        default=None,
 
263
                        help="obtain public ssh keys from the known_hosts file KNOWN_HOSTS_FILE")
 
264
        parser.add_option("-s", "--scan", 
 
265
                        action="store_true",
 
266
                        default=False,
 
267
                        dest="scan",
 
268
                        help="scan the listed hosts for public keys using ssh-keyscan")
 
269
        parser.add_option("-a", "--all",
 
270
                        action="store_true",
 
271
                        dest="all_hosts",
 
272
                        help="scan all hosts in the known_hosts file when used with -k. When used with -s, attempt a zone transfer to obtain all A records in the domain provided")
 
273
        parser.add_option("-d", "--trailing-dot",
 
274
                        action="store_true",
 
275
                        dest="trailing_dot",
 
276
                        help="add a trailing dot to the hostname in the SSHFP records")
 
277
        parser.add_option("-o", "--output",
 
278
                        action="store",
 
279
                        metavar="FILENAME",
 
280
                        default=None,
 
281
                        help="write to FILENAME instead of stdout")
 
282
        parser.add_option("-p", "--port",
 
283
                        action="store",
 
284
                        metavar="PORT",
 
285
                        type="int",
 
286
                        default=22,
 
287
                        help="use PORT for scanning")
 
288
        parser.add_option("-v", "--version",
 
289
                        action="store_true",
 
290
                        dest="version",
 
291
                        help="print version information and exit")
 
292
        parser.add_option("-q", "--quiet",
 
293
                        action="store_true",
 
294
                        dest="quiet")
 
295
        parser.add_option("-T", "--timeout",
 
296
                        action="store",
 
297
                        type="int",
 
298
                        dest="timeout",
 
299
                        default=5,
 
300
                        help="scanning timeout (default %default)")
 
301
        parser.add_option("-t", "--type",
 
302
                        action="append",
 
303
                        type="choice",
 
304
                        dest="algo",
 
305
                        choices=["rsa", "dsa"],
 
306
                        default=[],
 
307
                        help="key type to fetch (may be specified more than once, default dsa,rsa)")
 
308
        parser.add_option("-n", "--nameserver",
 
309
                        action="store",
 
310
                        type="string",
 
311
                        dest="nameserver",
 
312
                        default="",
 
313
                        help="nameserver to use for AXFR (only valid with -s -a)")
 
314
        (options, args) = parser.parse_args()
232
315
 
233
316
        # parse options
234
 
        khfile = ""
235
 
        dodns = 0
236
 
        dofile = 0
 
317
        khfile = options.known_hosts
 
318
        dodns = options.scan
237
319
        nameserver = ""
238
320
        domain = ""
239
 
        output = ""
240
 
        quiet = 0
241
 
        version = "1.1.3"
 
321
        output = options.output
 
322
        quiet = options.quiet
242
323
        data = ""
243
 
        trailing = 0
244
 
        timeout = "5"
245
 
        algo = "dsa,rsa"
246
 
        all_hosts = 0
247
 
        port = 22
 
324
        trailing = options.trailing_dot
 
325
        timeout = options.timeout
 
326
        algos = options.algo or ["dsa", "rsa"]
 
327
        all_hosts = options.all_hosts
 
328
        port = options.port
248
329
        hostnames = ()
249
 
        #if not opts and not args:
250
 
        #       usage()
251
 
        #       sys.exit()
252
 
 
253
 
        for o, a in opts:
254
 
                if o in ("-v", "--version"):
255
 
                        print "sshfp version: "+version
256
 
                        print "Authors:\n Paul Wouters <paul@xelerance.com>\n Jake Appelbaum <jacob@appelbaum.net>"
257
 
                        print "Source : http://www.xelerance.com/software/sshfp/"
258
 
                        sys.exit()
259
 
                if o in ("-h", "--help"):
260
 
                        usage()
261
 
                        sys.exit()
262
 
                if o in ("-d", "--trailling-dot"):
263
 
                        trailing = 1
264
 
                if o in ("-T", "--timeout"):
265
 
                        if not a:
266
 
                                print "error: no timeout specified"
267
 
                                sys.exit()
268
 
                        try:
269
 
                                timeout = str(int(a))
270
 
                        except:
271
 
                                print "error: timeout not specified in seconds"
272
 
                                sys.exit()
273
 
                if o in ("-t", "--type"):
274
 
                        if not a:
275
 
                                print "error: no type specified"
276
 
                                sys.exit()
277
 
                        if (a == "rsa") or (a == "dsa"):
278
 
                                algo = a
279
 
                        else:
280
 
                                print "error: invalid type"
281
 
                                sys.exit()
282
 
                if o in ("-q", "--quiet"):
283
 
                        quiet = 1
284
 
                if o in ("-p", "--port"):
285
 
                        if a:
286
 
                                try:
287
 
                                        port = int(a)
288
 
                                        if not quiet and port <> 22:
289
 
                                                print "WARNING: non-standard port numbers are not designated in SSHFP records"
290
 
                                except:
291
 
                                        print "error: port must be a number"
292
 
                                        sys.exit()
293
 
                if o in ("-a", "--all"):
294
 
                        all_hosts = 1
295
 
                        if a:
296
 
                                domain = a
297
 
                if o in ("-o", "--output"):
298
 
                        if not a:
299
 
                                print "error: no output file specified"
300
 
                                sys.exit()
301
 
                        else:
302
 
                                output = a
303
 
                if o in ("-k", "--knownhosts"):
304
 
                        dofile = 1
305
 
                        # optional arguments dont work cleanly in python??
306
 
                        if not a:
307
 
                                khfile = "~/.ssh/known_hosts"
308
 
                        else:
309
 
                                if os.path.isfile(a):
310
 
                                        khfile = a 
311
 
                                else:
312
 
                                        try:
313
 
                                                arec =  getRecord(a,"A")
314
 
                                                if arec:
315
 
                                                        # it's really a hostname argument, not a known_hosts file.
316
 
                                                        args.append(a)
317
 
                                                        khfile = "~/.ssh/known_hosts"
318
 
                                        except:
319
 
                                                # no file and no domain, prob an arg mistaken as option
320
 
                                                if a[0]=="-":
321
 
                                                        khfile = "~/.ssh/known_hosts"
322
 
                                                        opts.append(a)
323
 
                                                        # I guess we can't append opts for processing within
324
 
                                                        # the loop. Guess we need to exec a new sshfp or refactor.
325
 
                                                        # catch most commonly used options, eg "sshfp -k -a"
326
 
                                                        if a == "-a":
327
 
                                                                all_hosts = 1
328
 
                                                        if a == "-t":
329
 
                                                                trailing = 1
330
 
                                                else:
331
 
                                                        print "error: "+a+" is neither a known_hosts file or hostname"
332
 
                                                        sys.exit()
333
 
 
334
 
                if o in ("-s", "--scan"):
335
 
                        dodns = 1
336
 
                        # add any args to -s as arguments
337
 
                        # currently not possible in getopts call
338
 
                        if (a):
339
 
                                args.append(a)
340
 
 
341
 
                # print "DEBUG: opts"                           
342
 
                # print opts
343
 
                # print "DEBUG: args"                           
344
 
                # print args
345
 
 
346
 
        if (not dodns and not dofile):
 
330
 
 
331
        if options.version:
 
332
                show_version()
 
333
                sys.exit(0)
 
334
        if not quiet and port <> 22:
 
335
                print >>sys.stderr, "WARNING: non-standard port numbers are not designated in SSHFP records"
 
336
        if not quiet and options.known_hosts and options.scan:
 
337
                print >>sys.stderr, "WARNING: Ignoring -k option, -s was passwd"
 
338
        if options.nameserver and not options.scan and not options.all_hosts:
 
339
                print >>sys.stderr, "ERROR: Cannot specify -n without -s and -a"
 
340
                sys.exit(1)
 
341
        if not options.scan and options.all_hosts and args:
 
342
                print >>sys.stderr, "WARNING: -a and hosts both passed, ignoring manual host list"
 
343
        if not args:
 
344
                print >>sys.stderr, "WARNING: Assuming -a"
 
345
                all_hosts = True
 
346
        if not options.scan:
 
347
                khfile = DEFAULT_KNOWN_HOSTS_FILE
 
348
 
 
349
        if options.scan and options.all_hosts:
 
350
                datal = []
 
351
                for arg in args:
 
352
                        datal.append(sshfp_from_axfr(arg, options.nameserver))
 
353
                if not quiet:
 
354
                        datal.insert(0, ";")
 
355
                        datal.insert(0, "; Generated by sshfp %s from %s at %s" % (VERSION, nameserver, time.ctime()))
 
356
                        datal.insert(0, ";")
 
357
                data = "\n".join(datal)
 
358
        elif options.scan:      # Scan specified hosts
347
359
                if not args:
348
 
                        # use default
349
 
                        all_hosts = 1
350
 
                        dofile = 1
351
 
                        trailing = 1
352
 
                        if not khfile:
353
 
                                khfile = "~/.ssh/known_hosts"
354
 
                else:
355
 
                        dodns = 1
356
 
 
357
 
        if (dodns and dofile):
358
 
                        print "use either -k or -s"
359
 
                        usage()
360
 
                        sys.exit()
361
 
                
362
 
        if dodns:
363
 
 
364
 
                # filter special case for using @nameserver, verify for misinterpreted options as args
365
 
                newargs = ""
366
 
                for arg in args:
367
 
                        if arg != "":
368
 
                                if arg[0]=="@":
369
 
                                        #print "found ns:"+arg[1:]
370
 
                                        nameserver = arg[1:]
371
 
                                        if not all_hosts:
372
 
                                                print "WARNING: ssh-keyscan does not support @nameserver syntax, ignoring"
373
 
                                else:
374
 
                                        newargs = newargs + arg +" "
375
 
                                if arg[0]=="-":
376
 
                                        # shit, misinterpreted option as argument. We'll try to be more clever in the future.
377
 
                                        usage()
378
 
                                        sys.exit()
379
 
                if not newargs:
380
 
                        print "error: No hostnames specified"
381
 
                        sys.exit()
382
 
                if all_hosts:
383
 
                                data = sshfpFromAXFR(newargs,nameserver)
384
 
                                if not quiet:
385
 
                                        data = ";\n; Generated by sshfp "+ version +" from " + nameserver + " at "+ time.ctime() +"\n;\n" + data
386
 
                else:
387
 
                        data = sshfpFromDNS(newargs)
388
 
 
389
 
        if dofile:
390
 
                data = sshfpFromFile(khfile,args)
391
 
 
392
 
        if not data:
393
 
                sys.exit()
 
360
                        print >>sys.stderr, "ERROR: You asked me to scan, but didn't give any hosts to scan"
 
361
                        sys.exit(1)
 
362
                data = sshfp_from_dns(args)
 
363
        else: # known_hosts
 
364
                data = sshfp_from_file(khfile, args)
394
365
 
395
366
        if output:
396
367
                try:
397
 
                        fp = open(output,"w")
 
368
                        fp = open(output, "w")
398
369
                except IOError:
399
 
                        print "error: can't open '"+output+"' for writing"
400
 
                        sys.exit()
401
 
                else:
 
370
                        print >>sys.stderr, "ERROR: can't open '%s'' for writing" % output
 
371
                        sys.exit(1)
 
372
                else:
402
373
                        fp.write(data)
403
374
                        fp.close()
404
375
        else:
405
 
                print data[:-1]
 
376
                print data
406
377
 
407
378
if __name__ == "__main__":
408
 
        sys.exit(main())
 
379
        main()