fixed incorrect fallback character conversion
[public/netxms.git] / src / snmp / libnxsnmp / variable.cpp
CommitLineData
5039dede
AK
1/*
2** NetXMS - Network Management System
3** SNMP support library
9ceab287 4** Copyright (C) 2003-2016 Victor Kirhenshtein
5039dede
AK
5**
6** This program is free software; you can redistribute it and/or modify
65d2c384
VK
7** it under the terms of the GNU Lesser General Public License as published by
8** the Free Software Foundation; either version 3 of the License, or
5039dede
AK
9** (at your option) any later version.
10**
11** This program is distributed in the hope that it will be useful,
12** but WITHOUT ANY WARRANTY; without even the implied warranty of
13** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14** GNU General Public License for more details.
15**
65d2c384 16** You should have received a copy of the GNU Lesser General Public License
5039dede
AK
17** along with this program; if not, write to the Free Software
18** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19**
20** File: variable.cpp
21**
22**/
23
24#include "libnxsnmp.h"
25
638c64a3
VK
26/**
27 * SNMP_Variable default constructor
28 */
5039dede
AK
29SNMP_Variable::SNMP_Variable()
30{
e0471fad
VK
31 m_value = NULL;
32 m_type = ASN_NULL;
33 m_valueLength = 0;
5039dede
AK
34}
35
638c64a3
VK
36/**
37 * Create variable of ASN_NULL type
38 */
e0471fad 39SNMP_Variable::SNMP_Variable(const TCHAR *name)
5039dede 40{
9ceab287 41 m_name = SNMP_ObjectId::parse(name);
e0471fad
VK
42 m_value = NULL;
43 m_type = ASN_NULL;
44 m_valueLength = 0;
9ceab287 45}
5039dede 46
9ceab287
VK
47/**
48 * Create variable of ASN_NULL type
49 */
50SNMP_Variable::SNMP_Variable(const UINT32 *name, size_t nameLen) : m_name(name, nameLen)
51{
52 m_value = NULL;
53 m_type = ASN_NULL;
54 m_valueLength = 0;
5039dede
AK
55}
56
f3d71512
VK
57/**
58 * Create variable of ASN_NULL type
59 */
9ceab287 60SNMP_Variable::SNMP_Variable(const SNMP_ObjectId &name) : m_name(name)
5039dede 61{
e0471fad
VK
62 m_value = NULL;
63 m_type = ASN_NULL;
64 m_valueLength = 0;
e0471fad
VK
65}
66
67/**
68 * Copy constructor
69 */
9c5ebc32 70SNMP_Variable::SNMP_Variable(const SNMP_Variable *src)
e0471fad
VK
71{
72 m_valueLength = src->m_valueLength;
73 m_value = (src->m_value != NULL) ? (BYTE *)nx_memdup(src->m_value, src->m_valueLength) : NULL;
74 m_type = src->m_type;
9ceab287 75 m_name = src->m_name;
5039dede
AK
76}
77
f3d71512
VK
78/**
79 * SNMP_Variable destructor
80 */
5039dede
AK
81SNMP_Variable::~SNMP_Variable()
82{
9ceab287 83 free(m_value);
5039dede
AK
84}
85
f3d71512
VK
86/**
87 * Parse variable record in PDU
88 */
e0471fad 89bool SNMP_Variable::parse(BYTE *data, size_t varLength)
5039dede
AK
90{
91 BYTE *pbCurrPos;
e0471fad
VK
92 UINT32 type;
93 size_t length, dwIdLength;
5039dede 94 SNMP_OID *oid;
e0471fad 95 bool bResult = false;
5039dede
AK
96
97 // Object ID
e0471fad
VK
98 if (!BER_DecodeIdentifier(data, varLength, &type, &length, &pbCurrPos, &dwIdLength))
99 return false;
100 if (type != ASN_OBJECT_ID)
101 return false;
5039dede
AK
102
103 oid = (SNMP_OID *)malloc(sizeof(SNMP_OID));
104 memset(oid, 0, sizeof(SNMP_OID));
e0471fad 105 if (BER_DecodeContent(type, pbCurrPos, length, (BYTE *)oid))
5039dede 106 {
9ceab287 107 m_name.setValue(oid->value, (size_t)oid->length);
e0471fad
VK
108 varLength -= length + dwIdLength;
109 pbCurrPos += length;
5039dede
AK
110 bResult = TRUE;
111 }
e0471fad 112 safe_free(oid->value);
5039dede
AK
113 free(oid);
114
115 if (bResult)
116 {
117 bResult = FALSE;
e0471fad 118 if (BER_DecodeIdentifier(pbCurrPos, varLength, &m_type, &length, &pbCurrPos, &dwIdLength))
5039dede 119 {
e0471fad 120 switch(m_type)
5039dede
AK
121 {
122 case ASN_OBJECT_ID:
123 oid = (SNMP_OID *)malloc(sizeof(SNMP_OID));
124 memset(oid, 0, sizeof(SNMP_OID));
e0471fad 125 if (BER_DecodeContent(m_type, pbCurrPos, length, (BYTE *)oid))
5039dede 126 {
e0471fad
VK
127 m_valueLength = oid->length * sizeof(UINT32);
128 m_value = (BYTE *)oid->value;
129 bResult = true;
5039dede
AK
130 }
131 else
132 {
e0471fad 133 safe_free(oid->value);
5039dede
AK
134 }
135 free(oid);
136 break;
137 case ASN_INTEGER:
138 case ASN_COUNTER32:
139 case ASN_GAUGE32:
140 case ASN_TIMETICKS:
141 case ASN_UINTEGER32:
e0471fad
VK
142 m_valueLength = sizeof(UINT32);
143 m_value = (BYTE *)malloc(8);
144 bResult = BER_DecodeContent(m_type, pbCurrPos, length, m_value);
5039dede
AK
145 break;
146 case ASN_COUNTER64:
e0471fad
VK
147 m_valueLength = sizeof(QWORD);
148 m_value = (BYTE *)malloc(16);
149 bResult = BER_DecodeContent(m_type, pbCurrPos, length, m_value);
5039dede
AK
150 break;
151 default:
e0471fad
VK
152 m_valueLength = length;
153 m_value = (BYTE *)nx_memdup(pbCurrPos, length);
5039dede
AK
154 bResult = TRUE;
155 break;
156 }
157 }
158 }
159
160 return bResult;
161}
162
638c64a3
VK
163/**
164 * Get raw value
165 * Returns actual data length
166 */
9c5ebc32 167size_t SNMP_Variable::getRawValue(BYTE *buffer, size_t bufSize) const
630e15d6 168{
e0471fad
VK
169 size_t len = min(bufSize, (size_t)m_valueLength);
170 memcpy(buffer, m_value, len);
630e15d6
VK
171 return len;
172}
173
638c64a3
VK
174/**
175 * Get value as unsigned integer
176 */
9c5ebc32 177UINT32 SNMP_Variable::getValueAsUInt() const
5039dede 178{
967893bb 179 UINT32 dwValue;
5039dede 180
e0471fad 181 switch(m_type)
5039dede
AK
182 {
183 case ASN_INTEGER:
184 case ASN_COUNTER32:
185 case ASN_GAUGE32:
186 case ASN_TIMETICKS:
187 case ASN_UINTEGER32:
188 case ASN_IP_ADDR:
e0471fad 189 dwValue = *((UINT32 *)m_value);
5039dede
AK
190 break;
191 case ASN_COUNTER64:
e0471fad 192 dwValue = (UINT32)(*((QWORD *)m_value));
5039dede
AK
193 break;
194 default:
195 dwValue = 0;
196 break;
197 }
198
199 return dwValue;
200}
201
638c64a3
VK
202/**
203 * Get value as signed integer
204 */
9c5ebc32 205LONG SNMP_Variable::getValueAsInt() const
5039dede
AK
206{
207 LONG iValue;
208
e0471fad 209 switch(m_type)
5039dede
AK
210 {
211 case ASN_INTEGER:
212 case ASN_COUNTER32:
213 case ASN_GAUGE32:
214 case ASN_TIMETICKS:
215 case ASN_UINTEGER32:
216 case ASN_IP_ADDR:
e0471fad 217 iValue = *((LONG *)m_value);
5039dede
AK
218 break;
219 case ASN_COUNTER64:
e0471fad 220 iValue = (LONG)(*((QWORD *)m_value));
5039dede
AK
221 break;
222 default:
223 iValue = 0;
224 break;
225 }
226
227 return iValue;
228}
229
c1d186c8
VK
230/**
231 * Get value as string
d525c9ed 232 * Note: buffer size is in characters
c1d186c8 233 */
9c5ebc32 234TCHAR *SNMP_Variable::getValueAsString(TCHAR *buffer, size_t bufferSize) const
5039dede 235{
e0471fad 236 size_t length;
5039dede 237
e0471fad 238 if ((buffer == NULL) || (bufferSize == 0))
5039dede
AK
239 return NULL;
240
e0471fad 241 switch(m_type)
5039dede
AK
242 {
243 case ASN_INTEGER:
e0471fad 244 _sntprintf(buffer, bufferSize, _T("%d"), *((LONG *)m_value));
5039dede
AK
245 break;
246 case ASN_COUNTER32:
247 case ASN_GAUGE32:
248 case ASN_TIMETICKS:
249 case ASN_UINTEGER32:
e0471fad 250 _sntprintf(buffer, bufferSize, _T("%u"), *((UINT32 *)m_value));
5039dede
AK
251 break;
252 case ASN_COUNTER64:
e0471fad 253 _sntprintf(buffer, bufferSize, UINT64_FMT, *((QWORD *)m_value));
5039dede
AK
254 break;
255 case ASN_IP_ADDR:
e0471fad
VK
256 if (bufferSize >= 16)
257 IpToStr(ntohl(*((UINT32 *)m_value)), buffer);
5039dede 258 else
e0471fad 259 buffer[0] = 0;
5039dede
AK
260 break;
261 case ASN_OBJECT_ID:
e0471fad 262 SNMPConvertOIDToText(m_valueLength / sizeof(UINT32), (UINT32 *)m_value, buffer, bufferSize);
5039dede
AK
263 break;
264 case ASN_OCTET_STRING:
e0471fad
VK
265 length = min(bufferSize - 1, m_valueLength);
266 if (length > 0)
817dce5b 267 {
5039dede 268#ifdef UNICODE
e0471fad 269 if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)m_value, (int)length, buffer, (int)bufferSize) == 0)
817dce5b
VK
270 {
271 // fallback if conversion fails
767f18f6
VK
272 for(size_t i = 0; i < length; i++)
273 {
274 char c = ((char *)m_value)[i];
275 buffer[i] = ((c > 0) && (c < 128)) ? c : '?';
276 }
817dce5b 277 }
5039dede 278#else
e0471fad 279 memcpy(buffer, m_value, length);
5039dede 280#endif
817dce5b 281 }
e0471fad 282 buffer[length] = 0;
5039dede
AK
283 break;
284 default:
e0471fad 285 buffer[0] = 0;
5039dede
AK
286 break;
287 }
e0471fad 288 return buffer;
5039dede
AK
289}
290
c1d186c8
VK
291/**
292 * Get value as printable string, doing bin to hex conversion if necessary
d525c9ed 293 * Note: buffer size is in characters
c1d186c8 294 */
9c5ebc32 295TCHAR *SNMP_Variable::getValueAsPrintableString(TCHAR *buffer, size_t bufferSize, bool *convertToHex) const
49602267 296{
e0471fad 297 size_t length;
49602267
VK
298 bool convertToHexAllowed = *convertToHex;
299 *convertToHex = false;
300
301 if ((buffer == NULL) || (bufferSize == 0))
302 return NULL;
303
e0471fad 304 if (m_type == ASN_OCTET_STRING)
49602267 305 {
e0471fad
VK
306 length = min(bufferSize - 1, m_valueLength);
307 if (length > 0)
817dce5b 308 {
767f18f6
VK
309 bool conversionNeeded = false;
310 if (convertToHexAllowed)
817dce5b 311 {
767f18f6
VK
312 for(UINT32 i = 0; i < length; i++)
313 if ((m_value[i] < 0x1F) && (m_value[i] != 0x0D) && (m_value[i] != 0x0A))
314 {
315 if ((i == length - 1) && (m_value[i] == 0))
316 break; // 0 at the end is OK
317 conversionNeeded = true;
318 break;
319 }
817dce5b 320 }
767f18f6
VK
321
322 if (!conversionNeeded)
323 {
324#ifdef UNICODE
325 if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)m_value, (int)length, buffer, (int)bufferSize) < length)
326 {
327 if (convertToHexAllowed)
328 {
329 conversionNeeded = true;
330 }
331 else
332 {
333 // fallback if conversion fails
334 for(size_t i = 0; i < length; i++)
335 {
336 char c = ((char *)m_value)[i];
337 buffer[i] = ((c > 0) && (c < 128)) ? c : '?';
338 }
339 }
340 }
49602267 341#else
767f18f6 342 memcpy(buffer, m_value, length);
49602267 343#endif
767f18f6
VK
344 buffer[length] = 0;
345 }
817dce5b 346
767f18f6
VK
347 if (conversionNeeded)
348 {
349 TCHAR *hexString = (TCHAR *)malloc((length * 3 + 1) * sizeof(TCHAR));
350 UINT32 i, j;
351 for(i = 0, j = 0; i < length; i++)
352 {
353 hexString[j++] = bin2hex(m_value[i] >> 4);
354 hexString[j++] = bin2hex(m_value[i] & 15);
355 hexString[j++] = _T(' ');
356 }
357 hexString[j] = 0;
358 nx_strncpy(buffer, hexString, bufferSize);
359 free(hexString);
360 *convertToHex = true;
361 }
362 else
363 {
364 // Replace non-printable characters with question marks
365 for(UINT32 i = 0; i < length; i++)
366 if ((buffer[i] < 0x1F) && (buffer[i] != 0x0D) && (buffer[i] != 0x0A))
367 buffer[i] = _T('?');
368 }
369 }
370 else
371 {
372 buffer[0] = 0;
373 }
49602267
VK
374 }
375 else
376 {
817dce5b 377 return getValueAsString(buffer, bufferSize);
49602267
VK
378 }
379
380 return buffer;
381}
382
c1d186c8 383/**
9c5ebc32 384 * Get value as object id. Returned object must be destroyed by caller
c1d186c8 385 */
9ceab287 386SNMP_ObjectId SNMP_Variable::getValueAsObjectId() const
5039dede 387{
9ceab287
VK
388 if (m_type != ASN_OBJECT_ID)
389 return SNMP_ObjectId();
390 return SNMP_ObjectId((UINT32 *)m_value, m_valueLength / sizeof(UINT32));
5039dede
AK
391}
392
c1d186c8
VK
393/**
394 * Get value as MAC address
395 */
9c5ebc32 396TCHAR *SNMP_Variable::getValueAsMACAddr(TCHAR *buffer) const
5039dede
AK
397{
398 int i;
399 TCHAR *pszPos;
400
401 // MAC address usually encoded as octet string
e0471fad 402 if ((m_type == ASN_OCTET_STRING) && (m_valueLength >= 6))
5039dede 403 {
e0471fad
VK
404 for(i = 0, pszPos = buffer; i < 6; i++, pszPos += 3)
405 _sntprintf(pszPos, 4, _T("%02X:"), m_value[i]);
5039dede
AK
406 *(pszPos - 1) = 0;
407 }
408 else
409 {
e0471fad 410 _tcscpy(buffer, _T("00:00:00:00:00:00"));
5039dede 411 }
e0471fad 412 return buffer;
5039dede
AK
413}
414
e0471fad
VK
415/**
416 * Get value as IP address
417 */
9c5ebc32 418TCHAR *SNMP_Variable::getValueAsIPAddr(TCHAR *buffer) const
5039dede
AK
419{
420 // Ignore type and check only length
e0471fad 421 if (m_valueLength >= 4)
5039dede 422 {
e0471fad 423 IpToStr(ntohl(*((UINT32 *)m_value)), buffer);
5039dede
AK
424 }
425 else
426 {
e0471fad 427 _tcscpy(buffer, _T("0.0.0.0"));
5039dede 428 }
e0471fad 429 return buffer;
5039dede
AK
430}
431
e0471fad
VK
432/**
433 * Encode variable using BER
434 * Normally buffer provided should be at least m_valueLength + (name_length * 4) + 12 bytes
435 * Return value is number of bytes actually used in buffer
436 */
437size_t SNMP_Variable::encode(BYTE *pBuffer, size_t bufferSize)
5039dede 438{
e0471fad 439 size_t bytes, dwWorkBufSize;
5039dede
AK
440 BYTE *pWorkBuf;
441
9ceab287 442 dwWorkBufSize = (UINT32)(m_valueLength + m_name.length() * 4 + 16);
5039dede 443 pWorkBuf = (BYTE *)malloc(dwWorkBufSize);
9ceab287
VK
444 bytes = BER_Encode(ASN_OBJECT_ID, (BYTE *)m_name.value(),
445 m_name.length() * sizeof(UINT32),
5039dede 446 pWorkBuf, dwWorkBufSize);
e0471fad
VK
447 bytes += BER_Encode(m_type, m_value, m_valueLength,
448 pWorkBuf + bytes, dwWorkBufSize - bytes);
449 bytes = BER_Encode(ASN_SEQUENCE, pWorkBuf, bytes, pBuffer, bufferSize);
5039dede 450 free(pWorkBuf);
e0471fad 451 return bytes;
5039dede
AK
452}
453
e0471fad
VK
454/**
455 * Set variable from string
456 */
457void SNMP_Variable::setValueFromString(UINT32 type, const TCHAR *value)
5039dede 458{
e0471fad
VK
459 UINT32 *pdwBuffer;
460 size_t length;
5039dede 461
e0471fad
VK
462 m_type = type;
463 switch(m_type)
5039dede
AK
464 {
465 case ASN_INTEGER:
e0471fad
VK
466 m_valueLength = sizeof(LONG);
467 m_value = (BYTE *)realloc(m_value, m_valueLength);
468 *((LONG *)m_value) = _tcstol(value, NULL, 0);
5039dede
AK
469 break;
470 case ASN_COUNTER32:
471 case ASN_GAUGE32:
472 case ASN_TIMETICKS:
473 case ASN_UINTEGER32:
e0471fad
VK
474 m_valueLength = sizeof(UINT32);
475 m_value = (BYTE *)realloc(m_value, m_valueLength);
476 *((UINT32 *)m_value) = _tcstoul(value, NULL, 0);
5039dede
AK
477 break;
478 case ASN_COUNTER64:
e0471fad
VK
479 m_valueLength = sizeof(QWORD);
480 m_value = (BYTE *)realloc(m_value, m_valueLength);
481 *((QWORD *)m_value) = _tcstoull(value, NULL, 0);
5039dede
AK
482 break;
483 case ASN_IP_ADDR:
e0471fad
VK
484 m_valueLength = sizeof(UINT32);
485 m_value = (BYTE *)realloc(m_value, m_valueLength);
486 *((UINT32 *)m_value) = _t_inet_addr(value);
5039dede
AK
487 break;
488 case ASN_OBJECT_ID:
967893bb 489 pdwBuffer = (UINT32 *)malloc(sizeof(UINT32) * 256);
e0471fad
VK
490 length = SNMPParseOID(value, pdwBuffer, 256);
491 if (length > 0)
5039dede 492 {
e0471fad
VK
493 m_valueLength = length * sizeof(UINT32);
494 safe_free(m_value);
495 m_value = (BYTE *)nx_memdup(pdwBuffer, m_valueLength);
5039dede
AK
496 }
497 else
498 {
499 // OID parse error, set to .ccitt.zeroDotZero (.0.0)
e0471fad
VK
500 m_valueLength = sizeof(UINT32) * 2;
501 m_value = (BYTE *)realloc(m_value, m_valueLength);
502 memset(m_value, 0, m_valueLength);
5039dede
AK
503 }
504 break;
505 case ASN_OCTET_STRING:
e0471fad 506 m_valueLength = (UINT32)_tcslen(value);
5039dede 507#ifdef UNICODE
e0471fad 508 m_value = (BYTE *)realloc(m_value, m_valueLength);
5039dede 509 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
e0471fad
VK
510 value, (int)m_valueLength, (char *)m_value,
511 (int)m_valueLength, NULL, NULL);
5039dede 512#else
e0471fad
VK
513 safe_free(m_value);
514 m_value = (BYTE *)nx_memdup(value, m_valueLength);
5039dede
AK
515#endif
516 break;
517 default:
518 break;
519 }
520}