diff --git a/conpot/core/attack_session.py b/conpot/core/attack_session.py
index b0cbdc96..adad3080 100644
--- a/conpot/core/attack_session.py
+++ b/conpot/core/attack_session.py
@@ -23,9 +23,16 @@
logger = logging.getLogger(__name__)
-# one instance per connection
+NEW_CONNECTION = "NEW_CONNECTION"
+CONNECTION_LOST = "CONNECTION_LOST"
+CONNECTION_CLOSED = "CONNECTION_CLOSED"
+CONNECTION_FAILED = "CONNECTION_FAILED"
+CONNECTION_TERMINATED = "CONNECTION_TERMINATED"
+CONNECTION_QUIT = "CONNECTION_QUIT"
+CONNECTION_TIMEOUT = "CONNECTION_TIMEOUT"
+# one instance per connection
class AttackSession(object):
def __init__(
self,
diff --git a/conpot/protocols/IEC104/IEC104_server.py b/conpot/protocols/IEC104/IEC104_server.py
index d66761ab..da45affd 100644
--- a/conpot/protocols/IEC104/IEC104_server.py
+++ b/conpot/protocols/IEC104/IEC104_server.py
@@ -19,6 +19,7 @@
from .frames import struct, TESTFR_act, socket, errno
import logging
import conpot.core as conpot_core
+from conpot.core import attack_session
from gevent.server import StreamServer
import gevent
from .errors import Timeout_t3
@@ -52,7 +53,7 @@ def handle(self, sock, address):
address[1],
session.id,
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
iec104_handler = IEC104(self.device_data_controller, sock, address, session.id)
try:
while True:
@@ -65,7 +66,7 @@ def handle(self, sock, address):
request = sock.recv(6)
if not request:
logger.info("IEC104 Station disconnected. (%s)", session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack.session.CONNECTION_LOST})
iec104_handler.disconnect()
break
while request and len(request) < 2:
@@ -122,18 +123,18 @@ def handle(self, sock, address):
except gevent.Timeout:
logger.warning("T1 timed out. (%s)", session.id)
logger.info("IEC104 Station disconnected. (%s)", session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
iec104_handler.disconnect()
break
except socket.timeout:
logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
except socket.error as err:
if isinstance(err.args, tuple):
if err.errno == errno.EPIPE:
# remote peer disconnected
logger.info("IEC104 Station disconnected. (%s)", session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
else:
# determine and handle different error
pass
diff --git a/conpot/protocols/bacnet/bacnet_server.py b/conpot/protocols/bacnet/bacnet_server.py
index 5ce34cc4..1d829125 100644
--- a/conpot/protocols/bacnet/bacnet_server.py
+++ b/conpot/protocols/bacnet/bacnet_server.py
@@ -28,6 +28,7 @@
import conpot.core as conpot_core
from conpot.protocols.bacnet.bacnet_app import BACnetApp
from conpot.core.protocol_wrapper import conpot_protocol
+from conpot.core import attack_session
from conpot.utils.networking import get_interface_ip
import logging
@@ -69,7 +70,7 @@ def handle(self, data, address):
logger.info(
"New Bacnet connection from %s:%d. (%s)", address[0], address[1], session.id
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
# I'm not sure if gevent DatagramServer handles issues where the
# received data is over the MTU -> fragmentation
if data:
diff --git a/conpot/protocols/enip/enip_server.py b/conpot/protocols/enip/enip_server.py
index 97d570ef..d3be4e2c 100644
--- a/conpot/protocols/enip/enip_server.py
+++ b/conpot/protocols/enip/enip_server.py
@@ -29,6 +29,7 @@
from cpppo.server.enip import parser
from cpppo.server.enip import device
from conpot.core.protocol_wrapper import conpot_protocol
+from conpot.core import attack_session
import conpot.core as conpot_core
logger = logging.getLogger(__name__)
@@ -133,7 +134,7 @@ def handle(self, conn, address, enip_process=None, delay=None, **kwds):
"enip", host, port, conn.getsockname()[0], conn.getsockname()[1]
)
logger.debug("ENIP server %s begins serving client %s", name, address)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
tcp = conn.family == socket.AF_INET and conn.type == socket.SOCK_STREAM
udp = conn.family == socket.AF_INET and conn.type == socket.SOCK_DGRAM
@@ -332,7 +333,7 @@ def handle_tcp(
cpppo.timer() - begun,
delayseconds,
)
- session.add_event({"type": "CONNECTION_CLOSED"})
+ session.add_event({"type": attack_session.CONNECTION_CLOSED})
except:
logger.error("Failed request: %s", parser.enip_format(data))
enip_process(address, data=cpppo.dotdict()) # Terminate.
@@ -491,7 +492,7 @@ def handle_udp(self, conn, name, enip_process, session, **kwds):
logger.debug(
"Transaction complete after %7.3fs", cpppo.timer() - begun
)
- session.add_event({"type": "CONNECTION_CLOSED"})
+ session.add_event({"type": attack_session.CONNECTION_CLOSED})
stats["processed"] = source.sent
except:
# Parsing failure. Suck out some remaining input to give us some context, but don't re-raise
@@ -512,7 +513,7 @@ def handle_udp(self, conn, name, enip_process, session, **kwds):
where,
"".join(traceback.format_exception(*sys.exc_info())),
)
- session.add_event({"type": "CONNECTION_FAILED"})
+ session.add_event({"type": attack_session.CONNECTION_FAILED})
def set_tags(self):
typenames = {
diff --git a/conpot/protocols/ftp/ftp_base_handler.py b/conpot/protocols/ftp/ftp_base_handler.py
index a93268a4..ce2f39f0 100644
--- a/conpot/protocols/ftp/ftp_base_handler.py
+++ b/conpot/protocols/ftp/ftp_base_handler.py
@@ -19,6 +19,7 @@
import gevent
import conpot.core as conpot_core
from conpot.core.filesystem import FilesystemError
+from conpot.core import attack_session
import logging
import errno
import time
@@ -236,7 +237,7 @@ def setup(self):
self.client_address[0], self.client_address[1], self.session.id
)
)
- self.session.add_event({"type": "NEW_CONNECTION"})
+ self.session.add_event({"type": attack_session.NEW_CONNECTION})
# send 200 + banner -- new client has connected!
self.respond(b"200 " + self.config.banner.encode())
# Is there a delay in command response? < gevent.sleep(0.5) ?
@@ -292,7 +293,7 @@ def handle_cmd_channel(self):
self.client_address, self.session.id
)
)
- self.session.add_event({"type": "CONNECTION_LOST"})
+ self.session.add_event({"type": attack_session.CONNECTION_LOST})
self.finish()
return
socket_read, socket_write, _ = gevent.select.select(
@@ -349,7 +350,7 @@ def handle_cmd_channel(self):
self.client_address, self.session.id, se
)
)
- self.session.add_event({"type": "CONNECTION_LOST"})
+ self.session.add_event({"type": attack_session.CONNECTION_LOST})
self.finish()
def respond(self, response):
diff --git a/conpot/protocols/ftp/ftp_handler.py b/conpot/protocols/ftp/ftp_handler.py
index fe1ed488..c85f2e3f 100644
--- a/conpot/protocols/ftp/ftp_handler.py
+++ b/conpot/protocols/ftp/ftp_handler.py
@@ -12,6 +12,7 @@
import gevent
from gevent import socket
from conpot.core.filesystem import FilesystemError, FSOperationNotPermitted
+from conpot.core import attack_session
from conpot.protocols.ftp.ftp_utils import FTPPrivilegeException, get_data_from_iter
logger = logging.getLogger(__name__)
@@ -113,7 +114,7 @@ def do_SYST(self, arg):
def do_QUIT(self, arg):
self.respond(b"221 Bye.")
- self.session.add_event({"type": "CONNECTION_TERMINATED"})
+ self.session.add_event({"type": attack_session.CONNECTION_TERMINATED})
self.disconnect_client = True
def do_SITE_HELP(self, line):
@@ -878,7 +879,7 @@ def _process_command(self, cmd, *args, **kwargs):
if self.invalid_login_attempt >= self.max_login_attempts:
self.respond(b"421 Too many connections. Service temporarily unavailable.")
self.disconnect_client = True
- self.session.add_event({"type": "CONNECTION_TERMINATED"})
+ self.session.add_event({"type": attack_session.CONNECTION_TERMINATED})
else:
try:
method = getattr(self, "do_" + cmd.replace(" ", "_"))
@@ -936,7 +937,9 @@ def process_ftp_command(self):
)
# TODO: what to respond here? For now just terminate the session
self.disconnect_client = True
- self.session.add_event({"type": "CONNECTION_TERMINATED"})
+ self.session.add_event(
+ {"type": attack_session.CONNECTION_TERMINATED}
+ )
elif not (self.metrics.timeout() < self.config.timeout) and (
not self._data_channel
):
@@ -945,7 +948,7 @@ def process_ftp_command(self):
self.client_address, self.session.id
)
)
- self.session.add_event({"type": "CONNECTION_TIMEOUT"})
+ self.session.add_event({"type": attack_session.CONNECTION_TIMEOUT})
self.respond(b"421 Timeout.")
self.disconnect_client = True
else:
diff --git a/conpot/protocols/guardian_ast/guardian_ast_server.py b/conpot/protocols/guardian_ast/guardian_ast_server.py
index 296642cc..8af109f8 100644
--- a/conpot/protocols/guardian_ast/guardian_ast_server.py
+++ b/conpot/protocols/guardian_ast/guardian_ast_server.py
@@ -25,6 +25,7 @@
import logging
import random
import conpot.core as conpot_core
+from conpot.core import attack_session
from conpot.core.protocol_wrapper import conpot_protocol
from conpot.utils.networking import str_to_bytes
@@ -55,7 +56,7 @@ def handle(self, sock, addr):
logger.info(
"New GuardianAST connection from %s:%d. (%s)", addr[0], addr[1], session.id
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
current_time = datetime.datetime.utcnow()
fill_start = self.fill_offset_time - datetime.timedelta(minutes=313)
fill_stop = self.fill_offset_time - datetime.timedelta(minutes=303)
@@ -507,7 +508,7 @@ def I20500():
logger.info(
"GuardianAST client disconnected %s:%d. (%s)", addr[0], addr[1], session.id
)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
def start(self, host, port):
connection = (host, port)
diff --git a/conpot/protocols/kamstrup_management/kamstrup_management_server.py b/conpot/protocols/kamstrup_management/kamstrup_management_server.py
index 17cf93c3..80746ea0 100644
--- a/conpot/protocols/kamstrup_management/kamstrup_management_server.py
+++ b/conpot/protocols/kamstrup_management/kamstrup_management_server.py
@@ -21,6 +21,7 @@
import conpot.core as conpot_core
from .command_responder import CommandResponder
from conpot.core.protocol_wrapper import conpot_protocol
+from conpot.core import attack_session
from conpot.utils.networking import str_to_bytes
logger = logging.getLogger(__name__)
@@ -48,7 +49,7 @@ def handle(self, sock, address):
address[1],
session.id,
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
try:
sock.send(
@@ -63,7 +64,7 @@ def handle(self, sock, address):
data = sock.recv(1024)
if not data:
logger.info("Kamstrup client disconnected. (%s)", session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
break
request = data.decode()
logdata = {"request": request}
@@ -79,7 +80,7 @@ def handle(self, sock, address):
gevent.sleep(0.25) # TODO measure delay and/or RTT
if response is None:
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
break
# encode data before sending
reply = str_to_bytes(response)
@@ -87,7 +88,7 @@ def handle(self, sock, address):
except socket.timeout:
logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
sock.close()
diff --git a/conpot/protocols/kamstrup_meter/kamstrup_server.py b/conpot/protocols/kamstrup_meter/kamstrup_server.py
index d5779641..df565eab 100644
--- a/conpot/protocols/kamstrup_meter/kamstrup_server.py
+++ b/conpot/protocols/kamstrup_meter/kamstrup_server.py
@@ -26,6 +26,7 @@
from .request_parser import KamstrupRequestParser
from .command_responder import CommandResponder
from conpot.core.protocol_wrapper import conpot_protocol
+from conpot.core import attack_session
logger = logging.getLogger(__name__)
@@ -64,7 +65,7 @@ def handle(self, sock, address):
address[1],
session.id,
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
self.server_active = True
@@ -75,7 +76,7 @@ def handle(self, sock, address):
if not raw_request:
logger.info("Kamstrup client disconnected. (%s)", session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
break
for x in raw_request:
@@ -84,7 +85,7 @@ def handle(self, sock, address):
while True:
request = parser.get_request()
if not request:
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
break
else:
logdata = {
@@ -112,7 +113,7 @@ def handle(self, sock, address):
except socket.timeout:
logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
sock.close()
diff --git a/conpot/protocols/modbus/modbus_server.py b/conpot/protocols/modbus/modbus_server.py
index 5400b105..93d61834 100644
--- a/conpot/protocols/modbus/modbus_server.py
+++ b/conpot/protocols/modbus/modbus_server.py
@@ -17,6 +17,7 @@
from conpot.core.protocol_wrapper import conpot_protocol
from conpot.protocols.modbus import slave_db
import conpot.core as conpot_core
+from conpot.core import attack_session
logger = logging.getLogger(__name__)
@@ -105,7 +106,7 @@ def handle(self, sock, address):
logger.info(
"New Modbus connection from %s:%s. (%s)", address[0], address[1], session.id
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
try:
while True:
@@ -121,17 +122,17 @@ def handle(self, sock, address):
if not request:
logger.info("Modbus client disconnected. (%s)", session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
break
if request.strip().lower() == "quit.":
logger.info("Modbus client quit. (%s)", session.id)
- session.add_event({"type": "CONNECTION_QUIT"})
+ session.add_event({"type": attack_session.CONNECTION_QUIT})
break
if len(request) < 7:
logger.info(
"Modbus client provided data {} but invalid.".format(session.id)
)
- session.add_event({"type": "CONNECTION_TERMINATED"})
+ session.add_event({"type": attack_session.CONNECTION_TERMINATED})
break
_, _, length = struct.unpack(">HHH", request[:6])
while len(request) < (length + 6):
@@ -169,7 +170,9 @@ def handle(self, sock, address):
logger.info(
"Modbus connection terminated with client %s.", address[0]
)
- session.add_event({"type": "CONNECTION_TERMINATED"})
+ session.add_event(
+ {"type": attack_session.CONNECTION_TERMINATED}
+ )
sock.shutdown(socket.SHUT_RDWR)
sock.close()
break
@@ -179,13 +182,15 @@ def handle(self, sock, address):
"Modbus client ignored due to invalid addressing." " (%s)",
session.id,
)
- session.add_event({"type": "CONNECTION_TERMINATED"})
+ session.add_event(
+ {"type": attack_session.CONNECTION_TERMINATED}
+ )
sock.shutdown(socket.SHUT_RDWR)
sock.close()
break
except socket.timeout:
logger.debug("Socket timeout, remote: %s. (%s)", address[0], session.id)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
def start(self, host, port):
self.host = host
diff --git a/conpot/protocols/s7comm/s7_server.py b/conpot/protocols/s7comm/s7_server.py
index a815e0f0..da89166f 100644
--- a/conpot/protocols/s7comm/s7_server.py
+++ b/conpot/protocols/s7comm/s7_server.py
@@ -27,6 +27,7 @@
from conpot.protocols.s7comm.cotp import COTP_ConnectionConfirm
from conpot.protocols.s7comm.s7 import S7
import conpot.core as conpot_core
+from conpot.core import attack_session
from conpot.core.protocol_wrapper import conpot_protocol
from lxml import etree
@@ -82,14 +83,14 @@ def handle(self, sock, address):
address[0], address[1], session.id
)
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
try:
while True:
data = sock.recv(4, socket.MSG_WAITALL)
if len(data) == 0:
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
break
_, _, length = unpack("!BBH", data[:4])
@@ -283,12 +284,12 @@ def handle(self, sock, address):
)
except socket.timeout:
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
logger.debug(
"Socket timeout, remote: {0}. ({1})".format(address[0], session.id)
)
except socket.error:
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
logger.debug(
"Connection reset by peer, remote: {0}. ({1})".format(
address[0], session.id
diff --git a/conpot/protocols/tftp/tftp_server.py b/conpot/protocols/tftp/tftp_server.py
index c5a0aaf2..fa20b592 100644
--- a/conpot/protocols/tftp/tftp_server.py
+++ b/conpot/protocols/tftp/tftp_server.py
@@ -24,6 +24,7 @@
from conpot.protocols.tftp import tftp_handler
from gevent.server import DatagramServer
import conpot.core as conpot_core
+from conpot.core import attack_session
from conpot.core.protocol_wrapper import conpot_protocol
from conpot.utils.networking import get_interface_ip
from tftpy import TftpException, TftpTimeout
@@ -116,7 +117,7 @@ def handle(self, buffer, client_addr):
client_addr[0], client_addr[1]
)
)
- session.add_event({"type": "NEW_CONNECTION"})
+ session.add_event({"type": attack_session.NEW_CONNECTION})
logger.debug("Read %d bytes", len(buffer))
context = tftp_handler.TFTPContextServer(
client_addr[0], client_addr[1], self.timeout, self.root, None, None
@@ -124,13 +125,13 @@ def handle(self, buffer, client_addr):
context.vfs, context.data_fs = self.vfs, self.data_fs
if self.shutdown:
logger.info("Shutting down now. Disconnecting {}".format(client_addr))
- session.add_event({"type": "CONNECTION_TERMINATED"})
+ session.add_event({"type": attack_session.CONNECTION_TERMINATED})
try:
context.start(buffer)
context.cycle()
except TftpTimeout as err:
logger.info("Timeout occurred %s: %s" % (context, str(err)))
- session.add_event({"type": "CONNECTION_TIMEOUT"})
+ session.add_event({"type": attack_session.CONNECTION_TIMEOUT})
context.retry_count += 1
# TODO: We should accept retries from the user.
if context.retry_count >= self.TIMEOUT_RETRIES:
@@ -148,7 +149,7 @@ def handle(self, buffer, client_addr):
context, str(err)
)
)
- session.add_event({"type": "CONNECTION_LOST"})
+ session.add_event({"type": attack_session.CONNECTION_LOST})
logger.info("TFTP: terminating connection: {}".format(context))
session.set_ended()
context.end()
diff --git a/conpot/protocols/triconex/triconex.xsd b/conpot/protocols/triconex/triconex.xsd
new file mode 100644
index 00000000..98bdc39e
--- /dev/null
+++ b/conpot/protocols/triconex/triconex.xsd
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/conpot/protocols/triconex/triconex_server.py b/conpot/protocols/triconex/triconex_server.py
new file mode 100644
index 00000000..93ba646c
--- /dev/null
+++ b/conpot/protocols/triconex/triconex_server.py
@@ -0,0 +1,363 @@
+# BSD 3-Clause License
+#
+# Copyright (c) 2018, Nozomi Networks
+# Copyright (c) 2020, MushMush
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+# * Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# * Neither the name of the copyright holder nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+import socket
+import struct
+from lxml import etree
+from gevent.server import DatagramServer
+import conpot.core as conpot_core
+from conpot.utils.networking import get_interface_ip
+from conpot.core.protocol_wrapper import conpot_protocol
+from conpot.core import attack_session
+import crcmod
+import time
+import logging
+
+logger = logging.getLogger(__name__)
+cf = crcmod.mkCrcFun(0x18005, rev=True, initCrc=0, xorOut=0)
+
+
+def build_slot(leds0, leds1, model, color):
+ slotfmt = "<" + 32 * "B"
+ return struct.pack(
+ slotfmt,
+ leds0,
+ leds1,
+ model,
+ color,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ )
+
+
+# Construct slots
+mps = {
+ "active": build_slot(0x15, 0x21, 0xF0, 0x01),
+ "passive": build_slot(0x02, 0x01, 0xF0, 0x02),
+}
+
+slotsdesc = {
+ "empty": build_slot(0, 0, 0, 0),
+ "com": build_slot(5, 33, 55, 1),
+ "do": build_slot(5, 16, 20, 1),
+ "di": build_slot(5, 32, 11, 1),
+ "him": build_slot(5, 22, 53, 1),
+ "ddo": build_slot(0x4F, 0x21, 0x5C, 0x2),
+}
+
+
+def build_chassis_status_response(
+ triconId=0,
+ seq=0,
+ node=2,
+ projname="FIRSTPROJ",
+ activemp=0,
+ mpmodel=1,
+ slots=["com"],
+):
+ # Project segment
+ data = struct.pack(
+ "
+
+ Triconex
+ Schneider
+ Triconex system
+ triconex
+ the conpot team
+
+
+
+
+
+
diff --git a/conpot/templates/triconex/triconex/triconex.xml b/conpot/templates/triconex/triconex/triconex.xml
new file mode 100644
index 00000000..829b77bc
--- /dev/null
+++ b/conpot/templates/triconex/triconex/triconex.xml
@@ -0,0 +1,8 @@
+
+
+ empty
+ empty
+ empty
+ empty
+
+
\ No newline at end of file
diff --git a/conpot/tests/test_triconex.py b/conpot/tests/test_triconex.py
new file mode 100644
index 00000000..ee599634
--- /dev/null
+++ b/conpot/tests/test_triconex.py
@@ -0,0 +1,18 @@
+import unittest
+
+from conpot.utils.greenlet import spawn_test_server, teardown_test_server
+from conpot.protocols.triconex.triconex_server import TriconexServer
+
+
+class TestTriconexServer(unittest.TestCase):
+ def setUp(self):
+ self.triconex_server, self.greenlet = spawn_test_server(
+ TriconexServer, template="default", protocol="triconex"
+ )
+
+ def tearDown(self):
+ teardown_test_server(self.triconex_server, self.greenlet)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/requirements.txt b/requirements.txt
index 7b729187..7dbd94a8 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -27,4 +27,5 @@ freezegun!=0.3.13
pytest
pycrypto
sphinx_rtd_theme
+crcmod==1.7
psutil