Réception de traps snmp en quelques lignes de c++ avec l'api de windows

Description

Voici un exemple de programme permettant de recevoir des traps SNMP sous Windows, en utilisant l'API disponible, en C/C++.
Cet exemple servira à tous ceux qui, comme moi, pensaient que c'est difficile alors que quelques lignes suffisent.

Brève description de SNMP : SNMP sert à dialoguer avec des composants réseaux, ou autres. Le type de dialogue est défini par une arborescence qu'on appelle MIB, faisant l'objet d'une normalisation, mais que je ne détaillerai pas ici.

Je suggère la lecture des ouvrages disponibles, tels :
"Essential SNMP, 2nd Edition" de Douglas R.MAURO & Kevin J. SCHIMDT aux éditions O'REILLY. ISBN: 0-596-00840-6. ISBN13: 978-0-596-00840-6.
Vous trouverez dedans toute la description du protocole.
Traps SNMP : Tout composant utilisant SNMP peut envoyer des informations "spontanées", vers tous les composants/ordinateurs qui sont déclarés dans sa configuration. L'envoi est fait en UDP, sans garantie de délivrance. Si le programme destinataire n'est pas à l'écoute, le message est perdu sans que l'émetteur le sache.
Les traps SNMP sont utilisés pour informer que "quelque chose" s'est passé, c'est le récepteur qui doit savoir décoder le message, suivant la MIB, ou sans en tenir compte, dans ce cas le récepteur peut très bien n'utiliser que le texte "en clair" contenu dans le message trap.

L'exemple que je fournis va dans ce sens : j'utilise tout ce qui vient, avec un décodage automatique, et peu importe si c'est mal formaté. A vous de voir si vous avez besoin de plus de formalisme.
Je me suis servi des exemples du site http://www.winsnmp.com/samples/c/index.htm,
j'ai juste adapté pour que ça tourne dans une fenêtre Windows.
Le compilateur utilisé est C++ Builder 6, mais tout bon compilateur C est utilisable.
Il faut que le service SNMP soit activé sous Windows, si ce n'est pas le cas :
Démarrer, Paramètres, Panneau de configuration, Ajout/Sup de prog, Composants de Windows, Outils de gestion et d'analyse, détails, cocher : SNMP.

Source / Exemple :


//---------------------------------------------------------------------------
//Exemple de réception de traps SNMP sous Windows
//Ce programme nécessite C++ Builder 6 ou suivant pour fonctionner
//  mais il peut être adapté pour fonctionner avec n'importe quel compilateur C
//
//(C) UPUS 2008 par JC CHARRETEUR
// Ce programme très simple est dérivé des sources fournis à l'adresse :
//     http://www.winsnmp.com/samples/c/index.htm
//
// =====================================================================
// == Ce programme est fourni à titre d'exemple, sans garantie aucune ==
// == Il est gratuit, sous licence GNU, aucune rémunération ne peut   ==
// ==   être demandée pour la partie adaptée dans cet exemple.        ==
// =====================================================================
//
//---------------------------------------------------------------------------
// Ce programme fonctionne sous Windows XP, Vista, 2000, 2003...
// Il montre comment recevoir et décoder des traps SNMP en quelques lignes seulement.
//
//---------------------------------------------------------------------------

#include <vcl.h>
#include <stdio.h>
#include <winsnmp.h>
#pragma hdrstop

#include "Base.h"
#include "Val2Str.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"

TBaseWindow *BaseWindow;
HANDLE xWait;

SNMPAPI_STATUS Val2Str(smiLPVALUE Value, smiUINT32 Size, LPSTR lpString);
SNMPAPI_STATUS CALLBACK cbFuncWaitTrap
   (HSNMP_SESSION hSession, HWND hWnd, UINT wMsg,
    WPARAM wParam, LPARAM lParam, LPVOID lpClientData);

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//Déclaration ici pour fonctions hors contexte
// Ces fonctions peuvent être appelées depuis la fonction "registered",
// qui n'a pas besoin de connaître notre fenêtre de base

void __fastcall ClearStatusLine(void)
{
 BaseWindow->ClearStatusLine();
}
void __fastcall Logging(AnsiString S)
{
 BaseWindow->Logging(S);
}
void __fastcall Alert(AnsiString S)
{
 BaseWindow->Alert(S);
}
//---------------------------------------------------------------------------
SNMPAPI_STATUS CALLBACK cbFuncWaitTrap
   (HSNMP_SESSION hSession, HWND hWnd, UINT wMsg,
    WPARAM wParam, LPARAM lParam, LPVOID lpClientData)
{
HSNMP_PDU hPdu;
HSNMP_VBL hVbl;
smiOID dName;
smiVALUE dValue;
smiUINT32 nVb;
smiINT32 lReqId;
smiINT32 lType, lErr, lIdx;
smiINT32 lStat;
smiUINT32 i;
char szName[64];
char szValue[80];
char Texte[LGTEXTE];

//
 if (wParam !=  0) // Invalid state for trap messages
  {
   sprintf (Texte, "Invalid WinSNMP message: wParam = %d, lParam = %d",
      wParam, lParam);
   Alert(Texte);
   return SNMPAPI_SUCCESS;
  }

 lStat = SnmpRecvMsg (hSession, NULL, NULL, NULL, &hPdu);
 if (lStat != SNMPAPI_SUCCESS)
  {
   printf ("SnmpRecvMsg() failed");
   return 0;
  }

 lStat = SnmpGetPduData (hPdu, &lType, &lReqId, &lErr, &lIdx, &hVbl);
 if (lStat != SNMPAPI_SUCCESS)
  {
   sprintf(Texte, "SnmpGetPduData() failed");
   Alert(Texte);
   return 0;
  }

 sprintf (Texte, "Trap RequestId = %u", lReqId);
 Logging(Texte);
 nVb = SnmpCountVbl (hVbl);
 for (i=0; i<nVb; i++)
 {
   lStat = SnmpGetVb (hVbl, i+1, &dName, &dValue);
   lStat = SnmpOidToStr (&dName, sizeof(szName), szName);
   lStat = Val2Str (&dValue, sizeof(szValue), szValue);
   sprintf (Texte, "%s: %s", szName, szValue);
   Logging(Texte);
   lStat = SnmpFreeDescriptor (SNMP_SYNTAX_OID, (smiLPOPAQUE)&dName);
   lStat = SnmpFreeDescriptor (dValue.syntax,
                               (smiLPOPAQUE)&dValue.value.oid);
 } // end_for
 lStat = SnmpFreeVbl (hVbl);
 lStat = SnmpFreePdu (hPdu);
 return SNMPAPI_SUCCESS;
}
//---------------------------------------------------------------------------
//Cette fonction est recopiée quasiment telle quelle depuis l'exemple initial

SNMPAPI_STATUS Val2Str (smiLPVALUE Value, smiUINT32 Size, LPSTR lpString)
{
// Time formatting defines
#define DAY       (24L*60L*60L)
#define HOUR      (60L*60L)
#define MINUTE    (60L)
#define HUNDRED   (100L)
smiUINT32 lDays = 0;
smiUINT32 lHours = 0;
smiUINT32 lMinutes = 0;
smiUINT32 lSeconds = 0;
smiUINT32 lHundreths = 0;
//
smiUINT32 i, j;
ULARGE_INTEGER c64;
smiBYTE cB;
if (!Size) return (0);  // need some string to return value
// actually need more tests on remaining output string length!!!
lpString[0] = '\0';     // initialize default return value
switch (Value->syntax)
   {
   case SNMP_SYNTAX_INT:
   ltoa ((long)Value->value.sNumber, lpString, 10);
   break;

   // Note that a case for SNMP_SYNTAX_UINT32 is no longer included
   // because in SMIv2 it is indistinguishable from SNMP_SYNTAX_GAUGE32
   case SNMP_SYNTAX_CNTR32:
   case SNMP_SYNTAX_GAUGE32:
   ultoa ((long)Value->value.uNumber, lpString, 10);
   break;

   case SNMP_SYNTAX_TIMETICKS:
   if (Value->value.uNumber != 0)
      {
      lHundreths = Value->value.uNumber % HUNDRED;
      lSeconds = Value->value.uNumber / HUNDRED;
      if (lSeconds !=0)
         {
         lDays = lSeconds / DAY;
         lSeconds %= DAY;
         if (lSeconds != 0)
            {
            lHours = lSeconds / HOUR;
            lSeconds %= HOUR;
            if (lSeconds != 0)
               {
               lMinutes = lSeconds / MINUTE;
               lSeconds %= MINUTE;
               }
            }
         }
      }
   wsprintf (lpString, "%lu,%lu:%lu:%lu.%lu",
             lDays, lHours, lMinutes, lSeconds, lHundreths);
   break;

   case SNMP_SYNTAX_OCTETS:
   case SNMP_SYNTAX_BITS:
   case SNMP_SYNTAX_OPAQUE:
   if (Value->value.string.ptr)
      {
      BOOL fBinary = FALSE;
      for (i=0; i<Value->value.string.len && i<Size-1; i++)
         {
         cB = Value->value.string.ptr[i];
         if ((cB < 0x20 && (cB != 0x0A && cB != 0x0D)) || cB > 0x7E)
               { // Out of range...
               fBinary = TRUE; // it's some binary thing
               break;          // and we must hexify output
               }
         lpString[i] = cB;
         }
      lpString[i] = '\0';
      if (fBinary)
         {
         for (i=0, j=0; i<Value->value.string.len && j<Size-1; i++, j+=3)
            {
            wsprintf (&lpString[j], "%02X-", Value->value.string.ptr[i]);
            }
         lpString[j-1] = '\0'; // Lose terminating '-' character
         }
      }
   break;

   case SNMP_SYNTAX_IPADDR:
   case SNMP_SYNTAX_NSAPADDR:
   if (Value->value.string.ptr)
      {
      for (i=0, j=0; i<Value->value.string.len && j<Size-1; i++)
         {
         wsprintf (&lpString[j], "%d.", Value->value.string.ptr[i]);
         j = lstrlen (lpString);
         }
      lpString[j-1] = '\0'; // Lose terminating '.' character
      }
   break;

   case SNMP_SYNTAX_OID:
   SnmpOidToStr (&Value->value.oid, Size, lpString);
   break;

   case SNMP_SYNTAX_CNTR64:
#ifdef WIN32
   // Convert big-endian to little-endian for x86 processors
   c64.LowPart = Value->value.hNumber.lopart;
   c64.HighPart = Value->value.hNumber.hipart;
   wsprintf (lpString, "%I64u", c64);
#endif
#ifdef SOLARIS
   memmove (&c64, &Value->value.hNumber, sizeof (ULARGE_INTEGER));
//   c64.hipart=Value->value.hNumber.hipart;
//   c64.lopart=Value->value.hNumber.lopart;
   ulltostr (c64, lpString);
#endif
   break;

   case SNMP_SYNTAX_NULL:
   default:
   break;
   } // switch

return (smiUINT32)lstrlen (lpString);
}  // end_Val2Str

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//Cette fonction est "registered" au gestionnaire SNMP de Windows
// Quand l'initialisation est faite, le gestionnaire SNMP appelle
//   cette fonction et lui passe ce qui a été reçu.

SNMPAPI_STATUS CALLBACK cbFuncSendMsg
   (HSNMP_SESSION hSession,
    HWND          hWnd,
    UINT          wMsg,
    WPARAM        wParam,
    LPARAM        lParam,
    LPVOID        lpClientData)
{
 char Texte[LGTEXTE];

 if (wParam ==  0) // Normal response
  {
   HSNMP_PDU hPdu;
   HSNMP_VBL hVbl;
   smiOID    dName;
   smiVALUE  dValue;
   smiUINT32 nVb;
   smiINT32  lReqId;
   smiINT32  lType, lErr, lIdx;
//   smiINT32  lStat;
   smiUINT32 i;
   char      szName[LGTEXTE];
   char      szValue[LGTEXTE];

   SnmpRecvMsg(hSession, NULL, NULL, NULL, &hPdu);
   SnmpGetPduData(hPdu, &lType, &lReqId, &lErr, &lIdx, &hVbl);
   if (lErr != 0)
    {
      sprintf(Texte, "SNMP Error for RequestID = %d: Value = %d, Index =  %d",
               lReqId, lErr, lIdx);
      Alert(Texte);
    }
   else
    {
      nVb = SnmpCountVbl(hVbl);
      for (i = 0; i < nVb; i++)
      {
       SnmpGetVb(hVbl, i+1, &dName, &dValue);
       SnmpOidToStr(&dName, sizeof(szName), szName);
       Val2Str(&dValue, sizeof(szValue), szValue);
       sprintf(Texte, "%s: %s", szName, szValue);
       Logging(Texte);
       SnmpFreeDescriptor(SNMP_SYNTAX_OCTETS, (smiLPOPAQUE)&dName);
       SnmpFreeDescriptor(dValue.syntax,      (smiLPOPAQUE)&dValue.value.oid);
       } // end_for
    } // end_else

   SnmpFreeVbl(hVbl);
   SnmpFreePdu(hPdu);
  } // end_if wParam == 0
 else
  {
   sprintf(Texte, "Request #%d timed out.", lParam);
   Alert(Texte);
  }

 SetEvent(xWait);
 return SNMPAPI_SUCCESS;
}
//---------------------------------------------------------------------------

__fastcall TBaseWindow::TBaseWindow(TComponent* Owner)
	: TForm(Owner)

{
 Init();
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::Init (void)
{
 SNMP_Connected = false;
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::ClearStatusLine (void)
{
 StatusBar->SimpleText = "";
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::Logging(AnsiString S)
{
 StatusBar->SimpleText = S;
 LogWindow->Items->Add(S);
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::Alert(AnsiString S)
{
 ::Logging(S);
 Application->MessageBox(S.c_str(), "Alert", MB_ICONEXCLAMATION);
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::FormCreate(TObject */*Sender*/)
{
 TimerInit->Enabled = true;//On laisse le temps d'afficher la fenêtre
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::FormClose(TObject */*Sender*/,
      TCloseAction &Action)
{
 if (SNMP_Connected)
     CloseSNMP();//Ferme correctement avant de quitter
}
//---------------------------------------------------------------------------
//On a laissé 1 peu de temps à Windows pour afficher la fenêtre

void __fastcall TBaseWindow::TimerInitTimer(TObject */*Sender*/)
{
 TimerInit->Enabled = false;//Plus besoin
 Logging("Ready.");
}
//---------------------------------------------------------------------------

bool __fastcall TBaseWindow::InitSNMP (void)
{
 smiUINT32 lStat;

 lStat = SnmpStartup(&lStat, &lStat, &lStat, &lStat, &lStat);
 if (lStat == SNMPAPI_FAILURE)
  {
   Alert("Connexion SNMP initialization failed");
   return false;
 }

 Logging("SNMP session initialized");
 SNMP_Connected = true;
 return true;
}
//---------------------------------------------------------------------------

bool __fastcall TBaseWindow::CloseSNMP (void)
{
// smiUINT32 lStat;

 if (!SNMP_Connected)
   return true;//Alread closed

 SnmpClose(hSession);
 SnmpCleanup();

 SNMP_Connected = false;
 return true;
}
//---------------------------------------------------------------------------

void __fastcall TBaseWindow::ActionClearLogExecute(TObject *Sender)
{
 LogWindow->Items->Clear();
}
//---------------------------------------------------------------------------
//L'utilisateur demande à s'enregistrer pour recevoir les traps SNMP
// On déclare la fonction de réception,
//   elle sera appelée par Windows à chaque fois qu'un trap SNMP sera reçu.
// Note : Plein de programmes peuvent s'enregistrer simultanément, tous reçoivent
//        alors les mêmes messages

void __fastcall TBaseWindow::ActionReceiveTrapExecute(TObject */*Sender*/)
{
 SNMPAPI_CALLBACK cB = &cbFuncWaitTrap;
 smiUINT32 lM, lN, lL, lR, lX;
 SNMPAPI_STATUS lStat;
 char Texte[LGTEXTE];

 if (!SNMP_Connected)
  {
   if (!InitSNMP())
     return;
  }//End if !Connecte

 hSession = SnmpCreateSession(NULL, 0, cB, NULL);
 if (hSession == SNMPAPI_FAILURE)
  {
   sprintf(Texte, "SnmpCreateSession failed!");
   Alert(Texte);
   return;
  }

        //Demande au service SNMP de Windows de nous appeler
        // à chaque fois qu'un trap sera reçu.

 lStat = SnmpRegister(hSession, NULL, NULL, NULL, NULL, SNMPAPI_ON);
 if (lStat == SNMPAPI_FAILURE)
  {
   sprintf(Texte, "SnmpRegister failed!");
   Alert(Texte);
  }
}
//---------------------------------------------------------------------------
//End of Source
//---------------------------------------------------------------------------

Conclusion :


Ces quelques lignes de programme permettent de recevoir les traps SNMP sans se fatiguer, et sans avoir besoin de comprendre tout le protocole.

Le source complet du projet peut être téléchargé à l'adresse :
http://www.upus.fr/demo/rcv_snmp_traps.zip

Il faut les DLL de Borland pour exécuter :
http://www.upus.fr/demo/vcl60.zip

Bonne utilisation.

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.