/*
  SNMP management daemon for the Discworld Mudlib.
  Technically this daemon responds with v1 as it's protocol.
  However it does not support the full MIB-II spec mainly because a lot
  of it is not relevant to the information I wanted to advertise.
  Everything is advertised via the system entity in MIB-II using the
  identifiers 101 and up.  OID 100 is there as a dummy, it returns 0 and is
  for mrtg which requires 2 mibs to graph.
  
  So, for example, number of players on is numPlayers or 1.3.6.1.2.1.1.101
*/
#include "socket.h"
#include "socket_errors.h"
#include "snmp.h"
int s;
class SnmpMessage {
        int version;
        string community;
        string SourceAddress;
        int RequestType;
        int *PacketID;
        int ErrorStatus;
        int ErrorIndex;
        buffer *ObjectData;
        }
void WriteBuffer(string what, buffer tbuf)
{
  string tstr;
  int ti;
  tstr="";
  for(ti=0;ti<sizeof(tbuf);ti++)
  {
    tstr+=sprintf("%X ", tbuf[ti]);
  }
  tell_creator("sojan", "Buffer %s: %s\n", what, tstr);
}
void create()
{
  int tmp;
  s=socket_create(DATAGRAM_BINARY, "ReceiveData");
  tmp=socket_bind(s, LISTEN_PORT);
}
void SendReply(class SnmpMessage Reply)
{
  buffer ReplyBuf, RealReplyBuf, tmpbuf;
  int i,j,k,l,m;
  ReplyBuf=allocate_buffer(500);
  ReplyBuf[0]=SEQUENCE;
  ReplyBuf[1]=0;
  ReplyBuf[2]=INTEGER;
  ReplyBuf[3]=1;
  ReplyBuf[4]=Reply->version;
  ReplyBuf[5]=OCTET_STRING;
  ReplyBuf[6]=sizeof(Reply->community);
  i=write_buffer(ReplyBuf, 7, Reply->community);
  i=sizeof(Reply->community)+7;
  ReplyBuf[i]=Reply->RequestType;
  ReplyBuf[i+1]=0;
  ReplyBuf[i+2]=2;
  ReplyBuf[i+3]=sizeof(Reply->PacketID);
  k=i+4;
  for(j=0;j<sizeof(Reply->PacketID);j++)
  {
    ReplyBuf[k]=Reply->PacketID[j];
    k++;
  }
  ReplyBuf[k]=2;
  ReplyBuf[k+1]=1;
  ReplyBuf[k+2]=0;
  ReplyBuf[k+3]=2;
  ReplyBuf[k+4]=1;
  ReplyBuf[k+5]=0;
  ReplyBuf[k+6]=SEQUENCE;
  ReplyBuf[k+7]=0;
  l=k+8;
  for(j=0;j<sizeof(Reply->ObjectData);j++)
  {
    m=write_buffer(ReplyBuf, l, Reply->ObjectData[j]);
    l=l+sizeof(Reply->ObjectData[j]);
  }
  ReplyBuf[1]=l-2;
  ReplyBuf[k+7]=l-(k+8);
  RealReplyBuf=allocate_buffer(l);
  RealReplyBuf=ReplyBuf[0..l-1];
  RealReplyBuf[i+1]=sizeof(RealReplyBuf)-i-2;
  socket_write(s, RealReplyBuf, Reply->SourceAddress);
  return;
}
void ParseObjects(class SnmpMessage CurrentMessage)
{
  int i,j;
  class SnmpMessage MyReply;
  buffer mibdata;
  MyReply=new(
        class SnmpMessage,
        version:CurrentMessage->version,
        community:CurrentMessage->community,
        RequestType:GETRESPONSEPDU,
        SourceAddress:CurrentMessage->SourceAddress,
        PacketID:CurrentMessage->PacketID,
        ErrorStatus:0,
        ErrorIndex:0,
        ObjectData:({})
        );
  for(i=0;i<sizeof(CurrentMessage->ObjectData);i++)
  {
    j=CurrentMessage->ObjectData[i][1];
    mibdata=SNMPHANDLER->GetData(CurrentMessage->ObjectData[i][2..j+1]);
    if(mibdata[0]==SEQUENCE) MyReply->ObjectData+=({mibdata,}); 
  }
  SendReply(MyReply);
  return;
}
void ReceiveData(int fd, buffer message, string address)
{
  int i,j,k,ss,is,id,es,ei;
  class SnmpMessage NewMessage;
  id=0;
  es=0;
  ei=0;
  is=0;
  ss=0;
  NewMessage=new(
        class SnmpMessage, 
        version:message[4], 
        community:read_buffer(message, 7, message[6]),
        RequestType:message[message[6]+7],
        SourceAddress:address,
        PacketID:({}),
        ErrorStatus:0,
        ErrorIndex:0,
        ObjectData:({})
        );
  for(i=message[6]+9;i<message[1]+2;i++)
  {
    switch(message[i])
    {
      case INTEGER :
        switch(is)
        {
          case ID :
            i++;
            k=i;
            is++;
            for(j=0;j<message[k];j++)
            {
              i++;
              NewMessage->PacketID+=({message[i],});
            }
            break; 
          case ES :
            i++;
            i++;
            is++;
            NewMessage->ErrorStatus=message[i];
            break;
          case EI :
            i++;
            i++;
            is++;
            NewMessage->ErrorIndex=message[i];
            break;
        }  
        break;
      case SEQUENCE :
        if(!ss)
        {
          ss++;
          i++;
          break;
        }
        i++;
        j=message[i];
        i++;
        NewMessage->ObjectData+=({message[i..(i+j-1)],});
        break;
    }
  }
  ParseObjects(NewMessage);          
  return;
}