1 /*
2  * snmpserv.cxx
3  *
4  * SNMP Server (agent) class
5  *
6  * Portable Windows Library
7  *
8  * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
9  * Copyright (c) 2007 ISVO(Asia) Pte. Ltd.
10  *
11  * The contents of this file are subject to the Mozilla Public License
12  * Version 1.0 (the "License"); you may not use this file except in
13  * compliance with the License. You may obtain a copy of the License at
14  * http://www.mozilla.org/MPL/
15  *
16  * Software distributed under the License is distributed on an "AS IS"
17  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
18  * the License for the specific language governing rights and limitations
19  * under the License.
20  *
21  * The Original Code is Portable Windows Library.
22  *
23  * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24  *
25  * Contributor(s): ______________________________________.
26  *
27  * $Revision: 26773 $
28  * $Author: rjongbloed $
29  * $Date: 2011-12-07 00:43:33 -0600 (Wed, 07 Dec 2011) $
30  */
31 
32 #include <ptlib.h>
33 
34 #ifdef P_SNMP
35 
36 #include <ptclib/psnmp.h>
37 
38 #define new PNEW
39 
40 
41 #define SNMP_VERSION 0
42 
43 static const char defaultCommunity[] = "public";
44 
PSNMPServer(PIPSocket::Address binding,WORD localPort,PINDEX timeout,PINDEX rxSize,PINDEX txSize)45 PSNMPServer::PSNMPServer(PIPSocket::Address binding, WORD localPort, PINDEX timeout, PINDEX rxSize, PINDEX txSize)
46 #ifdef _MSC_VER
47 #pragma warning(disable:4355)
48 #endif
49  : m_thread(*this, &PSNMPServer::Main, true, "SNMP Server")
50 #ifdef _MSC_VER
51 #pragma warning(default:4355)
52 #endif
53  , community(defaultCommunity)
54  , version(SNMP_VERSION)
55  , maxRxSize(rxSize)
56  , maxTxSize(txSize)
57 {
58   SetReadTimeout(PTimeInterval(0, timeout));
59   baseSocket = new PUDPSocket;
60 
61   if (!baseSocket->Listen(binding, 0, localPort)) {
62     PTRACE(4,"SNMPsrv\tError: Unable to Listen on port " << localPort);
63   }
64   else {
65     Open(baseSocket);
66     m_thread.Resume();
67   }
68 }
69 
Main()70 void PSNMPServer::Main()
71 {
72    if (!HandleChannel())
73 	     Close();
74 }
75 
~PSNMPServer()76 PSNMPServer::~PSNMPServer()
77 {
78 	Close();
79 }
80 
HandleChannel()81 PBoolean PSNMPServer::HandleChannel()
82 {
83 
84   PBYTEArray readBuffer;
85   PBYTEArray sendBuffer(maxTxSize);
86 
87 
88   for (;;) {
89    	if (!IsOpen())
90 	  return PFalse;
91 
92 		// Reading
93 	    PINDEX rxSize = 0;
94 		readBuffer.SetSize(maxRxSize);
95 		for (;;) {
96 			if (!Read(readBuffer.GetPointer()+rxSize, maxRxSize - rxSize)) {
97 
98 			// if the buffer was too small, then we are receiving datagrams
99 			// and the datagram was too big
100 			if (PChannel::GetErrorCode() == PChannel::BufferTooSmall)
101 				lastErrorCode = RxBufferTooSmall;
102 			else
103 				lastErrorCode = NoResponse;
104 
105 			PTRACE(4,"SNMPsrv\tRenewing Socket due to timeout" << lastErrorCode);
106 
107 			} else if ((rxSize + GetLastReadCount()) >= 10)
108 			break;
109 
110 			else
111 			rxSize += GetLastReadCount();
112 		}
113 
114 	    rxSize += GetLastReadCount();
115 		readBuffer.SetSize(rxSize);
116 
117 		PIPSocket::Address remoteAddress;
118 		WORD remotePort;
119 		baseSocket->GetLastReceiveAddress(remoteAddress, remotePort);
120 
121 		if (!Authorise(remoteAddress)) {
122 		  PTRACE(4,"SNMPsrv\tReceived UnAuthorized Message from IP " << remoteAddress);
123 		  continue;
124 		}
125 		// process the request
126 		if (ProcessPDU(readBuffer, sendBuffer) == PTrue) {
127 			// send the packet
128 			baseSocket->SetSendAddress(remoteAddress, remotePort);
129 			PTRACE(4, "SNMPsrv\tWriting " << sendBuffer.GetSize() << " Bytes to basesocket");
130 			if (!Write(sendBuffer, sendBuffer.GetSize())) {
131 			    PTRACE(4,"SNMPsrv\tWrite Error.");
132 			    continue;
133 			}
134 			sendBuffer.SetSize(maxTxSize); //revert to max tx
135 		}
136   }
137 
138 }
139 
140 
Authorise(const PIPSocket::Address &)141 PBoolean PSNMPServer::Authorise(const PIPSocket::Address & /*received*/)
142 {
143   return PFalse;
144 }
145 
146 
SetVersion(PASNInt newVersion)147 void PSNMPServer::SetVersion(PASNInt newVersion)
148 {
149   version = newVersion;
150 }
151 
152 
153 
SendGetResponse(PSNMPVarBindingList &)154 PSNMP::ErrorType PSNMPServer::SendGetResponse (PSNMPVarBindingList &)
155 {
156   PAssertAlways("SendGetResponse not yet implemented");
157   return GenErr;
158 }
159 
160 
OnGetRequest(PINDEX,PSNMP::BindingList &,PSNMP::ErrorType &)161 PBoolean PSNMPServer::OnGetRequest (PINDEX , PSNMP::BindingList &, PSNMP::ErrorType &)
162 {
163 	return PFalse;
164 }
165 
166 
OnGetNextRequest(PINDEX,PSNMP::BindingList &,PSNMP::ErrorType &)167 PBoolean PSNMPServer::OnGetNextRequest (PINDEX , PSNMP::BindingList &, PSNMP::ErrorType &)
168 {
169 	return PFalse;
170 }
171 
172 
OnSetRequest(PINDEX,PSNMP::BindingList &,PSNMP::ErrorType &)173 PBoolean PSNMPServer::OnSetRequest (PINDEX , PSNMP::BindingList &,PSNMP::ErrorType &)
174 {
175 	return PFalse;
176 }
177 
178 
179 template <typename PDUType>
DecodeOID(const PDUType & pdu,PINDEX & reqID,PSNMP::BindingList & varlist)180 static void DecodeOID(const PDUType & pdu, PINDEX & reqID, PSNMP::BindingList & varlist)
181 {
182    reqID = pdu.m_request_id;
183    const PSNMP_VarBindList & vars = pdu.m_variable_bindings;
184 
185   // create the Requested list
186   for (PINDEX i = 0; i < vars.GetSize(); i++) {
187     varlist.push_back(pair<PString,PRFC1155_ObjectSyntax>(vars[i].m_name.AsString(),vars[i].m_value));
188   }
189 }
190 
191 template <typename PDUType>
EncodeOID(PDUType & pdu,const PINDEX & reqID,const PSNMP::BindingList & varlist,const PSNMP::ErrorType & errCode)192 static void EncodeOID(PDUType & pdu, const PINDEX & reqID,
193 					  const PSNMP::BindingList & varlist,
194 					  const PSNMP::ErrorType & errCode)
195 {
196    pdu.m_request_id = reqID;
197    pdu.m_error_status = errCode;
198    pdu.m_error_index = 0;
199 
200    if (errCode == PSNMP::NoError) {
201 	   // Build the response list
202 		PSNMP_VarBindList & vars = pdu.m_variable_bindings;
203 		PINDEX i = 0;
204 		vars.SetSize((int)varlist.size());
205 		PSNMP::BindingList::const_iterator Iter = varlist.begin();
206 		while (Iter != varlist.end()) {
207 		  vars[i].m_name.SetValue(Iter->first);
208 		  vars[i].m_value = Iter->second;
209 		  i++;
210 		  ++Iter;
211 		}
212    }
213 }
214 
MIB_LocalMatch(PSNMP_PDU & pdu)215 PBoolean PSNMPServer::MIB_LocalMatch(PSNMP_PDU & pdu)
216 {
217   PBoolean found = PFalse;
218   PSNMP_VarBindList & vars = pdu.m_variable_bindings;
219   PINDEX size = vars.GetSize();
220 
221   for(PINDEX x = 0 ;x < size; x++){
222     PRFC1155_ObjectSyntax *obj = (PRFC1155_ObjectSyntax*) objList.GetAt(vars[x].m_name);
223     if (obj != NULL){
224       vars[x].m_value = *obj;
225       found = PTrue;
226     }
227     else{
228       pdu.m_error_status = PSNMP::NoSuchName;
229     }
230   }
231   return found;
232 }
233 
ConfirmCommunity(PASN_OctetString &)234 PBoolean PSNMPServer::ConfirmCommunity(PASN_OctetString & /*community*/)
235 {
236 	return PFalse;
237 }
238 
ConfirmVersion(PASN_Integer vers)239 PBoolean PSNMPServer::ConfirmVersion(PASN_Integer vers)
240 {
241   return version == vers ? PTrue : PFalse;
242 }
243 
ProcessPDU(const PBYTEArray & readBuffer,PBYTEArray & sendBuffer)244 PBoolean PSNMPServer::ProcessPDU(const PBYTEArray & readBuffer, PBYTEArray & sendBuffer)
245 {
246 
247   PSNMP_Message msg;
248 
249   if (!msg.Decode((PASN_Stream &)readBuffer)) {
250 	  PTRACE(4,"SNMPsrv\tERROR DECODING PDU");
251 	  return PFalse;
252   }
253 
254   PTRACE(4, "SNMPsrv\tEncoded message" << msg);
255 
256   if (!ConfirmVersion(msg.m_version)){
257     PTRACE(4, "SNMPsrv\tVersion mismatch on request, ignoring");
258     return PFalse;
259   }
260 
261   if (!ConfirmCommunity(msg.m_community)){
262     PTRACE(4, "SNMPsrv\tCommunity string mismatch on request, ignoring");
263     return PFalse;
264   }
265 
266   PSNMP::BindingList varlist;
267   PINDEX reqID;
268 
269   PSNMP_Message resp;
270   PSNMP_PDUs sendpdu;
271 
272   PBoolean retval = PTrue;
273   PSNMP::ErrorType errCode = PSNMP::NoError;
274   switch (msg.m_pdu.GetTag()) {
275     case PSNMP_PDUs::e_get_request:
276       DecodeOID<PSNMP_GetRequest_PDU>(msg.m_pdu, reqID, varlist);
277       retval = OnGetRequest(reqID, varlist, errCode);
278       if (retval == PTrue){
279         sendpdu.SetTag(PSNMP_PDUs::e_get_response);
280         EncodeOID<PSNMP_GetResponse_PDU>(sendpdu, reqID,varlist,errCode);
281         resp.m_pdu = sendpdu;
282         resp.m_version = msg.m_version;
283         resp.m_community = msg.m_community;
284         PSNMP_GetResponse_PDU & mpdu = (PSNMP_GetResponse_PDU &) resp.m_pdu;
285         if (!MIB_LocalMatch(mpdu)){
286           retval = PFalse;
287           break;
288         }
289         resp.Encode((PASN_Stream &)sendBuffer);
290       }
291 
292     break;
293     case PSNMP_PDUs::e_get_next_request:
294       DecodeOID<PSNMP_GetNextRequest_PDU>(msg.m_pdu, reqID, varlist);
295       retval = OnGetNextRequest(reqID,varlist,errCode);
296       if (retval == PTrue){
297         sendpdu.SetTag(PSNMP_PDUs::e_get_response);
298         EncodeOID<PSNMP_GetResponse_PDU>(sendpdu, reqID,varlist,errCode);
299         resp.m_pdu = sendpdu;
300         resp.m_version = msg.m_version;
301         resp.m_community = msg.m_community;
302         PSNMP_GetResponse_PDU & mpdu = (PSNMP_GetResponse_PDU &) resp.m_pdu;
303         if (!MIB_LocalMatch(mpdu)){
304           retval = PFalse;
305           break;
306         }
307         resp.Encode((PASN_Stream &)sendBuffer);
308       }
309       break;
310 
311     case PSNMP_PDUs::e_set_request:
312       DecodeOID<PSNMP_SetRequest_PDU>(msg.m_pdu, reqID, varlist);
313       retval = OnSetRequest(reqID, varlist, errCode);
314       break;
315 
316     case PSNMP_PDUs::e_get_response:
317     case PSNMP_PDUs::e_trap:
318     default:
319       PTRACE(4,"SNMPsrv\tSNMP Request/Response not supported");
320       errCode= PSNMP::GenErr;
321       retval = PFalse;
322   }
323 
324   if (retval) {
325     PTRACE(4, "SNMPSrv\tSNMP Response " << resp);
326   }
327 
328   return retval;
329 }
330 
331 #endif
332 
333 // End Of File ///////////////////////////////////////////////////////////////
334