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