summaryrefslogtreecommitdiff
path: root/extcap/ssh-tcpdump
blob: d04b5e0d79019af418c0dd579b04379e990e86b5 (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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
#!/usr/bin/env python3
# Capture from a remote server over SSH which has tcpdump but no dumpcap.
#
# After copying this file to the extcap folder (see Help -> About
# for location), you can use the name "ssh-tcpdump:" followed by the interface
# name:
#
#   SSHHOST=peterw@someserver wireshark -i ssh-tcpdump:eth0 -p -k
#
# Note: interface names are currently hard-coded (lo and eth0).

#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')

IFACE_PREFIX = 'ssh-tcpdump:'
# TODO currently hard-coded interfaces, maybe add preferences?
ifaces = [
    'lo',
    'eth0',
]

def extcap_interfaces():
    print("extcap {version=1.0}")
    for iface in ifaces:
        print("interface {value=%s%s}{display=Remote tcpdump: %s}" % (
            IFACE_PREFIX, iface, iface
        ))

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}")

# TODO consider configuration option for setting the SSH host?
def extcap_config(iface):
    pass

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, cfilter, outfile):
    ssh_host = os.getenv("SSHHOST")
    if not ssh_host:
        raise RuntimeError("Missing SSHHOST")
    ssh_user = os.getenv('SSHUSER')
    if not ssh_user:
        if '@' in ssh_host:
            ssh_user = ssh_host.split('@')[0]
        else:
            ssh_user = os.getenv('USER')
    tcpdump_args = [
        "tcpdump",
        "-i", iface,
        "-p",
        "-U",
        "-w", "-",
    ]
    if ssh_user != 'root':
        tcpdump_args = ["sudo"] + tcpdump_args
    # Change to a less-privileged user
    if ssh_user:
        tcpdump_args += ["-Z", ssh_user]
    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')

    iface = args.extcap_interface[len(IFACE_PREFIX):]

    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.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)