package activedirectory

import (
	"bytes"
	"encoding/binary"

	"git.loafle.net/overflow/overflow_probe/discovery/core/scan/service/matcher/packet"
	"git.loafle.net/overflow/overflow_probe/discovery/discovery/types"
)

const (
	AD_MESSAGE_ID      = 0x99
	AD_MESSAGE_ID_QUIT = 0x89

	LDAP_VERSION3 = 3

	LDAP_SUCCESS = 0x00

	LDAP_REQ_BIND         = 0x60
	LDAP_RES_SEARCH_ENTRY = 0x64

	LDAP_REQ_UNBIND = 0x42
	LDAP_REQ_SEARCH = 0x63

	LDAP_SCOPE_BASE     = 0x00
	LDAP_DEREF_NEVER    = 0x00
	LDAP_FILTER_PRESENT = 0x87

	LDAP_RES_BIND = 0x61

	LDAP_AUTH_SIMPLE = 0x80

	AD_TYPE_STR = "supportedCapabilities"
)

type AD_SENDaaa struct {
	DefaultCode uint8
	PackLenFlag uint8
	PacketLen   uint32

	NextType1       uint8
	NextTypeLength1 uint8

	MessageId uint32

	ProtocolOp      uint8
	PtLenFlag       uint8
	PtPacketLen     uint32
	NextType2       uint8
	NextTypeLength2 uint8

	Version uint8

	NextType3       uint8
	NextTypeLength3 uint8
	Auth            uint8
	AuthLength      uint8
}

type AD_SEND struct {
	DefaultCode uint8
	PackLenFlag uint8

	PacketLen uint32

	NextType1    uint8
	NextType1Len uint8

	MessageId uint32

	ProtocolOp uint8

	PtPackLenFlag uint8
	PtPacketLen   uint32

	NextType2    uint8
	NextType2Len uint8
	NextType3    uint8
	NextType3Len uint8

	Scope uint8

	NextType4    uint8
	NextType4Len uint8

	DerefAliases uint8

	NextType5    uint8
	NextType5Len uint8

	SizeLimit uint8

	NextType6    uint8
	NextType6Len uint8

	TimeLimit uint8

	NextType7    uint8
	NextType7Len uint8

	TypesOnly uint8

	Filter1    uint8
	PresentLen uint8

	Present [11]byte

	DefaultCode2 uint8
	Pack2LenFlag uint8

	Packet2Len uint32

	UnknwonCode8 uint8
	ItemLength   uint8

	AttributeDescription [21]byte
}

type AD_QUIT struct {
	DefaultCode uint8
	PackLenFlag uint8

	PacketLength uint32

	NextType1       uint8
	NextTypeLength1 uint8

	MessageId uint32

	ProtocolOp uint8

	PtLenFlag   uint8
	PtPacketLen uint32
}

type AD_RECV struct {
	DefaultCode uint8
	PackLenFlag uint8

	PacketLength uint32

	NextType1    uint8
	NextType1Len uint8

	MessageId uint16

	ProtocolOp uint8

	PtPackLenFlag uint8
	PtPacketLen   uint32

	NextType2    uint8
	NextType2Len uint8

	UnknwonCode21 uint8
	UnknwonCode22 uint8
	UnknwonCode23 uint8
	UnknwonCode24 uint8
	UnknwonCode25 uint8
	UnknwonCode26 uint8

	UnknwonCode31 uint8
	UnknwonCode32 uint8
	UnknwonCode33 uint8
	UnknwonCode34 uint8
	UnknwonCode35 uint8
	UnknwonCode36 uint8
	UnknwonCode37 uint8

	TypeLength uint8
}

type ActiveDirectoryMatcher struct {
	sendPackets []*packet.Packet
}

func (ad *ActiveDirectoryMatcher) Match(index int, packet *packet.Packet, info *types.ServiceScanInfo) bool {

	if packet == nil {
		return false
	}

	buf := new(bytes.Buffer)
	buf.Write(packet.Buffer)

	adRecv := AD_RECV{}

	binary.Read(buf, binary.BigEndian, &adRecv)

	if adRecv.MessageId != AD_MESSAGE_ID {
		return false
	}

	if adRecv.ProtocolOp != LDAP_RES_SEARCH_ENTRY {
		return false
	}

	///AD_TYPE_STR

	//
	//if(packet->readCount_ < sizeof(AD_RECV) + recv->typeLength) {
	//    return false;
	//}

	//char* type = new char[recv->typeLength];
	//memcpy(type, packet->buffer_+sizeof(AD_RECV), recv->typeLength);
	//std::string typeStr = type;
	//
	//delete[] type;
	//if(typeStr.compare(AD_TYPE_STR) != 0) {
	//return false;
	//}

	return true
}

func (ad *ActiveDirectoryMatcher) PacketCount() int {
	return len(ad.sendPackets)
}
func (ad *ActiveDirectoryMatcher) Packet(index int) *packet.Packet {
	return ad.sendPackets[index]
}
func (ad *ActiveDirectoryMatcher) ServiceName() string {
	return "ActiveDirectory"
}

func (ad *ActiveDirectoryMatcher) IsError(index int, packet *packet.Packet, info *types.ServiceScanInfo) bool {
	return false
}
func (ad *ActiveDirectoryMatcher) IsNoResponse(index int) bool {

	if index == 1 {
		return true
	}

	return false
}

func (ad *ActiveDirectoryMatcher) IsPrePacket() bool {
	return false
}

func NewActiveDirectoryMatcher() *ActiveDirectoryMatcher {

	ls := AD_SEND{
		DefaultCode: 0x30,
		PackLenFlag: 0x84,

		PacketLen: 0x47,

		NextType1:    0x02,
		NextType1Len: 0x04,

		MessageId: AD_MESSAGE_ID,

		ProtocolOp: LDAP_REQ_SEARCH,

		PtPackLenFlag: 0x84,
		PtPacketLen:   0x3b,

		NextType2:    0x04,
		NextType2Len: 0x00,
		NextType3:    0x0a,
		NextType3Len: 0x01,

		Scope: LDAP_SCOPE_BASE,

		NextType4:    0x0a,
		NextType4Len: 0x01,

		DerefAliases: LDAP_DEREF_NEVER,

		NextType5:    0x02,
		NextType5Len: 0x01,

		SizeLimit: 0,

		NextType6:    0x02,
		NextType6Len: 0x01,

		TimeLimit: 0x78,

		NextType7:    0x01,
		NextType7Len: 0x01,

		TypesOnly: 0,

		Filter1:    LDAP_FILTER_PRESENT,
		PresentLen: 0x0b,

		//Present			:0000,

		DefaultCode2: 0x30,
		Pack2LenFlag: 0x84,

		Packet2Len: 0x17,

		UnknwonCode8: 0x04,
		ItemLength:   0x15,

		//AttributeDescription:,
	}

	copy(ls.Present[:], "objectclass")
	copy(ls.AttributeDescription[:], AD_TYPE_STR)

	mCache := new(bytes.Buffer)
	binary.Write(mCache, binary.BigEndian, ls)

	sendByte1 := mCache.Bytes()

	adm := ActiveDirectoryMatcher{
	//sendPackets: make([][]byte, 2),
	}

	pp := packet.NewPacket(sendByte1, len(sendByte1))

	adm.sendPackets = append(adm.sendPackets, pp)

	aq := AD_QUIT{
		DefaultCode:     0x30,
		PackLenFlag:     0x84,
		PacketLength:    0x0c,
		NextType1:       0x02,
		NextTypeLength1: 0x04,
		MessageId:       AD_MESSAGE_ID_QUIT,
		ProtocolOp:      LDAP_REQ_UNBIND,
		PtLenFlag:       0x84,
		PtPacketLen:     0x00,
	}

	lqBuffer := new(bytes.Buffer)
	binary.Write(lqBuffer, binary.BigEndian, aq)

	quBytes := lqBuffer.Bytes()

	pp2 := packet.NewPacket(quBytes, len(quBytes))

	adm.sendPackets = append(adm.sendPackets, pp2)

	return &adm
}
