summaryrefslogtreecommitdiff
path: root/extcap/ssh-dumpcap
blob: 55c9f8d89774a16c0cd0635318ee9556c8f8cde5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
#!/usr/bin/env python3
# Capture from a remote server over SSH which has dumpcap.
#
# After copying this file to the extcap folder (see Help -> About
# for location), you can capture with:
#
#   wireshark -i ssh-dumpcap -k \
#       -oextcap.ssh_dumpcap.remotehost:user@host \
#       -oextcap.ssh_dumpcap.remoteinterface:nflog:0x1234

#from __future__ import print_function

import argparse, os, sys
from shlex import quote
from signal import SIGINT

parser = argparse.ArgumentParser()

# Actions
parser.add_argument('--extcap-interfaces', action='store_true')
parser.add_argument('--extcap-dlts', action='store_true')
parser.add_argument('--extcap-config', action='store_true')
parser.add_argument('--capture', action='store_true')
parser.add_argument('--extcap-version')

parser.add_argument('--extcap-interface', metavar='IFACE')

parser.add_argument('--extcap-capture-filter', metavar='CFILTER')
parser.add_argument('--fifo', metavar='FIFO')

parser.add_argument('--remote-host', metavar='HOST')
parser.add_argument('--remote-interface', metavar='IFACE')

def extcap_interfaces():
    print("extcap {version=1.0}")
    print("interface {value=ssh-dumpcap}{display=Remote dumpcap}")

def extcap_dlts(iface):
    # Required for the interface to show up in the interace
    print("dlt {number=147}{name=USER0}{display=Remote capture dependent DLT}")

def extcap_config(iface):
    print("""
arg {number=0}{call=--remote-host}{display=Remote SSH server address}{type=string}{tooltip=The remote SSH host. It can be both an IP address or a hostname}{required=true}{group=Server}
arg {number=1}{call=--remote-interface}{display=Remote interface}{type=string}{tooltip=The remote network interface used for capture}{group=Capture}
""".strip())

def redirect_stdout(outfile):
    STDOUT_FILENO = 1
    try: os.close(STDOUT_FILENO)
    except OSError: pass
    fd = os.open(outfile, os.O_WRONLY, 0o600)
    # TODO this requires Py 3.4, maybe old version can use dup2
    os.set_inheritable(fd, True)
    if fd != STDOUT_FILENO:
        os.dup2(fd, STDOUT_FILENO)
        os.close(fd)

def extcap_capture(iface, ssh_host, cfilter, outfile):
    tcpdump_args = [
        "dumpcap",
        "-i", iface,
        "-p",
        "-q",
        "-w", "-",
    ]
    if cfilter:
        tcpdump_args += [cfilter]
    args = [
        "ssh", ssh_host,
        " ".join(quote(c) for c in tcpdump_args),
    ]
    #import subprocess; subprocess.call(["ls", "-l", "/proc/%d/fd/" % os.getpid()], stdout=2)
    redirect_stdout(outfile)
    os.execvp(args[0], args)

def main():
    args = parser.parse_args()
    if args.extcap_interfaces:
        return extcap_interfaces()

    if not args.extcap_interface:
        parser.error('Missing --extcap-interface option')
    #if args.extcap_interface != "ssh-dumpcap":
    #    parser.error('only ssh-dumpcap is supported as interface name')

    iface = args.remote_interface

    if args.extcap_dlts:
        return extcap_dlts(iface)
    elif args.extcap_config:
        return extcap_config(iface)
    elif args.capture:
        if not args.fifo:
            parser.error('Missing --fifo option for --capture')
        return extcap_capture(iface, args.remote_host, args.extcap_capture_filter, args.fifo)
    else:
        parser.error('Missing action')
        return 1

if __name__ == '__main__':
    try:
        sys.exit(main())
    except KeyboardInterrupt:
        sys.exit(128 + SIGINT)
    except OSError as e:
        print(e, file=sys.stderr)
        sys.exit(1)