#include "stdafx.h"
#include "FlashBox.h"

const char FlashBox::AK_NORD_DESCRIPTION[]={"AK-NORD, SERIAL DEVICE SERVER, XT-MACRO"};

FlashBox::FlashBox(unsigned int IP, const unsigned char MAC[6], unsigned int NetMask)
{
	//init
	m_IpAddr =IP;
	m_Feature = 0x2;		//SetIP enable
	m_Feature |= 0x8;		//Set subnet mask
	m_Feature |= 0x40;		//MAC address
	m_IpSubnet = NetMask;
	m_bGotFeature = true;	//es gab auch eine antwort
	memcpy(m_MAC,MAC,sizeof(m_MAC));

	in_addr adr;
	adr.S_un.S_addr = IP;
	strcpy_s(m_Description, sizeof(m_Description), "FlashBox");
}

FlashBox::~FlashBox(){}

//!löst ein Reset der Box aus, wird nicht unterstüzt
void FlashBox::ResetDevice(){}


//!setzt eine neue IP
bool FlashBox::ChangeIP(DWORD IPAddr, DWORD IpSubnet)
{
	SOCKET sSocketUdp;
	char Text[64];
	struct sockaddr_in sa_out;
	BOOTP_MESSAGE Msg[sizeof(BOOTP_MESSAGE)];
	bool result = true;
	char stIPServer[64]={"255.255.255.255"};

	/* Socket aufmachen/vorbereiten */
	//socket erzeugen
	sSocketUdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sSocketUdp == INVALID_SOCKET)
	{
		sprintf_s(Text, "Failed socket() (%d)", WSAGetLastError());;
		AfxMessageBox(Text);
		return false;
	}
	
	//Broadcast <-> Ja
	int Broadcast = 1;
	setsockopt(sSocketUdp, SOL_SOCKET, SO_BROADCAST, (char *)&Broadcast, sizeof(int));

	//Absender(PC) IPEndPoint
	memset(&sa_out, 0, sizeof(sa_out));
	sa_out.sin_addr.s_addr = inet_addr("255.255.255.255");
	sa_out.sin_port        = htons(BOOTP_PORT);
	sa_out.sin_family      = AF_INET;

	/* Paket erzeugen */
	CreateBOOTPReply(Msg, BOOTP_ID, inet_addr(stIPServer), htonl(IPAddr), IpSubnet, m_MAC);

	/* senden */
	if (sendto(sSocketUdp,(const char*) Msg, sizeof(BOOTP_MESSAGE), 0, (SOCKADDR*) &sa_out, sizeof(sa_out)) < 0){	
		AfxMessageBox("Failed sendto()\n");
		result = false;
	}
	
	closesocket(sSocketUdp);

	return result;
}

int TLV_Add(unsigned char *pBuffer, unsigned char Tag, int Length, unsigned char *pData)
{
	pBuffer[0] = Tag;
	pBuffer[1] = Length;
	memcpy(pBuffer+2, pData, Length);
	return Length+2;
}

int SnmpCreateRequest(unsigned char *pBuffer)
{
	unsigned int pos = 0;
	int len;

	pBuffer[pos++] = 0x30;	// Sequence Tag
	pBuffer[pos++] = 0;		// init length

	// Version
	pBuffer[pos++] = SNMP_INTEGER;		// Integer Tag
	pBuffer[pos++] = 1;		// Length
	pBuffer[pos++] = 0;		// Version 1
	pBuffer[1] += 3;

	// Community
	pBuffer[pos++] = SNMP_OCTET_STRING;		// Octetstring Tag
	pBuffer[pos++] = 6;		// Length
	memcpy(pBuffer+pos, "public", 6);
	pos += 6;
	pBuffer[1] += 8;

	{
		unsigned int getRequestPos;

		// GetRequest-PDU
		pBuffer[pos++] = 0xa0;
		getRequestPos = pos;
		pBuffer[pos++] = 0;

		// Request-ID
		pBuffer[pos++] = SNMP_INTEGER;		// Integer Tag
		pBuffer[pos++] = 4;		// Length
		pBuffer[pos++] = 0x12;
		pBuffer[pos++] = 0x34;
		pBuffer[pos++] = 0x47;
		pBuffer[pos++] = 0x11;
		pBuffer[getRequestPos] += 6;

		// Error-Status
		pBuffer[pos++] = SNMP_INTEGER;		// Integer Tag
		pBuffer[pos++] = 1;		// Length
		pBuffer[pos++] = 0;
		pBuffer[getRequestPos] += 3;

		// Error-Index
		pBuffer[pos++] = SNMP_INTEGER;		// Integer Tag
		pBuffer[pos++] = 1;		// Length
		pBuffer[pos++] = 0;
		pBuffer[getRequestPos] += 3;

		{
			unsigned int varBindListPos;

			// VarBindList - Sequence
			pBuffer[pos++] = 0x30;	// Sequence Tag
			varBindListPos = pos;
			pBuffer[pos++] = 0;		// init length

			{
				unsigned int varBindPos;
				int temp = 0;

				// Request sysDescr
				pBuffer[pos++] = 0x30;	// Sequence Tag
				varBindPos = pos;
				pBuffer[pos++] = 0;		// init length
				unsigned char sysDescr[] = {0x2b, 6, 1, 2, 1, 1, 1, 0};
				len = TLV_Add(pBuffer+pos, SNMP_OBJECT_IDENTIFIER, 8, sysDescr);
				pBuffer[varBindPos] += len;
				pos += len;
				pBuffer[pos++] = SNMP_NULL;
				pBuffer[pos++] = 0;
				pBuffer[varBindPos] += 2;
				temp += pBuffer[varBindPos]+2;

				// Request sysLocation
				pBuffer[pos++] = 0x30;	// Sequence Tag
				varBindPos = pos;
				pBuffer[pos++] = 0;		// init length
				unsigned char sysLocation[] = {0x2b, 6, 1, 2, 1, 1, 6, 0};
				len = TLV_Add(pBuffer+pos, SNMP_OBJECT_IDENTIFIER, 8, sysLocation);
				pBuffer[varBindPos] += len;
				pos += len;
				pBuffer[pos++] = SNMP_NULL;
				pBuffer[pos++] = 0;
				pBuffer[varBindPos] += 2;
				temp += pBuffer[varBindPos]+2;

				// Request sysName
				pBuffer[pos++] = 0x30;	// Sequence Tag
				varBindPos = pos;
				pBuffer[pos++] = 0;		// init length
				unsigned char sysName[] = {0x2b, 6, 1, 2, 1, 1, 5, 0};
				len = TLV_Add(pBuffer+pos, SNMP_OBJECT_IDENTIFIER, 8, sysName);
				pBuffer[varBindPos] += len;
				pos += len;
				pBuffer[pos++] = SNMP_NULL;
				pBuffer[pos++] = 0;
				pBuffer[varBindPos] += 2;
				temp += pBuffer[varBindPos]+2;

				// Request ifPhysAddress
				pBuffer[pos++] = 0x30;	// Sequence Tag
				varBindPos = pos;
				pBuffer[pos++] = 0;		// init length
				unsigned char ifPhysAddress[] = {0x2b, 6, 1, 2, 1, 2, 2, 1, 6, 1};
				len = TLV_Add(pBuffer+pos, SNMP_OBJECT_IDENTIFIER, 10, ifPhysAddress);
				pBuffer[varBindPos] += len;
				pos += len;
				pBuffer[pos++] = SNMP_NULL;
				pBuffer[pos++] = 0;
				pBuffer[varBindPos] += 2;
				temp += pBuffer[varBindPos]+2;

				// Request ipAdEntNetMask
				pBuffer[pos++] = 0x30;	// Sequence Tag
				varBindPos = pos;
				pBuffer[pos++] = 0;		// init length
				unsigned char ipAdEntNetMask[] = {0x2b, 6, 1, 2, 1, 4, 20, 1, 3};
				len = TLV_Add(pBuffer+pos, SNMP_OBJECT_IDENTIFIER, 9, ipAdEntNetMask);
				pBuffer[varBindPos] += len;
				pos += len;
				pBuffer[pos++] = SNMP_NULL;
				pBuffer[pos++] = 0;
				pBuffer[varBindPos] += 2;
				temp += pBuffer[varBindPos]+2;

				pBuffer[varBindListPos] += temp;
			}

			pBuffer[getRequestPos] += pBuffer[varBindListPos] + 2;
		}

		pBuffer[1] += pBuffer[getRequestPos] + 2;
	}

	return pBuffer[1] + 2;
}

int FlashBox::Discover(vector<iVIBBox*>* pTargetList)
{
	if(pTargetList == NULL)
		return -1;

	SOCKET sSocketUdp;
	struct sockaddr_in sa_out;
	struct sockaddr_in SenderAddr;
	int SenderAddrSize = sizeof(SenderAddr);
	bool bActive = true;
	int ret;
	unsigned int i;
	char Text[64];
	char Packet[1500];

	/* Socket aufmachen/vorbereiten */
	//socket erzeugen
	sSocketUdp = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (sSocketUdp == INVALID_SOCKET)
	{
		sprintf_s(Text, "Failed socket() (%d)", WSAGetLastError());;
		AfxMessageBox(Text);
		return -1;
	}
	
	//Broadcast <-> Ja
	int Broadcast = 1;
	setsockopt(sSocketUdp, SOL_SOCKET, SO_BROADCAST, (char *)&Broadcast, sizeof(int));

	// Increase receive buffer size
	int bufsize = 65536;
	setsockopt(sSocketUdp, SOL_SOCKET, SO_RCVBUF, (char *)&bufsize, sizeof(int));

	//Absender(PC) IPEndPoint
	memset(&sa_out, 0, sizeof(sa_out));
	sa_out.sin_addr.s_addr = inet_addr("255.255.255.255");
	sa_out.sin_port        = htons(SNMP_PORT);
	sa_out.sin_family      = AF_INET;

	i = SnmpCreateRequest((unsigned char *)Packet);

	//==> Suchen <==
	/** Anfrage senden **/	
	if (sendto(sSocketUdp, (char*)Packet, i, 0, (SOCKADDR*) &sa_out, sizeof(sa_out)) < 0)
		AfxMessageBox("Failed sendto()\n");

	// Use select() for timeout
	struct timeval timeout;
	timeout.tv_sec = 0;
	timeout.tv_usec = 250000; // 250 ms

	fd_set readSet;
	FD_ZERO(&readSet);
	FD_SET(sSocketUdp, &readSet);

	/** receive packet **/
	while (bActive)
	{
		// Wait for reply
		FD_SET(sSocketUdp, &readSet);
		if (select(0, &readSet, NULL, NULL, &timeout) <= 0)
		{
			//keine neues Paket	
			bActive = false;
		}
		else
		{
			/* eine neue Antwort?*/
			ret = recvfrom(sSocketUdp, Packet, sizeof(Packet), 0, (SOCKADDR*) &SenderAddr, &SenderAddrSize);
			//gibt es die IP schonn?
			//ein Paket kommt so oft an wie es Netzwerkkarten gibt 
			for (i=0; i<pTargetList->size(); i++){
				if (SenderAddr.sin_addr.S_un.S_addr == pTargetList->at(i)->Get_IpAddr())
					break;
			}
			if (i < pTargetList->size())
				continue;
			try
			{
				/* SNMP Parsen */
				SNMP_UniversalPrimitiveItem *pItemList;
				if(	ParseSNMPPayload((unsigned char* )Packet,ret,&pItemList) != 0)
					continue;
				
				/* sucht die passende OID und die Werte dazu */	
				SNMP_UniversalPrimitiveItem *pTemp=pItemList;
				char * sysDescr=NULL;
				unsigned char *sysMAC=NULL;
				unsigned int NetMask = 0;

				//leuft durch alle Nodes
				while(pTemp != NULL)
				{				
					if(pTemp->Type == SNMP_OBJECT_IDENTIFIER && pTemp->pNext !=NULL)
					{
						if (pTemp->pNext->Type == SNMP_OCTET_STRING)
						{
							if(memcmp(pTemp->pValue,"1.3.6.1.2.1.1.1.0.", pTemp->Size) == 0)
								sysDescr = (char *) pTemp->pNext->pValue;
							else if(memcmp(pTemp->pValue,"1.3.6.1.2.1.2.2.1.6.1.", pTemp->Size) == 0)
								sysMAC = (unsigned char *) pTemp->pNext->pValue;
						}
						else if (pTemp->pNext->Type == 0x40)
						{
							if(memcmp(pTemp->pValue,"1.3.6.1.2.1.4.20.1.3.", pTemp->Size) == 0)
								NetMask = *(unsigned int *)pTemp->pNext->pValue;
						}
					}
					pTemp = pTemp->pNext;
				}
				if( (sysDescr==NULL) || (sysMAC==NULL) )
					continue;

				/* vergleichen */
				int diff = strncmp(sysDescr,AK_NORD_DESCRIPTION, strlen(AK_NORD_DESCRIPTION) );
				if(diff==0){
					//neue Box gefunden, neues Obj erzeugen
					FlashBox* obj = new FlashBox(SenderAddr.sin_addr.S_un.S_addr, sysMAC, NetMask);

					//in liste einfügen
					pTargetList->push_back(obj);	
				}

				//freen 
				pTemp = pItemList;
				while(pTemp != NULL){
					SNMP_UniversalPrimitiveItem * dummyNext = pTemp->pNext;
					if(pTemp->pValue != NULL)
						free(pTemp->pValue);
					free(pTemp);
					pTemp = dummyNext;
				}	
			}
			catch(...){}
		}
	}

	closesocket(sSocketUdp);
	return 0;
}