Added IsSupportingRTP function to simplify detecting when STUN supports RTP
[pwlib.git] / src / ptclib / snmpclnt.cxx
blob5d32a39c3255a9f84bb3df2ee7c879d47c769395
1 /*
2 * snmpclnt.cxx
4 * SNMP Client class
6 * Portable Windows Library
8 * Copyright (c) 1993-2002 Equivalence Pty. Ltd.
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/
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.
20 * The Original Code is Portable Windows Library.
22 * The Initial Developer of the Original Code is Equivalence Pty. Ltd.
24 * Contributor(s): ______________________________________.
26 * $Log$
27 * Revision 1.9 2002/11/06 22:47:25 robertj
28 * Fixed header comment (copyright etc)
30 * Revision 1.8 1998/11/30 04:52:07 robertj
31 * New directory structure
33 * Revision 1.7 1998/10/13 14:06:34 robertj
34 * Complete rewrite of memory leak detection code.
36 * Revision 1.6 1998/09/23 06:22:40 robertj
37 * Added open source copyright license.
39 * Revision 1.5 1996/11/10 21:06:17 robertj
40 * Fixed endless retry bug in SNMP get.
42 * Revision 1.4 1996/11/04 04:00:00 robertj
43 * Added support for UDP packet truncation is reading SNMP reply.
45 * Revision 1.3 1996/10/08 13:06:24 robertj
46 * Fixed SNMP timeout (GNU compatibility).
48 * Revision 1.2 1996/09/20 12:20:19 robertj
49 * Used read timeout instead of member variable.
51 * Revision 1.1 1996/09/14 13:14:59 robertj
52 * Initial revision
56 #include <ptlib.h>
57 #include <ptclib/psnmp.h>
59 #define new PNEW
62 #define SNMP_VERSION 0
63 #define SNMP_PORT "snmp 161"
65 static const char defaultCommunity[] = "public";
68 ///////////////////////////////////////////////////////////////
70 // PSNMPClient
73 PSNMPClient::PSNMPClient(PINDEX retry, PINDEX timeout,
74 PINDEX rxSize, PINDEX txSize)
75 : community(defaultCommunity),
76 version(SNMP_VERSION),
77 retryMax(retry),
78 maxRxSize(rxSize),
79 maxTxSize(txSize)
81 SetReadTimeout(PTimeInterval(0, timeout));
82 requestId = rand() % 0x7fffffff;
86 PSNMPClient::PSNMPClient(const PString & host, PINDEX retry,
87 PINDEX timeout, PINDEX rxSize, PINDEX txSize)
88 : hostName(host),
89 community(defaultCommunity),
90 version(SNMP_VERSION),
91 retryMax(retry),
92 maxRxSize(rxSize),
93 maxTxSize(txSize)
95 SetReadTimeout(PTimeInterval(0, timeout));
96 Open(new PUDPSocket(host, SNMP_PORT));
97 requestId = rand() % 0x7fffffff;
101 void PSNMPClient::SetVersion(PASNInt newVersion)
103 version = newVersion;
107 PASNInt PSNMPClient::GetVersion() const
109 return version;
113 void PSNMPClient::SetCommunity(const PString & str)
115 community = str;
119 PString PSNMPClient::GetCommunity() const
121 return community;
125 void PSNMPClient::SetRequestID(PASNInt newRequestID)
127 requestId = newRequestID;
131 PASNInt PSNMPClient::GetRequestID() const
133 return requestId;
137 BOOL PSNMPClient::WriteGetRequest(PSNMPVarBindingList & varsIn,
138 PSNMPVarBindingList & varsOut)
140 return WriteRequest(GetRequest, varsIn, varsOut);
144 BOOL PSNMPClient::WriteGetNextRequest(PSNMPVarBindingList & varsIn,
145 PSNMPVarBindingList & varsOut)
147 return WriteRequest(GetNextRequest, varsIn, varsOut);
151 BOOL PSNMPClient::WriteSetRequest(PSNMPVarBindingList & varsIn,
152 PSNMPVarBindingList & varsOut)
154 return WriteRequest(SetRequest, varsIn, varsOut);
158 PSNMP::ErrorType PSNMPClient::GetLastErrorCode() const
160 return lastErrorCode;
164 PINDEX PSNMPClient::GetLastErrorIndex() const
166 return lastErrorIndex;
170 PString PSNMPClient::GetLastErrorText() const
172 return PSNMP::GetErrorText(lastErrorCode);
175 BOOL PSNMPClient::ReadRequest(PBYTEArray & readBuffer)
177 readBuffer.SetSize(maxRxSize);
178 PINDEX rxSize = 0;
180 for (;;) {
182 if (!Read(readBuffer.GetPointer()+rxSize, maxRxSize - rxSize)) {
184 // if the buffer was too small, then we are receiving datagrams
185 // and the datagram was too big
186 if (PChannel::GetErrorCode() == PChannel::BufferTooSmall)
187 lastErrorCode = RxBufferTooSmall;
188 else
189 lastErrorCode = NoResponse;
190 return FALSE;
192 } else if ((rxSize + GetLastReadCount()) >= 10)
193 break;
195 else
196 rxSize += GetLastReadCount();
199 rxSize += GetLastReadCount();
201 PINDEX hdrLen = 1;
203 // if not a valid sequence header, then stop reading
204 WORD len;
205 if ((readBuffer[0] != 0x30) ||
206 !PASNObject::DecodeASNLength(readBuffer, hdrLen, len)) {
207 lastErrorCode = MalformedResponse;
208 return FALSE;
211 // length of packet is length of header + length of data
212 len = (WORD)(len + hdrLen);
214 // return TRUE if we have the packet, else return FALSE
215 if (len <= maxRxSize)
216 return TRUE;
218 lastErrorCode = RxBufferTooSmall;
219 return FALSE;
221 #if 0
222 // and get a new data ptr
223 if (maxRxSize < len)
224 readBuffer.SetSize(len);
226 // read the remainder of the packet
227 while (rxSize < len) {
228 if (!Read(readBuffer.GetPointer()+rxSize, len - rxSize)) {
229 lastErrorCode = NoResponse;
230 return FALSE;
232 rxSize += GetLastReadCount();
234 return TRUE;
235 #endif
238 BOOL PSNMPClient::WriteRequest(PASNInt requestCode,
239 PSNMPVarBindingList & vars,
240 PSNMPVarBindingList & varsOut)
242 PASNSequence pdu;
243 PASNSequence * pduData = new PASNSequence((BYTE)requestCode);
244 PASNSequence * bindingList = new PASNSequence();
246 lastErrorIndex = 0;
248 // build a get request PDU
249 pdu.AppendInteger(version);
250 pdu.AppendString(community);
251 pdu.Append(pduData);
253 // build the PDU data
254 PASNInt thisRequestId = requestId;
255 requestId = rand() % 0x7fffffff;
256 pduData->AppendInteger(thisRequestId);
257 pduData->AppendInteger(0); // error status
258 pduData->AppendInteger(0); // error index
259 pduData->Append(bindingList); // binding list
261 // build the binding list
262 PINDEX i;
263 for (i = 0; i < vars.GetSize(); i++) {
264 PASNSequence * binding = new PASNSequence();
265 bindingList->Append(binding);
266 binding->AppendObjectID(vars.GetObjectID(i));
267 binding->Append((PASNObject *)vars[i].Clone());
270 // encode the PDU into a buffer
271 PBYTEArray sendBuffer;
272 pdu.Encode(sendBuffer);
274 if (sendBuffer.GetSize() > maxTxSize) {
275 lastErrorCode = TxDataTooBig;
276 return FALSE;
279 varsOut.RemoveAll();
281 PINDEX retry = retryMax;
283 for (;;) {
285 // send the packet
286 if (!Write(sendBuffer, sendBuffer.GetSize())) {
287 lastErrorCode = SendFailed;
288 return FALSE;
291 // receive a packet
292 if (ReadRequest(readBuffer))
293 break;
294 else if ((lastErrorCode != NoResponse) || (retry == 0))
295 return FALSE;
296 else
297 retry--;
300 // parse the response
301 PASNSequence response(readBuffer);
302 PINDEX seqLen = response.GetSize();
304 // check PDU
305 if (seqLen != 3 ||
306 response[0].GetType() != PASNObject::Integer ||
307 response[1].GetType() != PASNObject::String ||
308 response[2].GetType() != PASNObject::Choice) {
309 lastErrorCode = MalformedResponse;
310 return FALSE;
313 // check the PDU data
314 const PASNSequence & rPduData = response[2].GetSequence();
315 seqLen = rPduData.GetSize();
316 if (seqLen != 4 ||
317 rPduData.GetChoice() != GetResponse ||
318 rPduData[0].GetType() != PASNObject::Integer ||
319 rPduData[1].GetType() != PASNObject::Integer ||
320 rPduData[2].GetType() != PASNObject::Integer ||
321 rPduData[3].GetType() != PASNObject::Sequence) {
322 lastErrorCode = MalformedResponse;
323 return FALSE;
326 // check the request ID
327 PASNInt returnedRequestId = rPduData[0].GetInteger();
328 if (returnedRequestId != thisRequestId) {
329 lastErrorCode = MalformedResponse;
330 return FALSE;
333 // check the error status and return if non-zero
334 PASNInt errorStatus = rPduData[1].GetInteger();
335 if (errorStatus != 0) {
336 lastErrorIndex = rPduData[2].GetInteger();
337 lastErrorCode = (ErrorType)errorStatus;
338 return FALSE;
341 // check the variable bindings
342 const PASNSequence & rBindings = rPduData[3].GetSequence();
343 PINDEX bindingCount = rBindings.GetSize();
345 // create the return list
346 for (i = 0; i < bindingCount; i++) {
347 if (rBindings[i].GetType() != PASNObject::Sequence) {
348 lastErrorIndex = i+1;
349 lastErrorCode = MalformedResponse;
350 return FALSE;
352 const PASNSequence & rVar = rBindings[i].GetSequence();
353 if (rVar.GetSize() != 2 ||
354 rVar[0].GetType() != PASNObject::ObjectID) {
355 lastErrorIndex = i+1;
356 lastErrorCode = MalformedResponse;
357 return FALSE;
359 varsOut.Append(rVar[0].GetString(), (PASNObject *)rVar[1].Clone());
362 lastErrorCode = NoError;
363 return TRUE;
368 ///////////////////////////////////////////////////////////////
370 // Trap routines
373 void PSNMP::SendEnterpriseTrap (
374 const PIPSocket::Address & addr,
375 const PString & community,
376 const PString & enterprise,
377 PINDEX specificTrap,
378 PASNUnsigned timeTicks,
379 WORD sendPort)
381 PSNMPVarBindingList vars;
382 SendTrap(addr,
383 EnterpriseSpecific,
384 community,
385 enterprise,
386 specificTrap,
387 timeTicks,
388 vars,
389 sendPort);
393 void PSNMP::SendEnterpriseTrap (
394 const PIPSocket::Address & addr,
395 const PString & community,
396 const PString & enterprise,
397 PINDEX specificTrap,
398 PASNUnsigned timeTicks,
399 const PSNMPVarBindingList & vars,
400 WORD sendPort)
402 SendTrap(addr,
403 EnterpriseSpecific,
404 community,
405 enterprise,
406 specificTrap,
407 timeTicks,
408 vars,
409 sendPort);
413 void PSNMP::SendTrap(const PIPSocket::Address & addr,
414 PSNMP::TrapType trapType,
415 const PString & community,
416 const PString & enterprise,
417 PINDEX specificTrap,
418 PASNUnsigned timeTicks,
419 const PSNMPVarBindingList & vars,
420 WORD sendPort)
422 PIPSocket::Address agentAddress;
423 PIPSocket::GetHostAddress(agentAddress);
424 SendTrap(addr,
425 trapType,
426 community,
427 enterprise,
428 specificTrap,
429 timeTicks,
430 vars,
431 agentAddress,
432 sendPort);
436 void PSNMP::SendTrap(const PIPSocket::Address & addr,
437 PSNMP::TrapType trapType,
438 const PString & community,
439 const PString & enterprise,
440 PINDEX specificTrap,
441 PASNUnsigned timeTicks,
442 const PSNMPVarBindingList & vars,
443 const PIPSocket::Address & agentAddress,
444 WORD sendPort)
447 // send the trap to specified remote host
448 PUDPSocket socket(addr, sendPort);
449 if (socket.IsOpen())
450 WriteTrap(socket, trapType, community, enterprise,
451 specificTrap, timeTicks, vars, agentAddress);
454 void PSNMP::WriteTrap( PChannel & channel,
455 PSNMP::TrapType trapType,
456 const PString & community,
457 const PString & enterprise,
458 PINDEX specificTrap,
459 PASNUnsigned timeTicks,
460 const PSNMPVarBindingList & vars,
461 const PIPSocket::Address & agentAddress)
463 PASNSequence pdu;
464 PASNSequence * pduData = new PASNSequence((BYTE)Trap);
465 PASNSequence * bindingList = new PASNSequence();
467 // build a trap PDU PDU
468 pdu.AppendInteger(1);
469 pdu.AppendString(community);
470 pdu.Append(pduData);
472 // build the PDU data
473 pduData->AppendObjectID(enterprise); // enterprise
474 pduData->Append(new PASNIPAddress(agentAddress)); // agent address
475 pduData->AppendInteger((int)trapType); // trap type
476 pduData->AppendInteger(specificTrap); // specific trap
477 pduData->Append(new PASNTimeTicks(timeTicks)); // time of event
478 pduData->Append(bindingList); // binding list
480 // build the binding list
481 for (PINDEX i = 0; i < vars.GetSize(); i++) {
482 PASNSequence * binding = new PASNSequence();
483 bindingList->Append(binding);
484 binding->AppendObjectID(vars.GetObjectID(i));
485 binding->Append((PASNObject *)vars[i].Clone());
488 // encode the PDU into a buffer
489 PBYTEArray sendBuffer;
490 pdu.Encode(sendBuffer);
492 // send the trap to specified remote host
493 channel.Write(sendBuffer, sendBuffer.GetSize());
497 // End Of File ///////////////////////////////////////////////////////////////