/***************************************************************************
 *   CSNMP - SNMP protocol handling class, version 1.0                     *
 *   Copyright (C) 2004 by EVER Sp. z o.o.                                 *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef _CSNMP_H_
#define _CSNMP_H_

//
// Headers
//
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <math.h>
#ifndef __FreeBSD__
#	include <malloc.h>
#endif/*__FreeBSD__*/
#include <errno.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>

//
// Additional defs
//
#ifndef __int64
#define __int64		signed long long
#endif
#ifndef SOCKET
#define SOCKET		int
#endif
#ifndef BYTE
#define BYTE		unsigned char
#endif
#ifndef LPBYTE
#define LPBYTE		unsigned char*
#endif
#define INVALID_SOCKET		(-1)
#define SOCKET_ERROR		(-1)

#ifndef LOBYTE
	#define LOBYTE(w)		(w & 0xff)
#endif
#ifndef HIBYTE
	#define HIBYTE(w)		(LOBYTE(w >> 8))
#endif
#ifndef LOWORD
	#define LOWORD(dw)		(dw & 0xffff)
#endif
#ifndef HIWORD
	#define HIWORD(dw)		(LOWORD(dw >> 16))
#endif

//
// Error definitions
//
#define SNMP_ERROR_SOCKET_LISTEN		(-15)
#define SNMP_ERROR_SOCKET_BINDING		(-14)
#define SNMP_ERROR_SOCKET_CONNECT		(-13)
#define SNMP_ERROR_SOCKET_GENERAL		(-12)
#define SNMP_ERROR_SOCKET_TIMEOUT		(-11)
#define SNMP_ERROR_SOCKET_DISCONNECT	(-10)
#define SNMP_ERROR_UNSUPPORTED_TYPE		(-6 )
#define SNMP_ERROR_FRAME_LENGTH			(-5 )
#define SNMP_ERROR_FRAME				(-4 )
#define SNMP_ERROR_MEMALLOC				(-3 )
#define SNMP_ERROR_BADPARAM				(-2 )
#define SNMP_ERROR					(-1 )
#define SNMP_ERROR_SUCCESS				( 0 )
#define SNMP_ERROR_NOERROR				( 0 )
#define SNMP_ERROR_TOOBIG				( 1 )
#define SNMP_ERROR_NOSUCHNAME			( 2 )
#define SNMP_ERROR_BADVALUE				( 3 )
#define SNMP_ERROR_READONLY				( 4 )
#define SNMP_ERROR_GENERR				( 5 )

// Error strings
#define SNMP_ERROR_STR_SOCKET_LISTEN		"Listening error (listen)"
#define SNMP_ERROR_STR_SOCKET_BINDING		"Binding error (bind)"
#define SNMP_ERROR_STR_SOCKET_CONNECT		"Connecting error (connect)"
#define SNMP_ERROR_STR_SOCKET_GENERAL		"(General)"
#define SNMP_ERROR_STR_SOCKET_TIMEOUT		"Time limit has been reached"
#define SNMP_ERROR_STR_SOCKET_DISCONNECT	"Client disconnected"
#define SNMP_ERROR_STR_UNSUPPORTED_TYPE		"Unsupported object type"
#define SNMP_ERROR_STR_FRAME_LENGTH			"Invalid frame length"
#define SNMP_ERROR_STR_FRAME				"Invalid frame"
#define SNMP_ERROR_STR_MEMALLOC				"Memory allocation error"
#define SNMP_ERROR_STR_BADPARAM				"Invalid parameter"
#define SNMP_ERROR_STR						"Unknown error"
#define SNMP_ERROR_STR_SUCCESS				""
#define SNMP_ERROR_STR_NOERROR				"(Pdu) No error"
#define SNMP_ERROR_STR_TOOBIG				"(Pdu) Too big"
#define SNMP_ERROR_STR_NOSUCHNAME			"(Pdu) No such name"
#define SNMP_ERROR_STR_BADVALUE				"(Pdu) Bad value"
#define SNMP_ERROR_STR_READONLY				"(Pdu) Read only"
#define SNMP_ERROR_STR_GENERR				"(Pdu) Generic error"

// Maksymalna ilosc obiektow w ramce trap ktora bedzie zapisywana do bufora
#define SNMP_MAX_TRAP_OBJECTS			( 10)

// Maksymalna dlugosc ramki SNMP
#define SNMP_MAX_PDU_LENGTH				(484)

// Porty
#define SNMP_PORT						161
#define TRAP_PORT						162

//
// Enumerator typów ASN1 primitive
//
typedef enum _ASN1Prim_
{
	DT_INTEGER				= 0x02,
	DT_BIT_STRING			= 0x03,
	DT_OCTET_STRING			= 0x04,
	DT_NULL					= 0x05,
	DT_OBJECT_IDENTIFIER	= 0x06,
	DT_SEQUENCE_OF			= 0x30
}ASN1_Primitive, enumASN1_Primitive;

//
// Enumerator typów SNMP primitive (type of class APPLICATION)
//
typedef enum _SNMPPrim_
{
	PT_IpAddress			= 0x40,
	// Counter32 for SNMPv2
	PT_Counter				= 0x41,
	// Gauge32 for SNMPv2
	PT_Gauge				= 0x42,
	PT_TimeTicks			= 0x43,
	PT_Opaque				= 0x44,
	PT_NsapAdress			= 0x45,
	PT_Counter64			= 0x46,
	PT_UIInteger32			= 0x47
}SNMP_Primitive, enumSNMP_Primitive;

//
// Enumerator typów poleceń SNMP (PDU)
//
typedef enum _SNMPMessage_
{
	GetRequest			= 0xA0,
	GetNextRequest		= 0xA1,
	// Response-PDU for SNMPv2
	GetResponse			= 0xA2,
	SetRequest			= 0xA3,
	// Obsolete in SNMPv2
	Trap				= 0xA4,
	
	// Only in SNMPv2
	
	GetBulkRequest		= 0xA5,
	InformRequest		= 0xA6,
	SNMPv2_Trap			= 0xA7
}SNMP_Message, enumSNMP_Message;

//
// Enumerator typów GenericTrap z TRAP-PDU
//
typedef enum _SNMPGenericTrap
{
	GT_COLDSTART				= 0,
	GT_WARMSTART				= 1,
    GT_LINKDOWN					= 2,
	GT_LINKUP					= 3,
	GT_AUTHENTICATIONFAILURE	= 4,
	GT_EGPNEIGHBORLOSS			= 5,
	GT_ENTERPRISESPECIFIC		= 6
}SNMP_GenericTrap;

//
// Struktura OID
//
typedef struct _SNMPOID
{
	unsigned long		ulLength;
	LPBYTE				lpbSnmpData;
}SNMP_OID, *LPSNMP_OID;

//
// Struktura SNMP-PDU
//
typedef struct _SNMPPdu
{
	unsigned long		ulLength;
	unsigned long		ulVersion;
	char				szCommunity[127];
	SNMP_Message		eCommand;
	unsigned long		ulRequestId;
	int					iErrorStatus;
	int					iErrorIndex;
	SNMP_OID			OID;
}SNMP_Pdu, *LPSNMP_Pdu;

//
// Struktura TRAP-PDU
//
typedef struct _SNMPTrapPdu
{
	unsigned long		ulLength;
	unsigned long		ulVersion;
	char				szCommunity[127];
	SNMP_Message		eCommand;
	SNMP_OID			EnterpriseOID;
	BYTE				bAgentAddress[6];
	unsigned long		ulGenericTrap;
	unsigned long		ulSpecificTrap;
	unsigned long		ulTimeStamp;
	int					iNumObjects;
	SNMP_OID			sObjOID[SNMP_MAX_TRAP_OBJECTS];
}SNMP_TrapPdu, *LPSNMP_TrapPdu;

//
// Type definitions
//
typedef int (*CBTRAPFN)(LPSNMP_TrapPdu, struct sockaddr_in *);

//
// Klasa SNMP, obsługuje protokół SNMPv1 (jak na razie) /*24-10-2001*/
//
class CSNMP
{
private:
	// Uchwyt do gniazda SNMP
	SOCKET sSnmp;
	// Uchwyt do gniazda TRAP
	SOCKET sSnmpTrap;
	// Struktura gniazda lokalnego
	struct sockaddr_in sSaiLocal;
	// Struktura gniazda - remote
	struct sockaddr_in sSai;
	// Numer ostatniego błędu
	int iError;
	// Bufor pomocniczy ramki
	BYTE bFrameBuff[484];
	// Bufor pomocniczy ramki trap
	BYTE bFrameTrapBuff[484];
	// Flaga deinstalacji trap managera
	bool bQuitHandler;
	// Funkcja do kodowania BER (liczby > 127)
	int EncodeBER(unsigned long ulNumber, int *lpiLen, LPBYTE lpBuffer);
	// Funkcja do dekodowania zakodowanej długości
	int DecodeBER(LPBYTE lpBuffer, int *lpiLen, unsigned long *lpiDecoded);
	// Funkcja do dekodowania zakodowanych danych
	int DecodeInteger(LPBYTE lpBuffer, int *lpiLen, unsigned long *lpulDecoded);
	// Przesuwanie bloku pamieci
	int MoveMem(LPBYTE lpBuffer, int iDir, int iSize);
	// Funkcja do rozpoznawania ramki Trap
	int RecognizeTrapFrame(LPSNMP_TrapPdu lpstp, LPBYTE lpbData, int iSize);
	// pomocnicze
	int min(int a, int b) {
		if (a<=b) return a;
		else return b;
	};
	int max(int a, int b) {
		if (a>=b) return a;
		else return b;
	};

public:
	// constructor & destructor
	CSNMP(){};
	~CSNMP(){};

	// Struktur ramki TRAP-PDU nadawcza
	SNMP_TrapPdu	sTrapPdu;
	// Struktur ramki SNMP-PDU nadawcza
	SNMP_Pdu		sPdu;
	// Struktur ramki SNMP-PDU odbiorcza
	SNMP_Pdu		sPduRec;
	// Struktur OID
	SNMP_OID		sOid;

	/* Funkcje obsługi */
	
	// Inicjalizacja usługi SNMP
	int		Init(unsigned char ipAddress[16], u_short Port);
	int		Init();
	// Deinicjalizacja usługi
	int		UnInit();
	// Tworzy OID z podanych parametrow
	int		CreateOID(LPSNMP_OID lpOid, int iLen, ...);
	int		CreateOIDFromTable(LPSNMP_OID lpOid, int iCount, int *lpiTable);
	int		CreateOID(int iLen, ...);
	int		CreateOIDFromTable(int iLen, int *lpiTable);
	// Tworzy kompletną ramkę SNMP
	int		CreateFrame(int iVersion, char *szCommunity, 
		SNMP_Message eCommand, int iRequestId, SNMP_OID sOid);
	int		CreateFrame(int iVersion, char *szCommunity, 
		SNMP_Message eCommand, int iRequestId);
	// Wysyła ramkę SNMP
	int		SendPdu(LPSNMP_Pdu lpsp, int iType, int iSize, LPBYTE lpbData);
	int		SendPdu(LPSNMP_Pdu lpsp);
	int		SendPdu();
	// Odbiera ramkę SNMP i zwraca dane
	int		Receive(LPSNMP_Pdu lpsp, int *iType, int *lpiSize, LPBYTE lpbData, int iTimeout);
	// Odbiera ramkę SNMP różnego typu
	int		ReceiveInteger(unsigned long *lpInteger, int iTimeout);
	int		ReceiveOctetString(char *lpszOctetString, int iTimeout);
	// Obsług pułapek
	int		StartTrapHandler(CBTRAPFN lpcbt, int iPort);
	int		StopTrapHandler();
	// Oczyszcza bufory
	int		CleanUpPdu(LPSNMP_Pdu lpsp);
	int		CleanUpPdu();
	// Konwersje i narzędzia
	int		ConvertStringToOID(LPSNMP_OID lpOid, char *lpszOID);
	// Zwraca treść błędu SNMP
	char	*GetErrorString(int iErrorStatus);
	// zwraca true jeżeli typ jest typu liczbowego
	bool	IsNumericType( int iType ) {
		switch( iType )
		{
		case DT_INTEGER: 
		case PT_Counter: 
		case PT_Gauge: 
		case PT_TimeTicks:
		case PT_Opaque: 
		case PT_Counter64: 
		case PT_UIInteger32: 
			return true;
		default: 
			return false;
		}
	};
};

#endif//_CSNMP_H_

