1 /*
2 * snmpclnt.cxx
3 *
4 * SNMP Client class
5 *
6 * Portable Windows Library
7 *
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
9 *
10 * The contents of this file are subject to the Mozilla Public License
11 * Version 1.0 (the "License"); you may not use this file except in
12 * compliance with the License. You may obtain a copy of the License at
13 * http://www.mozilla.org/MPL/
14 *
15 * Software distributed under the License is distributed on an "AS IS"
16 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
17 * the License for the specific language governing rights and limitations
18 * under the License.
19 *
20 * The Original Code is Portable Windows Library.
21 *
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
23 *
24 * Contributor(s): ______________________________________.
25 *
26 * $Revision: 20385 $
27 * $Author: rjongbloed $
28 * $Date: 2008-06-04 05:40:38 -0500 (Wed, 04 Jun 2008) $
29 */
30
31 #include <ptlib.h>
32 #include <ptbuildopts.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 #define SNMP_PORT "snmp 161"
43
44 static const char defaultCommunity[] = "public";
45
46
47 ///////////////////////////////////////////////////////////////
48 //
49 // PSNMPClient
50 //
51
PSNMPClient(PINDEX retry,PINDEX timeout,PINDEX rxSize,PINDEX txSize)52 PSNMPClient::PSNMPClient(PINDEX retry, PINDEX timeout,
53 PINDEX rxSize, PINDEX txSize)
54 : community(defaultCommunity),
55 version(SNMP_VERSION),
56 retryMax(retry),
57 maxRxSize(rxSize),
58 maxTxSize(txSize)
59 {
60 SetReadTimeout(PTimeInterval(0, timeout));
61 requestId = rand() % 0x7fffffff;
62 }
63
64
PSNMPClient(const PString & host,PINDEX retry,PINDEX timeout,PINDEX rxSize,PINDEX txSize)65 PSNMPClient::PSNMPClient(const PString & host, PINDEX retry,
66 PINDEX timeout, PINDEX rxSize, PINDEX txSize)
67 : hostName(host),
68 community(defaultCommunity),
69 version(SNMP_VERSION),
70 retryMax(retry),
71 maxRxSize(rxSize),
72 maxTxSize(txSize)
73 {
74 SetReadTimeout(PTimeInterval(0, timeout));
75 Open(new PUDPSocket(host, SNMP_PORT));
76 requestId = rand() % 0x7fffffff;
77 }
78
79
SetVersion(PASNInt newVersion)80 void PSNMPClient::SetVersion(PASNInt newVersion)
81 {
82 version = newVersion;
83 }
84
85
GetVersion() const86 PASNInt PSNMPClient::GetVersion() const
87 {
88 return version;
89 }
90
91
SetCommunity(const PString & str)92 void PSNMPClient::SetCommunity(const PString & str)
93 {
94 community = str;
95 }
96
97
GetCommunity() const98 PString PSNMPClient::GetCommunity() const
99 {
100 return community;
101 }
102
103
SetRequestID(PASNInt newRequestID)104 void PSNMPClient::SetRequestID(PASNInt newRequestID)
105 {
106 requestId = newRequestID;
107 }
108
109
GetRequestID() const110 PASNInt PSNMPClient::GetRequestID() const
111 {
112 return requestId;
113 }
114
115
WriteGetRequest(PSNMPVarBindingList & varsIn,PSNMPVarBindingList & varsOut)116 PBoolean PSNMPClient::WriteGetRequest(PSNMPVarBindingList & varsIn,
117 PSNMPVarBindingList & varsOut)
118 {
119 return WriteRequest(GetRequest, varsIn, varsOut);
120 }
121
122
WriteGetNextRequest(PSNMPVarBindingList & varsIn,PSNMPVarBindingList & varsOut)123 PBoolean PSNMPClient::WriteGetNextRequest(PSNMPVarBindingList & varsIn,
124 PSNMPVarBindingList & varsOut)
125 {
126 return WriteRequest(GetNextRequest, varsIn, varsOut);
127 }
128
129
WriteSetRequest(PSNMPVarBindingList & varsIn,PSNMPVarBindingList & varsOut)130 PBoolean PSNMPClient::WriteSetRequest(PSNMPVarBindingList & varsIn,
131 PSNMPVarBindingList & varsOut)
132 {
133 return WriteRequest(SetRequest, varsIn, varsOut);
134 }
135
136
GetLastErrorCode() const137 PSNMP::ErrorType PSNMPClient::GetLastErrorCode() const
138 {
139 return lastErrorCode;
140 }
141
142
GetLastErrorIndex() const143 PINDEX PSNMPClient::GetLastErrorIndex() const
144 {
145 return lastErrorIndex;
146 }
147
148
GetLastErrorText() const149 PString PSNMPClient::GetLastErrorText() const
150 {
151 return PSNMP::GetErrorText(lastErrorCode);
152 }
153
ReadRequest(PBYTEArray & readBuffer)154 PBoolean PSNMPClient::ReadRequest(PBYTEArray & readBuffer)
155 {
156 readBuffer.SetSize(maxRxSize);
157 PINDEX rxSize = 0;
158
159 for (;;) {
160
161 if (!Read(readBuffer.GetPointer()+rxSize, maxRxSize - rxSize)) {
162
163 // if the buffer was too small, then we are receiving datagrams
164 // and the datagram was too big
165 if (PChannel::GetErrorCode() == PChannel::BufferTooSmall)
166 lastErrorCode = RxBufferTooSmall;
167 else
168 lastErrorCode = NoResponse;
169 return PFalse;
170
171 } else if ((rxSize + GetLastReadCount()) >= 10)
172 break;
173
174 else
175 rxSize += GetLastReadCount();
176 }
177
178 rxSize += GetLastReadCount();
179
180 PINDEX hdrLen = 1;
181
182 // if not a valid sequence header, then stop reading
183 WORD len;
184 if ((readBuffer[0] != 0x30) ||
185 !PASNObject::DecodeASNLength(readBuffer, hdrLen, len)) {
186 lastErrorCode = MalformedResponse;
187 return PFalse;
188 }
189
190 // length of packet is length of header + length of data
191 len = (WORD)(len + hdrLen);
192
193 // return PTrue if we have the packet, else return PFalse
194 if (len <= maxRxSize)
195 return PTrue;
196
197 lastErrorCode = RxBufferTooSmall;
198 return PFalse;
199
200 #if 0
201 // and get a new data ptr
202 if (maxRxSize < len)
203 readBuffer.SetSize(len);
204
205 // read the remainder of the packet
206 while (rxSize < len) {
207 if (!Read(readBuffer.GetPointer()+rxSize, len - rxSize)) {
208 lastErrorCode = NoResponse;
209 return PFalse;
210 }
211 rxSize += GetLastReadCount();
212 }
213 return PTrue;
214 #endif
215 }
216
WriteRequest(PASNInt requestCode,PSNMPVarBindingList & vars,PSNMPVarBindingList & varsOut)217 PBoolean PSNMPClient::WriteRequest(PASNInt requestCode,
218 PSNMPVarBindingList & vars,
219 PSNMPVarBindingList & varsOut)
220 {
221 PASNSequence pdu;
222 PASNSequence * pduData = new PASNSequence((BYTE)requestCode);
223 PASNSequence * bindingList = new PASNSequence();
224
225 lastErrorIndex = 0;
226
227 // build a get request PDU
228 pdu.AppendInteger(version);
229 pdu.AppendString(community);
230 pdu.Append(pduData);
231
232 // build the PDU data
233 PASNInt thisRequestId = requestId;
234 requestId = rand() % 0x7fffffff;
235 pduData->AppendInteger(thisRequestId);
236 pduData->AppendInteger(0); // error status
237 pduData->AppendInteger(0); // error index
238 pduData->Append(bindingList); // binding list
239
240 // build the binding list
241 PINDEX i;
242 for (i = 0; i < vars.GetSize(); i++) {
243 PASNSequence * binding = new PASNSequence();
244 bindingList->Append(binding);
245 binding->AppendObjectID(vars.GetObjectID(i));
246 binding->Append((PASNObject *)vars[i].Clone());
247 }
248
249 // encode the PDU into a buffer
250 PBYTEArray sendBuffer;
251 pdu.Encode(sendBuffer);
252
253 if (sendBuffer.GetSize() > maxTxSize) {
254 lastErrorCode = TxDataTooBig;
255 return PFalse;
256 }
257
258 varsOut.RemoveAll();
259
260 PINDEX retry = retryMax;
261
262 for (;;) {
263
264 // send the packet
265 if (!Write(sendBuffer, sendBuffer.GetSize())) {
266 lastErrorCode = SendFailed;
267 return PFalse;
268 }
269
270 // receive a packet
271 if (ReadRequest(readBuffer))
272 break;
273 else if ((lastErrorCode != NoResponse) || (retry == 0))
274 return PFalse;
275 else
276 retry--;
277 }
278
279 // parse the response
280 PASNSequence response(readBuffer);
281 PINDEX seqLen = response.GetSize();
282
283 // check PDU
284 if (seqLen != 3 ||
285 response[0].GetType() != PASNObject::Integer ||
286 response[1].GetType() != PASNObject::String ||
287 response[2].GetType() != PASNObject::Choice) {
288 lastErrorCode = MalformedResponse;
289 return PFalse;
290 }
291
292 // check the PDU data
293 const PASNSequence & rPduData = response[2].GetSequence();
294 seqLen = rPduData.GetSize();
295 if (seqLen != 4 ||
296 rPduData.GetChoice() != GetResponse ||
297 rPduData[0].GetType() != PASNObject::Integer ||
298 rPduData[1].GetType() != PASNObject::Integer ||
299 rPduData[2].GetType() != PASNObject::Integer ||
300 rPduData[3].GetType() != PASNObject::Sequence) {
301 lastErrorCode = MalformedResponse;
302 return PFalse;
303 }
304
305 // check the request ID
306 PASNInt returnedRequestId = rPduData[0].GetInteger();
307 if (returnedRequestId != thisRequestId) {
308 lastErrorCode = MalformedResponse;
309 return PFalse;
310 }
311
312 // check the error status and return if non-zero
313 PASNInt errorStatus = rPduData[1].GetInteger();
314 if (errorStatus != 0) {
315 lastErrorIndex = rPduData[2].GetInteger();
316 lastErrorCode = (ErrorType)errorStatus;
317 return PFalse;
318 }
319
320 // check the variable bindings
321 const PASNSequence & rBindings = rPduData[3].GetSequence();
322 PINDEX bindingCount = rBindings.GetSize();
323
324 // create the return list
325 for (i = 0; i < bindingCount; i++) {
326 if (rBindings[i].GetType() != PASNObject::Sequence) {
327 lastErrorIndex = i+1;
328 lastErrorCode = MalformedResponse;
329 return PFalse;
330 }
331 const PASNSequence & rVar = rBindings[i].GetSequence();
332 if (rVar.GetSize() != 2 ||
333 rVar[0].GetType() != PASNObject::ObjectID) {
334 lastErrorIndex = i+1;
335 lastErrorCode = MalformedResponse;
336 return PFalse;
337 }
338 varsOut.Append(rVar[0].GetString(), (PASNObject *)rVar[1].Clone());
339 }
340
341 lastErrorCode = NoError;
342 return PTrue;
343 }
344
345
346
347 ///////////////////////////////////////////////////////////////
348 //
349 // Trap routines
350 //
351
SendEnterpriseTrap(const PIPSocket::Address & addr,const PString & community,const PString & enterprise,PINDEX specificTrap,PASNUnsigned timeTicks,WORD sendPort)352 void PSNMP::SendEnterpriseTrap (
353 const PIPSocket::Address & addr,
354 const PString & community,
355 const PString & enterprise,
356 PINDEX specificTrap,
357 PASNUnsigned timeTicks,
358 WORD sendPort)
359 {
360 PSNMPVarBindingList vars;
361 SendTrap(addr,
362 EnterpriseSpecific,
363 community,
364 enterprise,
365 specificTrap,
366 timeTicks,
367 vars,
368 sendPort);
369 }
370
371
SendEnterpriseTrap(const PIPSocket::Address & addr,const PString & community,const PString & enterprise,PINDEX specificTrap,PASNUnsigned timeTicks,const PSNMPVarBindingList & vars,WORD sendPort)372 void PSNMP::SendEnterpriseTrap (
373 const PIPSocket::Address & addr,
374 const PString & community,
375 const PString & enterprise,
376 PINDEX specificTrap,
377 PASNUnsigned timeTicks,
378 const PSNMPVarBindingList & vars,
379 WORD sendPort)
380 {
381 SendTrap(addr,
382 EnterpriseSpecific,
383 community,
384 enterprise,
385 specificTrap,
386 timeTicks,
387 vars,
388 sendPort);
389 }
390
391
SendTrap(const PIPSocket::Address & addr,PSNMP::TrapType trapType,const PString & community,const PString & enterprise,PINDEX specificTrap,PASNUnsigned timeTicks,const PSNMPVarBindingList & vars,WORD sendPort)392 void PSNMP::SendTrap(const PIPSocket::Address & addr,
393 PSNMP::TrapType trapType,
394 const PString & community,
395 const PString & enterprise,
396 PINDEX specificTrap,
397 PASNUnsigned timeTicks,
398 const PSNMPVarBindingList & vars,
399 WORD sendPort)
400 {
401 PIPSocket::Address agentAddress;
402 PIPSocket::GetHostAddress(agentAddress);
403 SendTrap(addr,
404 trapType,
405 community,
406 enterprise,
407 specificTrap,
408 timeTicks,
409 vars,
410 agentAddress,
411 sendPort);
412 }
413
414
SendTrap(const PIPSocket::Address & addr,PSNMP::TrapType trapType,const PString & community,const PString & enterprise,PINDEX specificTrap,PASNUnsigned timeTicks,const PSNMPVarBindingList & vars,const PIPSocket::Address & agentAddress,WORD sendPort)415 void PSNMP::SendTrap(const PIPSocket::Address & addr,
416 PSNMP::TrapType trapType,
417 const PString & community,
418 const PString & enterprise,
419 PINDEX specificTrap,
420 PASNUnsigned timeTicks,
421 const PSNMPVarBindingList & vars,
422 const PIPSocket::Address & agentAddress,
423 WORD sendPort)
424
425 {
426 // send the trap to specified remote host
427 PUDPSocket socket(addr, sendPort);
428 if (socket.IsOpen())
429 WriteTrap(socket, trapType, community, enterprise,
430 specificTrap, timeTicks, vars, agentAddress);
431 }
432
WriteTrap(PChannel & channel,PSNMP::TrapType trapType,const PString & community,const PString & enterprise,PINDEX specificTrap,PASNUnsigned timeTicks,const PSNMPVarBindingList & vars,const PIPSocket::Address & agentAddress)433 void PSNMP::WriteTrap( PChannel & channel,
434 PSNMP::TrapType trapType,
435 const PString & community,
436 const PString & enterprise,
437 PINDEX specificTrap,
438 PASNUnsigned timeTicks,
439 const PSNMPVarBindingList & vars,
440 const PIPSocket::Address & agentAddress)
441 {
442 PASNSequence pdu;
443 PASNSequence * pduData = new PASNSequence((BYTE)Trap);
444 PASNSequence * bindingList = new PASNSequence();
445
446 // build a trap PDU PDU
447 pdu.AppendInteger(1);
448 pdu.AppendString(community);
449 pdu.Append(pduData);
450
451 // build the PDU data
452 pduData->AppendObjectID(enterprise); // enterprise
453 pduData->Append(new PASNIPAddress(agentAddress)); // agent address
454 pduData->AppendInteger((int)trapType); // trap type
455 pduData->AppendInteger(specificTrap); // specific trap
456 pduData->Append(new PASNTimeTicks(timeTicks)); // time of event
457 pduData->Append(bindingList); // binding list
458
459 // build the binding list
460 for (PINDEX i = 0; i < vars.GetSize(); i++) {
461 PASNSequence * binding = new PASNSequence();
462 bindingList->Append(binding);
463 binding->AppendObjectID(vars.GetObjectID(i));
464 binding->Append((PASNObject *)vars[i].Clone());
465 }
466
467 // encode the PDU into a buffer
468 PBYTEArray sendBuffer;
469 pdu.Encode(sendBuffer);
470
471 // send the trap to specified remote host
472 channel.Write(sendBuffer, sendBuffer.GetSize());
473 }
474
475 #endif // P_SNMP
476
477 // End Of File ///////////////////////////////////////////////////////////////
478