# SPDX-License-Identifier: GPL-2.0-only
# This file is part of Scapy
# See https://scapy.net/ for more information
# Copyright (C) Philippe Biondi <phil@secdev.org>

"""
Sebek: kernel module for data collection on honeypots.
"""

# scapy.contrib.description = Sebek
# scapy.contrib.status = loads

from scapy.fields import FieldLenField, IPField, IntField, ShortEnumField, \
    ShortField, StrFixedLenField, StrLenField, XIntField, ByteEnumField
from scapy.packet import Packet, bind_layers
from scapy.layers.inet import UDP
from scapy.data import IP_PROTOS


# SEBEK


class SebekHead(Packet):
    name = "Sebek header"
    fields_desc = [XIntField("magic", 0xd0d0d0),
                   ShortField("version", 1),
                   ShortEnumField("type", 0, {"read": 0, "write": 1,
                                              "socket": 2, "open": 3}),
                   IntField("counter", 0),
                   IntField("time_sec", 0),
                   IntField("time_usec", 0)]

    def mysummary(self):
        return self.sprintf("Sebek Header v%SebekHead.version% %SebekHead.type%")  # noqa: E501

# we need this because Sebek headers differ between v1 and v3, and
# between v3 type socket and v3 others


class SebekV1(Packet):
    name = "Sebek v1"
    fields_desc = [IntField("pid", 0),
                   IntField("uid", 0),
                   IntField("fd", 0),
                   StrFixedLenField("cmd", "", 12),
                   FieldLenField("data_length", None, "data", fmt="I"),
                   StrLenField("data", "", length_from=lambda x:x.data_length)]

    def mysummary(self):
        if isinstance(self.underlayer, SebekHead):
            return self.underlayer.sprintf("Sebek v1 %SebekHead.type% (%SebekV1.cmd%)")  # noqa: E501
        else:
            return self.sprintf("Sebek v1 (%SebekV1.cmd%)")


class SebekV3(Packet):
    name = "Sebek v3"
    fields_desc = [IntField("parent_pid", 0),
                   IntField("pid", 0),
                   IntField("uid", 0),
                   IntField("fd", 0),
                   IntField("inode", 0),
                   StrFixedLenField("cmd", "", 12),
                   FieldLenField("data_length", None, "data", fmt="I"),
                   StrLenField("data", "", length_from=lambda x:x.data_length)]

    def mysummary(self):
        if isinstance(self.underlayer, SebekHead):
            return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3.cmd%)")  # noqa: E501
        else:
            return self.sprintf("Sebek v3 (%SebekV3.cmd%)")


class SebekV2(SebekV3):
    def mysummary(self):
        if isinstance(self.underlayer, SebekHead):
            return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2.cmd%)")  # noqa: E501
        else:
            return self.sprintf("Sebek v2 (%SebekV2.cmd%)")


class SebekV3Sock(Packet):
    name = "Sebek v2 socket"
    fields_desc = [IntField("parent_pid", 0),
                   IntField("pid", 0),
                   IntField("uid", 0),
                   IntField("fd", 0),
                   IntField("inode", 0),
                   StrFixedLenField("cmd", "", 12),
                   IntField("data_length", 15),
                   IPField("dip", "127.0.0.1"),
                   ShortField("dport", 0),
                   IPField("sip", "127.0.0.1"),
                   ShortField("sport", 0),
                   ShortEnumField("call", 0, {"bind": 2,
                                              "connect": 3, "listen": 4,
                                              "accept": 5, "sendmsg": 16,
                                              "recvmsg": 17, "sendto": 11,
                                              "recvfrom": 12}),
                   ByteEnumField("proto", 0, IP_PROTOS)]

    def mysummary(self):
        if isinstance(self.underlayer, SebekHead):
            return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV3Sock.cmd%)")  # noqa: E501
        else:
            return self.sprintf("Sebek v3 socket (%SebekV3Sock.cmd%)")


class SebekV2Sock(SebekV3Sock):
    def mysummary(self):
        if isinstance(self.underlayer, SebekHead):
            return self.underlayer.sprintf("Sebek v%SebekHead.version% %SebekHead.type% (%SebekV2Sock.cmd%)")  # noqa: E501
        else:
            return self.sprintf("Sebek v2 socket (%SebekV2Sock.cmd%)")


bind_layers(UDP, SebekHead, sport=1101)
bind_layers(UDP, SebekHead, dport=1101)
bind_layers(UDP, SebekHead, dport=1101, sport=1101)
bind_layers(SebekHead, SebekV1, version=1)
bind_layers(SebekHead, SebekV2Sock, version=2, type=2)
bind_layers(SebekHead, SebekV2, version=2)
bind_layers(SebekHead, SebekV3Sock, version=3, type=2)
bind_layers(SebekHead, SebekV3, version=3)
