cowrie 2.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- backend_pool/README.md +43 -0
- backend_pool/__init__.py +0 -0
- backend_pool/libvirt/__init__.py +0 -0
- backend_pool/libvirt/backend_service.py +216 -0
- backend_pool/libvirt/guest_handler.py +112 -0
- backend_pool/libvirt/network_handler.py +100 -0
- backend_pool/libvirt/snapshot_handler.py +33 -0
- backend_pool/nat.py +127 -0
- backend_pool/pool_server.py +225 -0
- backend_pool/pool_service.py +481 -0
- backend_pool/ssh_exec.py +109 -0
- backend_pool/telnet_exec.py +156 -0
- backend_pool/util.py +81 -0
- cowrie/__init__.py +12 -0
- cowrie/_version.py +34 -0
- cowrie/commands/__init__.py +61 -0
- cowrie/commands/adduser.py +104 -0
- cowrie/commands/apt.py +219 -0
- cowrie/commands/awk.py +233 -0
- cowrie/commands/base.py +1117 -0
- cowrie/commands/base64.py +150 -0
- cowrie/commands/bash.py +83 -0
- cowrie/commands/busybox.py +107 -0
- cowrie/commands/cat.py +139 -0
- cowrie/commands/chmod.py +145 -0
- cowrie/commands/chpasswd.py +102 -0
- cowrie/commands/crontab.py +78 -0
- cowrie/commands/curl.py +470 -0
- cowrie/commands/dd.py +146 -0
- cowrie/commands/dig.py +202 -0
- cowrie/commands/du.py +131 -0
- cowrie/commands/env.py +36 -0
- cowrie/commands/ethtool.py +82 -0
- cowrie/commands/find.py +114 -0
- cowrie/commands/finger.py +129 -0
- cowrie/commands/free.py +114 -0
- cowrie/commands/fs.py +661 -0
- cowrie/commands/ftpget.py +247 -0
- cowrie/commands/gcc.py +309 -0
- cowrie/commands/git.py +172 -0
- cowrie/commands/groups.py +92 -0
- cowrie/commands/ifconfig.py +82 -0
- cowrie/commands/iptables.py +482 -0
- cowrie/commands/last.py +49 -0
- cowrie/commands/locate.py +99 -0
- cowrie/commands/ls.py +231 -0
- cowrie/commands/lspci.py +44 -0
- cowrie/commands/nc.py +135 -0
- cowrie/commands/netstat.py +205 -0
- cowrie/commands/nohup.py +26 -0
- cowrie/commands/perl.py +125 -0
- cowrie/commands/ping.py +126 -0
- cowrie/commands/python.py +135 -0
- cowrie/commands/scp.py +226 -0
- cowrie/commands/service.py +121 -0
- cowrie/commands/sleep.py +45 -0
- cowrie/commands/ssh.py +152 -0
- cowrie/commands/sudo.py +137 -0
- cowrie/commands/tar.py +101 -0
- cowrie/commands/tee.py +157 -0
- cowrie/commands/tftp.py +138 -0
- cowrie/commands/ulimit.py +47 -0
- cowrie/commands/uname.py +183 -0
- cowrie/commands/uniq.py +87 -0
- cowrie/commands/unzip.py +132 -0
- cowrie/commands/uptime.py +24 -0
- cowrie/commands/wc.py +136 -0
- cowrie/commands/wget.py +368 -0
- cowrie/commands/which.py +32 -0
- cowrie/commands/yum.py +330 -0
- cowrie/core/__init__.py +0 -0
- cowrie/core/artifact.py +97 -0
- cowrie/core/auth.py +264 -0
- cowrie/core/cef.py +117 -0
- cowrie/core/checkers.py +147 -0
- cowrie/core/config.py +74 -0
- cowrie/core/credentials.py +101 -0
- cowrie/core/network.py +113 -0
- cowrie/core/output.py +245 -0
- cowrie/core/realm.py +57 -0
- cowrie/core/ttylog.py +92 -0
- cowrie/core/utils.py +135 -0
- cowrie/data/arch/bsd-aarch64-lsb +0 -0
- cowrie/data/arch/bsd-aarch64-msb +0 -0
- cowrie/data/arch/bsd-bfin-msb +0 -0
- cowrie/data/arch/bsd-mips-lsb +0 -0
- cowrie/data/arch/bsd-mips-msb +0 -0
- cowrie/data/arch/bsd-mips64-lsb +0 -0
- cowrie/data/arch/bsd-mips64-msb +0 -0
- cowrie/data/arch/bsd-powepc-msb +0 -0
- cowrie/data/arch/bsd-powepc64-lsb +0 -0
- cowrie/data/arch/bsd-riscv64-lsb +0 -0
- cowrie/data/arch/bsd-sparc-msb +0 -0
- cowrie/data/arch/bsd-sparc64-msb +0 -0
- cowrie/data/arch/bsd-x32-lsb +0 -0
- cowrie/data/arch/bsd-x64-lsb +0 -0
- cowrie/data/arch/linux-aarch64-lsb +0 -0
- cowrie/data/arch/linux-aarch64-msb +0 -0
- cowrie/data/arch/linux-alpha-lsb +0 -0
- cowrie/data/arch/linux-am33-lsb +0 -0
- cowrie/data/arch/linux-arc-lsb +0 -0
- cowrie/data/arch/linux-arc-msb +0 -0
- cowrie/data/arch/linux-arm-lsb +0 -0
- cowrie/data/arch/linux-arm-msb +0 -0
- cowrie/data/arch/linux-avr32-lsb +0 -0
- cowrie/data/arch/linux-bfin-lsb +0 -0
- cowrie/data/arch/linux-c6x-lsb +0 -0
- cowrie/data/arch/linux-c6x-msb +0 -0
- cowrie/data/arch/linux-cris-lsb +0 -0
- cowrie/data/arch/linux-frv-msb +0 -0
- cowrie/data/arch/linux-h8300-msb +0 -0
- cowrie/data/arch/linux-hppa-msb +0 -0
- cowrie/data/arch/linux-hppa64-msb +0 -0
- cowrie/data/arch/linux-ia64-lsb +0 -0
- cowrie/data/arch/linux-m32r-msb +0 -0
- cowrie/data/arch/linux-m68k-msb +0 -0
- cowrie/data/arch/linux-microblaze-msb +0 -0
- cowrie/data/arch/linux-mips-lsb +0 -0
- cowrie/data/arch/linux-mips-msb +0 -0
- cowrie/data/arch/linux-mips64-lsb +0 -0
- cowrie/data/arch/linux-mips64-msb +0 -0
- cowrie/data/arch/linux-mn10300-lsb +0 -0
- cowrie/data/arch/linux-nios-lsb +0 -0
- cowrie/data/arch/linux-nios-msb +0 -0
- cowrie/data/arch/linux-powerpc-lsb +0 -0
- cowrie/data/arch/linux-powerpc-msb +0 -0
- cowrie/data/arch/linux-powerpc64-lsb +0 -0
- cowrie/data/arch/linux-powerpc64-msb +0 -0
- cowrie/data/arch/linux-riscv64-lsb +0 -0
- cowrie/data/arch/linux-s390x-msb +0 -0
- cowrie/data/arch/linux-sh-lsb +0 -0
- cowrie/data/arch/linux-sh-msb +0 -0
- cowrie/data/arch/linux-sparc-msb +0 -0
- cowrie/data/arch/linux-sparc64-msb +0 -0
- cowrie/data/arch/linux-tilegx-lsb +0 -0
- cowrie/data/arch/linux-tilegx-msb +0 -0
- cowrie/data/arch/linux-tilegx64-lsb +0 -0
- cowrie/data/arch/linux-tilegx64-msb +0 -0
- cowrie/data/arch/linux-x64-lsb +0 -0
- cowrie/data/arch/linux-x86-lsb +0 -0
- cowrie/data/arch/linux-xtensa-msb +0 -0
- cowrie/data/arch/osx-x32-lsb +0 -0
- cowrie/data/arch/osx-x64-lsb +0 -0
- cowrie/data/arch/src/README +10 -0
- cowrie/data/arch/src/nop-arc.asm +4 -0
- cowrie/data/arch/src/nop-bfin.asm +4 -0
- cowrie/data/arch/src/nop-hppa64.asm +4 -0
- cowrie/data/arch/src/nop-ia64.asm +4 -0
- cowrie/data/arch/src/nop-mips.asm +4 -0
- cowrie/data/arch/src/nop-mips64.asm +4 -0
- cowrie/data/arch/src/nop-osx.asm +4 -0
- cowrie/data/arch/src/nop.asm +4 -0
- cowrie/data/cmdoutput.json +955 -0
- cowrie/data/fs.pickle +0 -0
- cowrie/data/honeyfs/etc/group +44 -0
- cowrie/data/honeyfs/etc/host.conf +1 -0
- cowrie/data/honeyfs/etc/hostname +1 -0
- cowrie/data/honeyfs/etc/hosts +7 -0
- cowrie/data/honeyfs/etc/inittab +69 -0
- cowrie/data/honeyfs/etc/issue +2 -0
- cowrie/data/honeyfs/etc/issue.net +2 -0
- cowrie/data/honeyfs/etc/motd +7 -0
- cowrie/data/honeyfs/etc/passwd +21 -0
- cowrie/data/honeyfs/etc/resolv.conf +2 -0
- cowrie/data/honeyfs/etc/shadow +21 -0
- cowrie/data/honeyfs/proc/cpuinfo +50 -0
- cowrie/data/honeyfs/proc/meminfo +42 -0
- cowrie/data/honeyfs/proc/modules +92 -0
- cowrie/data/honeyfs/proc/mounts +14 -0
- cowrie/data/honeyfs/proc/net/arp +3 -0
- cowrie/data/honeyfs/proc/version +1 -0
- cowrie/data/pool_configs/aarch64_guest.xml +64 -0
- cowrie/data/pool_configs/default_filter.xml +33 -0
- cowrie/data/pool_configs/default_guest.xml +66 -0
- cowrie/data/pool_configs/default_network.xml +12 -0
- cowrie/data/pool_configs/wrt_arm_guest.xml +44 -0
- cowrie/data/pool_configs/wrt_x86_guest.xml +81 -0
- cowrie/data/txtcmds/bin/df +7 -0
- cowrie/data/txtcmds/bin/dmesg +572 -0
- cowrie/data/txtcmds/bin/enable +58 -0
- cowrie/data/txtcmds/bin/izconfig +1 -0
- cowrie/data/txtcmds/bin/mount +7 -0
- cowrie/data/txtcmds/bin/stty +0 -0
- cowrie/data/txtcmds/bin/sync +0 -0
- cowrie/data/txtcmds/bin/ulimit +16 -0
- cowrie/data/txtcmds/usr/bin/clear +1 -0
- cowrie/data/txtcmds/usr/bin/emacs +1 -0
- cowrie/data/txtcmds/usr/bin/getconf +1 -0
- cowrie/data/txtcmds/usr/bin/killall +1 -0
- cowrie/data/txtcmds/usr/bin/locate +1 -0
- cowrie/data/txtcmds/usr/bin/lscpu +21 -0
- cowrie/data/txtcmds/usr/bin/make +1 -0
- cowrie/data/txtcmds/usr/bin/nano +1 -0
- cowrie/data/txtcmds/usr/bin/nproc +1 -0
- cowrie/data/txtcmds/usr/bin/pico +1 -0
- cowrie/data/txtcmds/usr/bin/pkill +0 -0
- cowrie/data/txtcmds/usr/bin/top +1 -0
- cowrie/data/txtcmds/usr/bin/vi +1 -0
- cowrie/data/txtcmds/usr/bin/vim +1 -0
- cowrie/data/txtcmds/usr/sbin/vipw +2 -0
- cowrie/insults/__init__.py +0 -0
- cowrie/insults/insults.py +246 -0
- cowrie/llm/README.md +60 -0
- cowrie/llm/llm.py +222 -0
- cowrie/output/README.md +16 -0
- cowrie/output/abuseipdb.py +496 -0
- cowrie/output/axiom.py +58 -0
- cowrie/output/crashreporter.py +79 -0
- cowrie/output/csirtg.py +99 -0
- cowrie/output/cuckoo.py +173 -0
- cowrie/output/datadog.py +67 -0
- cowrie/output/discord.py +46 -0
- cowrie/output/dshield.py +174 -0
- cowrie/output/elasticsearch.py +125 -0
- cowrie/output/graylog.py +61 -0
- cowrie/output/greynoise.py +95 -0
- cowrie/output/hpfeeds3.py +118 -0
- cowrie/output/influx.py +217 -0
- cowrie/output/jsonlog.py +81 -0
- cowrie/output/localsyslog.py +74 -0
- cowrie/output/malshare.py +104 -0
- cowrie/output/misp.py +590 -0
- cowrie/output/mongodb.py +132 -0
- cowrie/output/mysql.py +291 -0
- cowrie/output/oraclecloud.py +118 -0
- cowrie/output/postgresql.py +268 -0
- cowrie/output/prometheus.py +197 -0
- cowrie/output/redis.py +62 -0
- cowrie/output/remotesyslog.py +45 -0
- cowrie/output/rethinkdblog.py +47 -0
- cowrie/output/reversedns.py +114 -0
- cowrie/output/rmq.py +134 -0
- cowrie/output/s3.py +98 -0
- cowrie/output/slack.py +209 -0
- cowrie/output/socketlog.py +42 -0
- cowrie/output/splunk.py +120 -0
- cowrie/output/sqlite.py +209 -0
- cowrie/output/telegram.py +64 -0
- cowrie/output/textlog.py +59 -0
- cowrie/output/virustotal.py +500 -0
- cowrie/output/xmpp.py +97 -0
- cowrie/pool_interface/__init__.py +0 -0
- cowrie/pool_interface/client.py +126 -0
- cowrie/pool_interface/handler.py +73 -0
- cowrie/python/__init__.py +0 -0
- cowrie/python/logfile.py +54 -0
- cowrie/scripts/__init__.py +0 -0
- cowrie/scripts/asciinema.py +136 -0
- cowrie/scripts/createdynamicprocess.py +62 -0
- cowrie/scripts/createfs.py +197 -0
- cowrie/scripts/fsctl.py +781 -0
- cowrie/scripts/playlog.py +143 -0
- cowrie/shell/__init__.py +0 -0
- cowrie/shell/avatar.py +79 -0
- cowrie/shell/command.py +210 -0
- cowrie/shell/customparser.py +62 -0
- cowrie/shell/filetransfer.py +235 -0
- cowrie/shell/fs.py +626 -0
- cowrie/shell/honeypot.py +699 -0
- cowrie/shell/protocol.py +392 -0
- cowrie/shell/pwd.py +223 -0
- cowrie/shell/server.py +92 -0
- cowrie/shell/session.py +93 -0
- cowrie/shell/stdio.py +90 -0
- cowrie/ssh/__init__.py +0 -0
- cowrie/ssh/channel.py +113 -0
- cowrie/ssh/connection.py +68 -0
- cowrie/ssh/factory.py +186 -0
- cowrie/ssh/forwarding.py +188 -0
- cowrie/ssh/keys.py +129 -0
- cowrie/ssh/session.py +81 -0
- cowrie/ssh/transport.py +288 -0
- cowrie/ssh/userauth.py +185 -0
- cowrie/ssh_proxy/README.md +44 -0
- cowrie/ssh_proxy/__init__.py +0 -0
- cowrie/ssh_proxy/client_transport.py +184 -0
- cowrie/ssh_proxy/protocols/__init__.py +0 -0
- cowrie/ssh_proxy/protocols/base_protocol.py +89 -0
- cowrie/ssh_proxy/protocols/exec_term.py +104 -0
- cowrie/ssh_proxy/protocols/port_forward.py +42 -0
- cowrie/ssh_proxy/protocols/sftp.py +374 -0
- cowrie/ssh_proxy/protocols/ssh.py +437 -0
- cowrie/ssh_proxy/protocols/term.py +226 -0
- cowrie/ssh_proxy/server_transport.py +434 -0
- cowrie/ssh_proxy/userauth.py +37 -0
- cowrie/ssh_proxy/util.py +17 -0
- cowrie/telnet/__init__.py +0 -0
- cowrie/telnet/factory.py +78 -0
- cowrie/telnet/session.py +165 -0
- cowrie/telnet/transport.py +114 -0
- cowrie/telnet/userauth.py +152 -0
- cowrie/telnet_proxy/README.md +14 -0
- cowrie/telnet_proxy/__init__.py +0 -0
- cowrie/telnet_proxy/client_transport.py +70 -0
- cowrie/telnet_proxy/handler.py +341 -0
- cowrie/telnet_proxy/server_transport.py +204 -0
- cowrie/test/__init__.py +0 -0
- cowrie/test/fake_server.py +56 -0
- cowrie/test/fake_transport.py +182 -0
- cowrie/test/proxy_compare.py +101 -0
- cowrie/test/test_awk.py +54 -0
- cowrie/test/test_base64.py +43 -0
- cowrie/test/test_base_commands.py +334 -0
- cowrie/test/test_cat.py +51 -0
- cowrie/test/test_chmod.py +118 -0
- cowrie/test/test_echo.py +224 -0
- cowrie/test/test_ftpget.py +52 -0
- cowrie/test/test_ls.py +57 -0
- cowrie/test/test_network.py +84 -0
- cowrie/test/test_proxy.py +105 -0
- cowrie/test/test_tee.py +63 -0
- cowrie/test/test_tftp.py +37 -0
- cowrie/test/test_uniq.py +50 -0
- cowrie/test/test_utils.py +90 -0
- cowrie-2.7.0.data/scripts/cowrie +240 -0
- cowrie-2.7.0.dist-info/METADATA +190 -0
- cowrie-2.7.0.dist-info/RECORD +322 -0
- cowrie-2.7.0.dist-info/WHEEL +5 -0
- cowrie-2.7.0.dist-info/entry_points.txt +5 -0
- cowrie-2.7.0.dist-info/licenses/LICENSE.rst +31 -0
- cowrie-2.7.0.dist-info/top_level.txt +3 -0
- twisted/plugins/cowrie_plugin.py +255 -0
backend_pool/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
QEMU/libvirt Python examples to handle a guest
|
|
2
|
+
|
|
3
|
+
# Developer Guide
|
|
4
|
+
We'll start by looking at the classes that compose the Backend Pool, from "outermost" to the inner, specific classes.
|
|
5
|
+
|
|
6
|
+
## pool_server.py
|
|
7
|
+
The main interface of the backend pool is exposed as a TCP server in _pool\_server.py_. The protocol is a very simple
|
|
8
|
+
wire protocol, always composed of an op-code, a status code (for responses), and any needed data thereafter.
|
|
9
|
+
|
|
10
|
+
## pool_service.py
|
|
11
|
+
The server interfaces exposes a producer-consumer infinite loop that runs on _pool\_service.py_.
|
|
12
|
+
|
|
13
|
+
The **producer** is an infinite loop started by the server, and runs every 5 seconds. It creates VMs up to the
|
|
14
|
+
configured limit, checks which VMs become available (by testing if they accept SSH and/or Telnet connections), and
|
|
15
|
+
destroys VMs that are no longer needed.
|
|
16
|
+
|
|
17
|
+
**Consumer** methods are called by server request, and basically involve requesting and freeing VMs. All operations on
|
|
18
|
+
shared data in the producer-consumer are guarded by a lock, since there may be concurrent requests. The lock protects
|
|
19
|
+
the _guests_ list, which contains references for each VM backend (in our case libvirt/QEMU instances).
|
|
20
|
+
|
|
21
|
+
Since we won't be dealing with a very large number of VMs (never more than 100, we find that a single simple lock is
|
|
22
|
+
enough.
|
|
23
|
+
|
|
24
|
+
The Pool Service expects to find a "backend service" with a given interface:
|
|
25
|
+
* A method to initialise the backend interface and environment (start_backend), stop it and destroy the current
|
|
26
|
+
environment (stop_backend), and shutdown it permanently for the current execution (shutdown_backend).
|
|
27
|
+
* A method to create a new guest (create_guest)
|
|
28
|
+
* A method to destroy a guest (destroy_guest)
|
|
29
|
+
|
|
30
|
+
Currently the service supports a libvirt/QEMU backend. However, by splitting the logic from generic guest handling /
|
|
31
|
+
interaction with main Cowrie, from the logic to create guests in a low-level perspective, we hope to ease development
|
|
32
|
+
of different kinds of backends in the future.
|
|
33
|
+
|
|
34
|
+
## libvirt classes
|
|
35
|
+
The main class for libvirt is _backend\_service.py_, and implements the interface discussed above. Guest, network and
|
|
36
|
+
snapshot handlers deal with those specific components of libvirt's handling.
|
|
37
|
+
|
|
38
|
+
Initialising libvirt involves connecting to the running system daemon, creating a network filter to restrict guest's
|
|
39
|
+
Internet access, and creating a "cowrie" network in libvirt.
|
|
40
|
+
|
|
41
|
+
Guest creation is started by creating a snapshot from the base qcow2 image defined in the configs, and instantiating
|
|
42
|
+
a guest from the XML provided. The Guest Handler replaces templates ("{guest_name}") with user configs for the wanted
|
|
43
|
+
guest. If the XML provided does not contain templates, then no replacement takes place, naturally.
|
backend_pool/__init__.py
ADDED
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
"""
|
|
2
|
+
backend service
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
|
|
6
|
+
# See the COPYRIGHT file for more information
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import random
|
|
12
|
+
import sys
|
|
13
|
+
import uuid
|
|
14
|
+
from typing import TYPE_CHECKING
|
|
15
|
+
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from collections.abc import Callable
|
|
18
|
+
|
|
19
|
+
from twisted.python import log
|
|
20
|
+
|
|
21
|
+
from cowrie.core.config import CowrieConfig
|
|
22
|
+
|
|
23
|
+
import backend_pool.libvirt.guest_handler
|
|
24
|
+
import backend_pool.libvirt.network_handler
|
|
25
|
+
import backend_pool.util
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LibvirtError(Exception):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class LibvirtBackendService:
|
|
33
|
+
def __init__(self) -> None:
|
|
34
|
+
# lazy import to avoid exception if not using the backend_pool
|
|
35
|
+
# and libvirt not installed (#1185)
|
|
36
|
+
import libvirt
|
|
37
|
+
|
|
38
|
+
libvirt_uri: str = CowrieConfig.get(
|
|
39
|
+
"backend_pool", "libvirt_uri", fallback="qemu:///system"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# open connection to libvirt
|
|
43
|
+
self.conn = libvirt.open(libvirt_uri)
|
|
44
|
+
if self.conn is None:
|
|
45
|
+
log.msg(
|
|
46
|
+
eventid="cowrie.backend_pool.libvirtd",
|
|
47
|
+
format="Failed to open connection to libvirtd at %(uri)s",
|
|
48
|
+
uri=libvirt_uri,
|
|
49
|
+
)
|
|
50
|
+
raise LibvirtError()
|
|
51
|
+
|
|
52
|
+
self.filter = None
|
|
53
|
+
self.network = None
|
|
54
|
+
|
|
55
|
+
# signals backend is ready to be operated
|
|
56
|
+
self.ready: bool = False
|
|
57
|
+
|
|
58
|
+
# table to associate IPs and MACs
|
|
59
|
+
seed: int = random.randint(0, sys.maxsize)
|
|
60
|
+
self.network_table = backend_pool.util.generate_network_table(seed)
|
|
61
|
+
|
|
62
|
+
log.msg(
|
|
63
|
+
eventid="cowrie.backend_pool.libvirtd",
|
|
64
|
+
format="Connection to libvirtd established at %(uri)s",
|
|
65
|
+
uri=libvirt_uri,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def start_backend(self) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Initialises QEMU/libvirt environment needed to run guests.
|
|
71
|
+
Namely starts networks and network filters.
|
|
72
|
+
"""
|
|
73
|
+
# create a network filter
|
|
74
|
+
self.filter = backend_pool.libvirt.network_handler.create_filter(self.conn)
|
|
75
|
+
|
|
76
|
+
# create a network for the guests (as a NAT)
|
|
77
|
+
self.network = backend_pool.libvirt.network_handler.create_network(
|
|
78
|
+
self.conn, self.network_table
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# service is ready to be used (create guests and use them)
|
|
82
|
+
self.ready = True
|
|
83
|
+
|
|
84
|
+
def stop_backend(self) -> None:
|
|
85
|
+
log.msg(
|
|
86
|
+
eventid="cowrie.backend_pool.libvirtd",
|
|
87
|
+
format="Doing libvirtd clean shutdown...",
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
self.ready = False
|
|
91
|
+
|
|
92
|
+
self.destroy_all_cowrie()
|
|
93
|
+
|
|
94
|
+
def shutdown_backend(self) -> None:
|
|
95
|
+
self.conn.close() # close libvirt connection
|
|
96
|
+
|
|
97
|
+
log.msg(
|
|
98
|
+
eventid="cowrie.backend_pool.libvirtd",
|
|
99
|
+
format="Connection to libvirtd closed successfully",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def get_mac_ip(self, ip_tester: Callable[[str], bool]) -> tuple[str, str]:
|
|
103
|
+
"""
|
|
104
|
+
Get a MAC and IP that are not being used by any guest.
|
|
105
|
+
"""
|
|
106
|
+
# Try to find a free pair 500 times.
|
|
107
|
+
retries = 0
|
|
108
|
+
while retries < 500:
|
|
109
|
+
mac = random.choice(list(self.network_table.keys()))
|
|
110
|
+
ip = self.network_table[mac]
|
|
111
|
+
if ip_tester(ip):
|
|
112
|
+
return mac, ip
|
|
113
|
+
|
|
114
|
+
retries += 1
|
|
115
|
+
|
|
116
|
+
raise LibvirtError()
|
|
117
|
+
|
|
118
|
+
def create_guest(self, ip_tester):
|
|
119
|
+
"""
|
|
120
|
+
Returns an unready domain and its snapshot information.
|
|
121
|
+
|
|
122
|
+
Guarantee that the IP is free with the ip_tester function.
|
|
123
|
+
"""
|
|
124
|
+
if not self.ready:
|
|
125
|
+
return
|
|
126
|
+
|
|
127
|
+
# create a single guest
|
|
128
|
+
guest_unique_id = uuid.uuid4().hex
|
|
129
|
+
guest_mac, guest_ip = self.get_mac_ip(ip_tester)
|
|
130
|
+
dom, snapshot = backend_pool.libvirt.guest_handler.create_guest(
|
|
131
|
+
self.conn, guest_mac, guest_unique_id
|
|
132
|
+
)
|
|
133
|
+
if dom is None:
|
|
134
|
+
log.msg(
|
|
135
|
+
eventid="cowrie.backend_pool.libvirtd", format="Failed to create guest"
|
|
136
|
+
)
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
return dom, snapshot, guest_ip
|
|
140
|
+
|
|
141
|
+
def destroy_guest(self, domain, snapshot):
|
|
142
|
+
if not self.ready:
|
|
143
|
+
return
|
|
144
|
+
|
|
145
|
+
try:
|
|
146
|
+
# destroy the domain in QEMU
|
|
147
|
+
domain.destroy()
|
|
148
|
+
|
|
149
|
+
# we want to remove the snapshot if either:
|
|
150
|
+
# - explicitely set save_snapshots to False
|
|
151
|
+
# - no snapshot dir was defined (using cowrie's root dir) - should not happen but prevent it
|
|
152
|
+
if (
|
|
153
|
+
(
|
|
154
|
+
not CowrieConfig.getboolean(
|
|
155
|
+
"backend_pool", "save_snapshots", fallback=True
|
|
156
|
+
)
|
|
157
|
+
or CowrieConfig.get("backend_pool", "snapshot_path", fallback=None)
|
|
158
|
+
is None
|
|
159
|
+
)
|
|
160
|
+
and os.path.exists(snapshot)
|
|
161
|
+
and os.path.isfile(snapshot)
|
|
162
|
+
):
|
|
163
|
+
os.remove(snapshot) # destroy its disk snapshot
|
|
164
|
+
except Exception as error:
|
|
165
|
+
log.err(
|
|
166
|
+
eventid="cowrie.backend_pool.libvirtd",
|
|
167
|
+
format="Error destroying guest: %(error)s",
|
|
168
|
+
error=error,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
def __destroy_all_guests(self) -> None:
|
|
172
|
+
domains = self.conn.listDomainsID()
|
|
173
|
+
# if not domains:
|
|
174
|
+
# log.msg(
|
|
175
|
+
# eventid="cowrie.backend_pool.libvirtd",
|
|
176
|
+
# format="Could not get domain list",
|
|
177
|
+
# )
|
|
178
|
+
|
|
179
|
+
for domain_id in domains:
|
|
180
|
+
d = self.conn.lookupByID(domain_id)
|
|
181
|
+
if d.name().startswith("cowrie"):
|
|
182
|
+
try:
|
|
183
|
+
d.destroy()
|
|
184
|
+
except KeyboardInterrupt:
|
|
185
|
+
pass
|
|
186
|
+
|
|
187
|
+
def __destroy_all_networks(self) -> None:
|
|
188
|
+
networks = self.conn.listNetworks()
|
|
189
|
+
# if not networks:
|
|
190
|
+
# log.msg(
|
|
191
|
+
# eventid="cowrie.backend_pool.libvirtd",
|
|
192
|
+
# format="Could not get network list",
|
|
193
|
+
# )
|
|
194
|
+
|
|
195
|
+
for network in networks:
|
|
196
|
+
if network.startswith("cowrie"):
|
|
197
|
+
n = self.conn.networkLookupByName(network)
|
|
198
|
+
n.destroy()
|
|
199
|
+
|
|
200
|
+
def __destroy_all_network_filters(self) -> None:
|
|
201
|
+
network_filters = self.conn.listNWFilters()
|
|
202
|
+
# if not network_filters:
|
|
203
|
+
# log.msg(
|
|
204
|
+
# eventid="cowrie.backend_pool.libvirtd",
|
|
205
|
+
# format="Could not get network filters list",
|
|
206
|
+
# )
|
|
207
|
+
|
|
208
|
+
for nw_filter in network_filters:
|
|
209
|
+
if nw_filter.startswith("cowrie"):
|
|
210
|
+
n = self.conn.nwfilterLookupByName(nw_filter)
|
|
211
|
+
n.undefine()
|
|
212
|
+
|
|
213
|
+
def destroy_all_cowrie(self) -> None:
|
|
214
|
+
self.__destroy_all_guests()
|
|
215
|
+
self.__destroy_all_networks()
|
|
216
|
+
self.__destroy_all_network_filters()
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
|
|
2
|
+
# See the COPYRIGHT file for more information
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
from configparser import NoOptionError
|
|
7
|
+
|
|
8
|
+
from twisted.python import log
|
|
9
|
+
|
|
10
|
+
from cowrie.core.config import CowrieConfig
|
|
11
|
+
|
|
12
|
+
import backend_pool.libvirt.snapshot_handler
|
|
13
|
+
import backend_pool.util
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QemuGuestError(Exception):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def create_guest(connection, mac_address, guest_unique_id):
|
|
21
|
+
# lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
|
|
22
|
+
import libvirt
|
|
23
|
+
|
|
24
|
+
# get guest configurations
|
|
25
|
+
configuration_file: str = os.path.join(
|
|
26
|
+
CowrieConfig.get(
|
|
27
|
+
"backend_pool", "config_files_path", fallback="src/cowrie/data/pool_configs"
|
|
28
|
+
),
|
|
29
|
+
CowrieConfig.get("backend_pool", "guest_config", fallback="default_guest.xml"),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
version_tag: str = CowrieConfig.get("backend_pool", "guest_tag", fallback="guest")
|
|
33
|
+
base_image: str = CowrieConfig.get("backend_pool", "guest_image_path")
|
|
34
|
+
hypervisor: str = CowrieConfig.get(
|
|
35
|
+
"backend_pool", "guest_hypervisor", fallback="qemu"
|
|
36
|
+
)
|
|
37
|
+
memory: int = CowrieConfig.getint("backend_pool", "guest_memory", fallback=128)
|
|
38
|
+
qemu_machine: str = CowrieConfig.get(
|
|
39
|
+
"backend_pool", "guest_qemu_machine", fallback="pc-q35-3.1"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# check if base image exists
|
|
43
|
+
if not os.path.isfile(base_image):
|
|
44
|
+
log.msg(
|
|
45
|
+
eventid="cowrie.backend_pool.guest_handler",
|
|
46
|
+
format="Base image provided was not found: %(base_image)s",
|
|
47
|
+
base_image=base_image,
|
|
48
|
+
)
|
|
49
|
+
os._exit(1)
|
|
50
|
+
|
|
51
|
+
# only in some cases, like wrt
|
|
52
|
+
kernel_image: str = CowrieConfig.get(
|
|
53
|
+
"backend_pool", "guest_kernel_image", fallback=""
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
# get a directory to save snapshots, even if temporary
|
|
57
|
+
try:
|
|
58
|
+
# guest configuration, to be read by qemu, needs an absolute path
|
|
59
|
+
snapshot_path: str = backend_pool.util.to_absolute_path(
|
|
60
|
+
CowrieConfig.get("backend_pool", "snapshot_path")
|
|
61
|
+
)
|
|
62
|
+
except NoOptionError:
|
|
63
|
+
snapshot_path = os.getcwd()
|
|
64
|
+
|
|
65
|
+
# create a disk snapshot to be used by the guest
|
|
66
|
+
disk_img: str = os.path.join(
|
|
67
|
+
snapshot_path, f"snapshot-{version_tag}-{guest_unique_id}.qcow2"
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
if not backend_pool.libvirt.snapshot_handler.create_disk_snapshot(
|
|
71
|
+
base_image, disk_img
|
|
72
|
+
):
|
|
73
|
+
log.msg(
|
|
74
|
+
eventid="cowrie.backend_pool.guest_handler",
|
|
75
|
+
format="There was a problem creating the disk snapshot.",
|
|
76
|
+
)
|
|
77
|
+
raise QemuGuestError()
|
|
78
|
+
|
|
79
|
+
guest_xml = backend_pool.util.read_file(configuration_file)
|
|
80
|
+
guest_config = guest_xml.format(
|
|
81
|
+
guest_name="cowrie-" + version_tag + "_" + guest_unique_id,
|
|
82
|
+
disk_image=disk_img,
|
|
83
|
+
base_image=base_image,
|
|
84
|
+
kernel_image=kernel_image,
|
|
85
|
+
hypervisor=hypervisor,
|
|
86
|
+
memory=memory,
|
|
87
|
+
qemu_machine=qemu_machine,
|
|
88
|
+
mac_address=mac_address,
|
|
89
|
+
network_name="cowrie",
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
dom = connection.createXML(guest_config, 0)
|
|
94
|
+
if dom is None:
|
|
95
|
+
log.err(
|
|
96
|
+
eventid="cowrie.backend_pool.guest_handler",
|
|
97
|
+
format="Failed to create a domain from an XML definition.",
|
|
98
|
+
)
|
|
99
|
+
sys.exit(1)
|
|
100
|
+
except libvirt.libvirtError as e:
|
|
101
|
+
log.err(
|
|
102
|
+
eventid="cowrie.backend_pool.guest_handler",
|
|
103
|
+
format="Error booting guest: %(error)s",
|
|
104
|
+
error=e,
|
|
105
|
+
)
|
|
106
|
+
raise
|
|
107
|
+
log.msg(
|
|
108
|
+
eventid="cowrie.backend_pool.guest_handler",
|
|
109
|
+
format="Guest %(name)s has booted",
|
|
110
|
+
name=dom.name(),
|
|
111
|
+
)
|
|
112
|
+
return dom, disk_img
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
|
|
2
|
+
# See the COPYRIGHT file for more information
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
|
|
7
|
+
from twisted.python import log
|
|
8
|
+
|
|
9
|
+
from cowrie.core.config import CowrieConfig
|
|
10
|
+
|
|
11
|
+
import backend_pool.util
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def create_filter(connection):
|
|
15
|
+
# lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
|
|
16
|
+
import libvirt
|
|
17
|
+
|
|
18
|
+
filter_file: str = os.path.join(
|
|
19
|
+
CowrieConfig.get(
|
|
20
|
+
"backend_pool", "config_files_path", fallback="src/cowrie/data/pool_configs"
|
|
21
|
+
),
|
|
22
|
+
CowrieConfig.get(
|
|
23
|
+
"backend_pool", "nw_filter_config", fallback="default_filter.xml"
|
|
24
|
+
),
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
filter_xml = backend_pool.util.read_file(filter_file)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
return connection.nwfilterDefineXML(filter_xml)
|
|
31
|
+
except libvirt.libvirtError as e:
|
|
32
|
+
log.err(
|
|
33
|
+
eventid="cowrie.backend_pool.network_handler",
|
|
34
|
+
format="Filter already exists: %(error)s",
|
|
35
|
+
error=e,
|
|
36
|
+
)
|
|
37
|
+
return connection.nwfilterLookupByName("cowrie-default-filter")
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def create_network(connection, network_table):
|
|
41
|
+
# lazy import to avoid exception if not using the backend_pool and libvirt not installed (#1185)
|
|
42
|
+
import libvirt
|
|
43
|
+
|
|
44
|
+
# TODO support more interfaces and therefore more IP space to allow > 253 guests
|
|
45
|
+
network_file: str = os.path.join(
|
|
46
|
+
CowrieConfig.get(
|
|
47
|
+
"backend_pool", "config_files_path", fallback="src/cowrie/data/pool_configs"
|
|
48
|
+
),
|
|
49
|
+
CowrieConfig.get(
|
|
50
|
+
"backend_pool", "network_config", fallback="default_network.xml"
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
network_xml = backend_pool.util.read_file(network_file)
|
|
55
|
+
|
|
56
|
+
template_host: str = "<host mac='{mac_address}' name='{name}' ip='{ip_address}'/>\n"
|
|
57
|
+
hosts: str = ""
|
|
58
|
+
|
|
59
|
+
# generate a host entry for every possible guest in this network (253 entries)
|
|
60
|
+
it = iter(network_table)
|
|
61
|
+
for guest_id in range(0, 253):
|
|
62
|
+
vm_name = "vm" + str(guest_id)
|
|
63
|
+
|
|
64
|
+
key = next(it)
|
|
65
|
+
hosts += template_host.format(
|
|
66
|
+
name=vm_name, mac_address=key, ip_address=network_table[key]
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
network_config = network_xml.format(
|
|
70
|
+
network_name="cowrie",
|
|
71
|
+
iface_name="virbr2",
|
|
72
|
+
default_gateway="192.168.150.1",
|
|
73
|
+
dhcp_range_start="192.168.150.2",
|
|
74
|
+
dhcp_range_end="192.168.150.254",
|
|
75
|
+
hosts=hosts,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# create a transient virtual network
|
|
79
|
+
try:
|
|
80
|
+
net = connection.networkCreateXML(network_config)
|
|
81
|
+
if net is None:
|
|
82
|
+
log.msg(
|
|
83
|
+
eventid="cowrie.backend_pool.network_handler",
|
|
84
|
+
format="Failed to define a virtual network",
|
|
85
|
+
)
|
|
86
|
+
sys.exit(1)
|
|
87
|
+
|
|
88
|
+
# set the network active
|
|
89
|
+
# not needed since apparently transient networks are created as active; uncomment if persistent
|
|
90
|
+
# net.create()
|
|
91
|
+
|
|
92
|
+
except libvirt.libvirtError as e:
|
|
93
|
+
log.err(
|
|
94
|
+
eventid="cowrie.backend_pool.network_handler",
|
|
95
|
+
format="Network already exists: %(error)s",
|
|
96
|
+
error=e,
|
|
97
|
+
)
|
|
98
|
+
return connection.networkLookupByName("cowrie")
|
|
99
|
+
|
|
100
|
+
return net
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Copyright (c) 2019 Guilherme Borges <guilhermerosasborges@gmail.com>
|
|
2
|
+
# See the COPYRIGHT file for more information
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import getpass
|
|
7
|
+
import shutil
|
|
8
|
+
import subprocess
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_disk_snapshot(source_img: str, destination_img: str) -> bool:
|
|
12
|
+
try:
|
|
13
|
+
shutil.chown(source_img, getpass.getuser())
|
|
14
|
+
except PermissionError:
|
|
15
|
+
# log.msg('Should have root to create snapshot')
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
# could use `capture_output=True` instead of `stdout` and `stderr` args in Python 3.7
|
|
19
|
+
out = subprocess.run(
|
|
20
|
+
[
|
|
21
|
+
"qemu-img",
|
|
22
|
+
"create",
|
|
23
|
+
"-f",
|
|
24
|
+
"qcow2",
|
|
25
|
+
"-F",
|
|
26
|
+
"qcow2",
|
|
27
|
+
"-b",
|
|
28
|
+
source_img,
|
|
29
|
+
destination_img,
|
|
30
|
+
],
|
|
31
|
+
capture_output=True,
|
|
32
|
+
)
|
|
33
|
+
return out.returncode == 0
|
backend_pool/nat.py
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
from threading import Lock
|
|
3
|
+
from typing import Any
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from twisted.python import failure
|
|
8
|
+
from twisted.internet.interfaces import IAddress
|
|
9
|
+
from twisted.internet import protocol
|
|
10
|
+
from twisted.internet import reactor
|
|
11
|
+
from twisted.internet.protocol import connectionDone
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ClientProtocol(protocol.Protocol):
|
|
15
|
+
server_protocol: ServerProtocol
|
|
16
|
+
|
|
17
|
+
def dataReceived(self, data: bytes) -> None:
|
|
18
|
+
assert self.server_protocol.transport is not None
|
|
19
|
+
self.server_protocol.transport.write(data)
|
|
20
|
+
|
|
21
|
+
def connectionLost(self, reason: failure.Failure = connectionDone) -> None:
|
|
22
|
+
assert self.server_protocol.transport is not None
|
|
23
|
+
self.server_protocol.transport.loseConnection()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class ClientFactory(protocol.ClientFactory):
|
|
27
|
+
def __init__(self, server_protocol: ServerProtocol) -> None:
|
|
28
|
+
self.server_protocol = server_protocol
|
|
29
|
+
|
|
30
|
+
def buildProtocol(self, addr: IAddress) -> ClientProtocol:
|
|
31
|
+
client_protocol = ClientProtocol()
|
|
32
|
+
client_protocol.server_protocol = self.server_protocol
|
|
33
|
+
self.server_protocol.client_protocol = client_protocol
|
|
34
|
+
return client_protocol
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ServerProtocol(protocol.Protocol):
|
|
38
|
+
def __init__(self, dst_ip: str, dst_port: int):
|
|
39
|
+
self.dst_ip: str = dst_ip
|
|
40
|
+
self.dst_port: int = dst_port
|
|
41
|
+
self.client_protocol: ClientProtocol
|
|
42
|
+
self.buffer: list[bytes] = []
|
|
43
|
+
|
|
44
|
+
def connectionMade(self):
|
|
45
|
+
reactor.connectTCP(self.dst_ip, self.dst_port, ClientFactory(self))
|
|
46
|
+
|
|
47
|
+
def dataReceived(self, data: bytes) -> None:
|
|
48
|
+
self.buffer.append(data)
|
|
49
|
+
self.sendData()
|
|
50
|
+
|
|
51
|
+
def sendData(self) -> None:
|
|
52
|
+
if not self.client_protocol:
|
|
53
|
+
reactor.callLater(0.5, self.sendData) # type: ignore[attr-defined]
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
assert self.client_protocol.transport is not None
|
|
57
|
+
for packet in self.buffer:
|
|
58
|
+
self.client_protocol.transport.write(packet)
|
|
59
|
+
self.buffer = []
|
|
60
|
+
|
|
61
|
+
def connectionLost(self, reason: failure.Failure = connectionDone) -> None:
|
|
62
|
+
assert self.client_protocol.transport is not None
|
|
63
|
+
self.client_protocol.transport.loseConnection()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ServerFactory(protocol.Factory):
|
|
67
|
+
def __init__(self, dst_ip: str, dst_port: int) -> None:
|
|
68
|
+
self.dst_ip: str = dst_ip
|
|
69
|
+
self.dst_port: int = dst_port
|
|
70
|
+
|
|
71
|
+
def buildProtocol(self, addr: IAddress) -> ServerProtocol:
|
|
72
|
+
return ServerProtocol(self.dst_ip, self.dst_port)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class NATService:
|
|
76
|
+
"""
|
|
77
|
+
This service provides a NAT-like service when the backend pool
|
|
78
|
+
is located in a remote machine. Guests are bound to a local
|
|
79
|
+
IP (e.g., 192.168.150.0/24), and so not accessible from a remote
|
|
80
|
+
Cowrie. This class provides TCP proxies that associate accessible
|
|
81
|
+
IPs in the backend pool's machine to the internal IPs used by
|
|
82
|
+
guests, like a NAT.
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
def __init__(self):
|
|
86
|
+
self.bindings: dict[int, Any] = {}
|
|
87
|
+
self.lock = Lock() # we need to be thread-safe just in case, this is accessed from multiple clients
|
|
88
|
+
|
|
89
|
+
def request_binding(
|
|
90
|
+
self, guest_id: int, dst_ip: str, ssh_port: int, telnet_port: int
|
|
91
|
+
) -> tuple[int, int]:
|
|
92
|
+
with self.lock:
|
|
93
|
+
# see if binding is already created
|
|
94
|
+
if guest_id in self.bindings:
|
|
95
|
+
# increase connected
|
|
96
|
+
self.bindings[guest_id][0] += 1
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
self.bindings[guest_id][1]._realPortNumber,
|
|
100
|
+
self.bindings[guest_id][2]._realPortNumber,
|
|
101
|
+
)
|
|
102
|
+
else:
|
|
103
|
+
nat_ssh = reactor.listenTCP( # type: ignore[attr-defined]
|
|
104
|
+
0, ServerFactory(dst_ip, ssh_port), interface="0.0.0.0"
|
|
105
|
+
)
|
|
106
|
+
nat_telnet = reactor.listenTCP( # type: ignore[attr-defined]
|
|
107
|
+
0, ServerFactory(dst_ip, telnet_port), interface="0.0.0.0"
|
|
108
|
+
)
|
|
109
|
+
self.bindings[guest_id] = [1, nat_ssh, nat_telnet]
|
|
110
|
+
|
|
111
|
+
return nat_ssh._realPortNumber, nat_telnet._realPortNumber
|
|
112
|
+
|
|
113
|
+
def free_binding(self, guest_id: int) -> None:
|
|
114
|
+
with self.lock:
|
|
115
|
+
self.bindings[guest_id][0] -= 1
|
|
116
|
+
|
|
117
|
+
# stop listening if no one is connected
|
|
118
|
+
if self.bindings[guest_id][0] <= 0:
|
|
119
|
+
self.bindings[guest_id][1].stopListening()
|
|
120
|
+
self.bindings[guest_id][2].stopListening()
|
|
121
|
+
del self.bindings[guest_id]
|
|
122
|
+
|
|
123
|
+
def free_all(self):
|
|
124
|
+
with self.lock:
|
|
125
|
+
for guest_id in self.bindings:
|
|
126
|
+
self.bindings[guest_id][1].stopListening()
|
|
127
|
+
self.bindings[guest_id][2].stopListening()
|