changes in string set API
[public/netxms.git] / src / server / libnxsrv / snmp.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** Copyright (C) 2003-2014 Victor Kirhenshtein
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This program is distributed in the hope that it will be useful,
11 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 ** GNU General Public License for more details.
14 **
15 ** You should have received a copy of the GNU General Public License
16 ** along with this program; if not, write to the Free Software
17 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 **
19 ** File: snmp.cpp
20 **
21 **/
22
23 #include "libnxsrv.h"
24
25 /**
26 * Unique request ID
27 */
28 static VolatileCounter s_requestId = 1;
29
30 /**
31 * Generate new request ID
32 */
33 UINT32 LIBNXSRV_EXPORTABLE SnmpNewRequestId()
34 {
35 return (UINT32)InterlockedIncrement(&s_requestId);
36 }
37
38 /**
39 * Get value for SNMP variable
40 * If szOidStr is not NULL, string representation of OID is used, otherwise -
41 * binary representation from oidBinary and dwOidLen
42 * Note: buffer size is in bytes
43 */
44 UINT32 LIBNXSRV_EXPORTABLE SnmpGet(int version, SNMP_Transport *transport,
45 const TCHAR *szOidStr, const UINT32 *oidBinary, size_t dwOidLen, void *pValue,
46 size_t bufferSize, UINT32 dwFlags)
47 {
48 if (version != transport->getSnmpVersion())
49 {
50 int v = transport->getSnmpVersion();
51 transport->setSnmpVersion(version);
52 DbgPrintf(7, _T("SnmpGet: transport SNMP version %d changed to %d"), v, version);
53 UINT32 rc = SnmpGetEx(transport, szOidStr, oidBinary, dwOidLen, pValue, bufferSize, dwFlags, NULL);
54 transport->setSnmpVersion(v);
55 return rc;
56 }
57 else
58 {
59 return SnmpGetEx(transport, szOidStr, oidBinary, dwOidLen, pValue, bufferSize, dwFlags, NULL);
60 }
61 }
62
63 /**
64 * Get value for SNMP variable
65 * If szOidStr is not NULL, string representation of OID is used, otherwise -
66 * binary representation from oidBinary and dwOidLen
67 * If SG_RAW_RESULT flag given and dataLen is not NULL actial data length will be stored there
68 * Note: buffer size is in bytes
69 */
70 UINT32 LIBNXSRV_EXPORTABLE SnmpGetEx(SNMP_Transport *pTransport,
71 const TCHAR *szOidStr, const UINT32 *oidBinary, size_t dwOidLen, void *pValue,
72 size_t bufferSize, UINT32 dwFlags, UINT32 *dataLen)
73 {
74 SNMP_PDU *pRqPDU, *pRespPDU;
75 UINT32 pdwVarName[MAX_OID_LEN], dwResult = SNMP_ERR_SUCCESS;
76 size_t nameLength;
77
78 if (pTransport == NULL)
79 return SNMP_ERR_COMM;
80
81 // Create PDU and send request
82 pRqPDU = new SNMP_PDU(SNMP_GET_REQUEST, (UINT32)InterlockedIncrement(&s_requestId), pTransport->getSnmpVersion());
83 if (szOidStr != NULL)
84 {
85 nameLength = SNMPParseOID(szOidStr, pdwVarName, MAX_OID_LEN);
86 if (nameLength == 0)
87 {
88 InetAddress a = pTransport->getPeerIpAddress();
89 nxlog_write(MSG_OID_PARSE_ERROR, EVENTLOG_ERROR_TYPE, "ssA", szOidStr, _T("SnmpGet"), &a);
90 dwResult = SNMP_ERR_BAD_OID;
91 }
92 }
93 else
94 {
95 memcpy(pdwVarName, oidBinary, dwOidLen * sizeof(UINT32));
96 nameLength = dwOidLen;
97 }
98
99 if (dwResult == SNMP_ERR_SUCCESS) // Still no errors
100 {
101 pRqPDU->bindVariable(new SNMP_Variable(pdwVarName, nameLength));
102 dwResult = pTransport->doRequest(pRqPDU, &pRespPDU, g_snmpTimeout, 3);
103
104 // Analyze response
105 if (dwResult == SNMP_ERR_SUCCESS)
106 {
107 if ((pRespPDU->getNumVariables() > 0) &&
108 (pRespPDU->getErrorCode() == SNMP_PDU_ERR_SUCCESS))
109 {
110 SNMP_Variable *pVar = pRespPDU->getVariable(0);
111
112 if ((pVar->getType() != ASN_NO_SUCH_OBJECT) &&
113 (pVar->getType() != ASN_NO_SUCH_INSTANCE))
114 {
115 if (dwFlags & SG_RAW_RESULT)
116 {
117 pVar->getRawValue((BYTE *)pValue, bufferSize);
118 if (dataLen != NULL)
119 *dataLen = (UINT32)pVar->getValueLength();
120 }
121 else if (dwFlags & SG_HSTRING_RESULT)
122 {
123 size_t rawLen = (bufferSize - sizeof(TCHAR)) / 2 / sizeof(TCHAR);
124 BYTE *raw = (BYTE *)malloc(rawLen);
125 rawLen = (int)pVar->getRawValue(raw, rawLen);
126 BinToStr(raw, rawLen, (TCHAR *)pValue);
127 free(raw);
128 }
129 else if (dwFlags & SG_STRING_RESULT)
130 {
131 pVar->getValueAsString((TCHAR *)pValue, bufferSize / sizeof(TCHAR));
132 }
133 else if (dwFlags & SG_PSTRING_RESULT)
134 {
135 bool convert = true;
136 pVar->getValueAsPrintableString((TCHAR *)pValue, bufferSize / sizeof(TCHAR), &convert);
137 }
138 else
139 {
140 switch(pVar->getType())
141 {
142 case ASN_INTEGER:
143 case ASN_UINTEGER32:
144 case ASN_COUNTER32:
145 case ASN_GAUGE32:
146 case ASN_TIMETICKS:
147 *((INT32 *)pValue) = pVar->getValueAsInt();
148 break;
149 case ASN_IP_ADDR:
150 *((UINT32 *)pValue) = ntohl(pVar->getValueAsUInt());
151 break;
152 case ASN_OCTET_STRING:
153 pVar->getValueAsString((TCHAR *)pValue, bufferSize / sizeof(TCHAR));
154 break;
155 case ASN_OBJECT_ID:
156 pVar->getValueAsString((TCHAR *)pValue, bufferSize / sizeof(TCHAR));
157 break;
158 case ASN_NULL:
159 dwResult = SNMP_ERR_NO_OBJECT;
160 break;
161 default:
162 nxlog_write(MSG_SNMP_UNKNOWN_TYPE, NXLOG_WARNING, "x", pVar->getType());
163 dwResult = SNMP_ERR_BAD_TYPE;
164 break;
165 }
166 }
167 }
168 else
169 {
170 dwResult = SNMP_ERR_NO_OBJECT;
171 }
172 }
173 else
174 {
175 if (pRespPDU->getErrorCode() == SNMP_PDU_ERR_NO_SUCH_NAME)
176 dwResult = SNMP_ERR_NO_OBJECT;
177 else
178 dwResult = SNMP_ERR_AGENT;
179 }
180 delete pRespPDU;
181 }
182 else
183 {
184 if (dwFlags & SG_VERBOSE)
185 nxlog_write(MSG_SNMP_GET_ERROR, EVENTLOG_ERROR_TYPE, "d", dwResult);
186 }
187 }
188
189 delete pRqPDU;
190 return dwResult;
191 }
192
193 /**
194 * Enumerate multiple values by walking through MIB, starting at given root
195 */
196 UINT32 LIBNXSRV_EXPORTABLE SnmpWalk(UINT32 dwVersion, SNMP_Transport *pTransport, const TCHAR *szRootOid,
197 UINT32 (* pHandler)(UINT32, SNMP_Variable *, SNMP_Transport *, void *),
198 void *pUserArg, BOOL bVerbose)
199 {
200 if (pTransport == NULL)
201 return SNMP_ERR_COMM;
202
203 // Get root
204 UINT32 pdwRootName[MAX_OID_LEN];
205 size_t dwRootLen = SNMPParseOID(szRootOid, pdwRootName, MAX_OID_LEN);
206 if (dwRootLen == 0)
207 {
208 InetAddress a = pTransport->getPeerIpAddress();
209 nxlog_write(MSG_OID_PARSE_ERROR, EVENTLOG_ERROR_TYPE, "ssA", szRootOid, _T("SnmpWalk"), &a);
210 return SNMP_ERR_BAD_OID;
211 }
212
213 // First OID to request
214 UINT32 pdwName[MAX_OID_LEN];
215 memcpy(pdwName, pdwRootName, dwRootLen * sizeof(UINT32));
216 size_t nameLength = dwRootLen;
217
218 // Walk the MIB
219 UINT32 dwResult;
220 BOOL bRunning = TRUE;
221 UINT32 firstObjectName[MAX_OID_LEN];
222 size_t firstObjectNameLen = 0;
223 while(bRunning)
224 {
225 SNMP_PDU *pRqPDU = new SNMP_PDU(SNMP_GET_NEXT_REQUEST, (UINT32)InterlockedIncrement(&s_requestId), dwVersion);
226 pRqPDU->bindVariable(new SNMP_Variable(pdwName, nameLength));
227 SNMP_PDU *pRespPDU;
228 dwResult = pTransport->doRequest(pRqPDU, &pRespPDU, g_snmpTimeout, 3);
229
230 // Analyze response
231 if (dwResult == SNMP_ERR_SUCCESS)
232 {
233 if ((pRespPDU->getNumVariables() > 0) &&
234 (pRespPDU->getErrorCode() == SNMP_PDU_ERR_SUCCESS))
235 {
236 SNMP_Variable *pVar = pRespPDU->getVariable(0);
237
238 if ((pVar->getType() != ASN_NO_SUCH_OBJECT) &&
239 (pVar->getType() != ASN_NO_SUCH_INSTANCE))
240 {
241 // Should we stop walking?
242 // Some buggy SNMP agents may return first value after last one
243 // (Toshiba Strata CTX do that for example), so last check is here
244 if ((pVar->getName()->getLength() < dwRootLen) ||
245 (memcmp(pdwRootName, pVar->getName()->getValue(), dwRootLen * sizeof(UINT32))) ||
246 (pVar->getName()->compare(pdwName, nameLength) == OID_EQUAL) ||
247 (pVar->getName()->compare(firstObjectName, firstObjectNameLen) == OID_EQUAL))
248 {
249 bRunning = FALSE;
250 delete pRespPDU;
251 delete pRqPDU;
252 break;
253 }
254 nameLength = pVar->getName()->getLength();
255 memcpy(pdwName, pVar->getName()->getValue(), nameLength * sizeof(UINT32));
256 if (firstObjectNameLen == 0)
257 {
258 firstObjectNameLen = nameLength;
259 memcpy(firstObjectName, pdwName, nameLength * sizeof(UINT32));
260 }
261
262 // Call user's callback function for processing
263 dwResult = pHandler(dwVersion, pVar, pTransport, pUserArg);
264 if (dwResult != SNMP_ERR_SUCCESS)
265 {
266 bRunning = FALSE;
267 }
268 }
269 else
270 {
271 // Consider no object/no instance as end of walk signal instead of failure
272 bRunning = FALSE;
273 }
274 }
275 else
276 {
277 // Some SNMP agents sends NO_SUCH_NAME PDU error after last element in MIB
278 if (pRespPDU->getErrorCode() != SNMP_PDU_ERR_NO_SUCH_NAME)
279 dwResult = SNMP_ERR_AGENT;
280 bRunning = FALSE;
281 }
282 delete pRespPDU;
283 }
284 else
285 {
286 if (bVerbose)
287 nxlog_write(MSG_SNMP_GET_ERROR, EVENTLOG_ERROR_TYPE, "d", dwResult);
288 bRunning = FALSE;
289 }
290 delete pRqPDU;
291 }
292 return dwResult;
293 }