fixed incorrect fallback character conversion
[public/netxms.git] / src / snmp / libnxsnmp / variable.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** SNMP support library
4 ** Copyright (C) 2003-2016 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
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
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 **
16 ** You should have received a copy of the GNU Lesser General Public License
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
26 /**
27 * SNMP_Variable default constructor
28 */
29 SNMP_Variable::SNMP_Variable()
30 {
31 m_value = NULL;
32 m_type = ASN_NULL;
33 m_valueLength = 0;
34 }
35
36 /**
37 * Create variable of ASN_NULL type
38 */
39 SNMP_Variable::SNMP_Variable(const TCHAR *name)
40 {
41 m_name = SNMP_ObjectId::parse(name);
42 m_value = NULL;
43 m_type = ASN_NULL;
44 m_valueLength = 0;
45 }
46
47 /**
48 * Create variable of ASN_NULL type
49 */
50 SNMP_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;
55 }
56
57 /**
58 * Create variable of ASN_NULL type
59 */
60 SNMP_Variable::SNMP_Variable(const SNMP_ObjectId &name) : m_name(name)
61 {
62 m_value = NULL;
63 m_type = ASN_NULL;
64 m_valueLength = 0;
65 }
66
67 /**
68 * Copy constructor
69 */
70 SNMP_Variable::SNMP_Variable(const SNMP_Variable *src)
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;
75 m_name = src->m_name;
76 }
77
78 /**
79 * SNMP_Variable destructor
80 */
81 SNMP_Variable::~SNMP_Variable()
82 {
83 free(m_value);
84 }
85
86 /**
87 * Parse variable record in PDU
88 */
89 bool SNMP_Variable::parse(BYTE *data, size_t varLength)
90 {
91 BYTE *pbCurrPos;
92 UINT32 type;
93 size_t length, dwIdLength;
94 SNMP_OID *oid;
95 bool bResult = false;
96
97 // Object ID
98 if (!BER_DecodeIdentifier(data, varLength, &type, &length, &pbCurrPos, &dwIdLength))
99 return false;
100 if (type != ASN_OBJECT_ID)
101 return false;
102
103 oid = (SNMP_OID *)malloc(sizeof(SNMP_OID));
104 memset(oid, 0, sizeof(SNMP_OID));
105 if (BER_DecodeContent(type, pbCurrPos, length, (BYTE *)oid))
106 {
107 m_name.setValue(oid->value, (size_t)oid->length);
108 varLength -= length + dwIdLength;
109 pbCurrPos += length;
110 bResult = TRUE;
111 }
112 safe_free(oid->value);
113 free(oid);
114
115 if (bResult)
116 {
117 bResult = FALSE;
118 if (BER_DecodeIdentifier(pbCurrPos, varLength, &m_type, &length, &pbCurrPos, &dwIdLength))
119 {
120 switch(m_type)
121 {
122 case ASN_OBJECT_ID:
123 oid = (SNMP_OID *)malloc(sizeof(SNMP_OID));
124 memset(oid, 0, sizeof(SNMP_OID));
125 if (BER_DecodeContent(m_type, pbCurrPos, length, (BYTE *)oid))
126 {
127 m_valueLength = oid->length * sizeof(UINT32);
128 m_value = (BYTE *)oid->value;
129 bResult = true;
130 }
131 else
132 {
133 safe_free(oid->value);
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:
142 m_valueLength = sizeof(UINT32);
143 m_value = (BYTE *)malloc(8);
144 bResult = BER_DecodeContent(m_type, pbCurrPos, length, m_value);
145 break;
146 case ASN_COUNTER64:
147 m_valueLength = sizeof(QWORD);
148 m_value = (BYTE *)malloc(16);
149 bResult = BER_DecodeContent(m_type, pbCurrPos, length, m_value);
150 break;
151 default:
152 m_valueLength = length;
153 m_value = (BYTE *)nx_memdup(pbCurrPos, length);
154 bResult = TRUE;
155 break;
156 }
157 }
158 }
159
160 return bResult;
161 }
162
163 /**
164 * Get raw value
165 * Returns actual data length
166 */
167 size_t SNMP_Variable::getRawValue(BYTE *buffer, size_t bufSize) const
168 {
169 size_t len = min(bufSize, (size_t)m_valueLength);
170 memcpy(buffer, m_value, len);
171 return len;
172 }
173
174 /**
175 * Get value as unsigned integer
176 */
177 UINT32 SNMP_Variable::getValueAsUInt() const
178 {
179 UINT32 dwValue;
180
181 switch(m_type)
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:
189 dwValue = *((UINT32 *)m_value);
190 break;
191 case ASN_COUNTER64:
192 dwValue = (UINT32)(*((QWORD *)m_value));
193 break;
194 default:
195 dwValue = 0;
196 break;
197 }
198
199 return dwValue;
200 }
201
202 /**
203 * Get value as signed integer
204 */
205 LONG SNMP_Variable::getValueAsInt() const
206 {
207 LONG iValue;
208
209 switch(m_type)
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:
217 iValue = *((LONG *)m_value);
218 break;
219 case ASN_COUNTER64:
220 iValue = (LONG)(*((QWORD *)m_value));
221 break;
222 default:
223 iValue = 0;
224 break;
225 }
226
227 return iValue;
228 }
229
230 /**
231 * Get value as string
232 * Note: buffer size is in characters
233 */
234 TCHAR *SNMP_Variable::getValueAsString(TCHAR *buffer, size_t bufferSize) const
235 {
236 size_t length;
237
238 if ((buffer == NULL) || (bufferSize == 0))
239 return NULL;
240
241 switch(m_type)
242 {
243 case ASN_INTEGER:
244 _sntprintf(buffer, bufferSize, _T("%d"), *((LONG *)m_value));
245 break;
246 case ASN_COUNTER32:
247 case ASN_GAUGE32:
248 case ASN_TIMETICKS:
249 case ASN_UINTEGER32:
250 _sntprintf(buffer, bufferSize, _T("%u"), *((UINT32 *)m_value));
251 break;
252 case ASN_COUNTER64:
253 _sntprintf(buffer, bufferSize, UINT64_FMT, *((QWORD *)m_value));
254 break;
255 case ASN_IP_ADDR:
256 if (bufferSize >= 16)
257 IpToStr(ntohl(*((UINT32 *)m_value)), buffer);
258 else
259 buffer[0] = 0;
260 break;
261 case ASN_OBJECT_ID:
262 SNMPConvertOIDToText(m_valueLength / sizeof(UINT32), (UINT32 *)m_value, buffer, bufferSize);
263 break;
264 case ASN_OCTET_STRING:
265 length = min(bufferSize - 1, m_valueLength);
266 if (length > 0)
267 {
268 #ifdef UNICODE
269 if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (char *)m_value, (int)length, buffer, (int)bufferSize) == 0)
270 {
271 // fallback if conversion fails
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 }
277 }
278 #else
279 memcpy(buffer, m_value, length);
280 #endif
281 }
282 buffer[length] = 0;
283 break;
284 default:
285 buffer[0] = 0;
286 break;
287 }
288 return buffer;
289 }
290
291 /**
292 * Get value as printable string, doing bin to hex conversion if necessary
293 * Note: buffer size is in characters
294 */
295 TCHAR *SNMP_Variable::getValueAsPrintableString(TCHAR *buffer, size_t bufferSize, bool *convertToHex) const
296 {
297 size_t length;
298 bool convertToHexAllowed = *convertToHex;
299 *convertToHex = false;
300
301 if ((buffer == NULL) || (bufferSize == 0))
302 return NULL;
303
304 if (m_type == ASN_OCTET_STRING)
305 {
306 length = min(bufferSize - 1, m_valueLength);
307 if (length > 0)
308 {
309 bool conversionNeeded = false;
310 if (convertToHexAllowed)
311 {
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 }
320 }
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 }
341 #else
342 memcpy(buffer, m_value, length);
343 #endif
344 buffer[length] = 0;
345 }
346
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 }
374 }
375 else
376 {
377 return getValueAsString(buffer, bufferSize);
378 }
379
380 return buffer;
381 }
382
383 /**
384 * Get value as object id. Returned object must be destroyed by caller
385 */
386 SNMP_ObjectId SNMP_Variable::getValueAsObjectId() const
387 {
388 if (m_type != ASN_OBJECT_ID)
389 return SNMP_ObjectId();
390 return SNMP_ObjectId((UINT32 *)m_value, m_valueLength / sizeof(UINT32));
391 }
392
393 /**
394 * Get value as MAC address
395 */
396 TCHAR *SNMP_Variable::getValueAsMACAddr(TCHAR *buffer) const
397 {
398 int i;
399 TCHAR *pszPos;
400
401 // MAC address usually encoded as octet string
402 if ((m_type == ASN_OCTET_STRING) && (m_valueLength >= 6))
403 {
404 for(i = 0, pszPos = buffer; i < 6; i++, pszPos += 3)
405 _sntprintf(pszPos, 4, _T("%02X:"), m_value[i]);
406 *(pszPos - 1) = 0;
407 }
408 else
409 {
410 _tcscpy(buffer, _T("00:00:00:00:00:00"));
411 }
412 return buffer;
413 }
414
415 /**
416 * Get value as IP address
417 */
418 TCHAR *SNMP_Variable::getValueAsIPAddr(TCHAR *buffer) const
419 {
420 // Ignore type and check only length
421 if (m_valueLength >= 4)
422 {
423 IpToStr(ntohl(*((UINT32 *)m_value)), buffer);
424 }
425 else
426 {
427 _tcscpy(buffer, _T("0.0.0.0"));
428 }
429 return buffer;
430 }
431
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 */
437 size_t SNMP_Variable::encode(BYTE *pBuffer, size_t bufferSize)
438 {
439 size_t bytes, dwWorkBufSize;
440 BYTE *pWorkBuf;
441
442 dwWorkBufSize = (UINT32)(m_valueLength + m_name.length() * 4 + 16);
443 pWorkBuf = (BYTE *)malloc(dwWorkBufSize);
444 bytes = BER_Encode(ASN_OBJECT_ID, (BYTE *)m_name.value(),
445 m_name.length() * sizeof(UINT32),
446 pWorkBuf, dwWorkBufSize);
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);
450 free(pWorkBuf);
451 return bytes;
452 }
453
454 /**
455 * Set variable from string
456 */
457 void SNMP_Variable::setValueFromString(UINT32 type, const TCHAR *value)
458 {
459 UINT32 *pdwBuffer;
460 size_t length;
461
462 m_type = type;
463 switch(m_type)
464 {
465 case ASN_INTEGER:
466 m_valueLength = sizeof(LONG);
467 m_value = (BYTE *)realloc(m_value, m_valueLength);
468 *((LONG *)m_value) = _tcstol(value, NULL, 0);
469 break;
470 case ASN_COUNTER32:
471 case ASN_GAUGE32:
472 case ASN_TIMETICKS:
473 case ASN_UINTEGER32:
474 m_valueLength = sizeof(UINT32);
475 m_value = (BYTE *)realloc(m_value, m_valueLength);
476 *((UINT32 *)m_value) = _tcstoul(value, NULL, 0);
477 break;
478 case ASN_COUNTER64:
479 m_valueLength = sizeof(QWORD);
480 m_value = (BYTE *)realloc(m_value, m_valueLength);
481 *((QWORD *)m_value) = _tcstoull(value, NULL, 0);
482 break;
483 case ASN_IP_ADDR:
484 m_valueLength = sizeof(UINT32);
485 m_value = (BYTE *)realloc(m_value, m_valueLength);
486 *((UINT32 *)m_value) = _t_inet_addr(value);
487 break;
488 case ASN_OBJECT_ID:
489 pdwBuffer = (UINT32 *)malloc(sizeof(UINT32) * 256);
490 length = SNMPParseOID(value, pdwBuffer, 256);
491 if (length > 0)
492 {
493 m_valueLength = length * sizeof(UINT32);
494 safe_free(m_value);
495 m_value = (BYTE *)nx_memdup(pdwBuffer, m_valueLength);
496 }
497 else
498 {
499 // OID parse error, set to .ccitt.zeroDotZero (.0.0)
500 m_valueLength = sizeof(UINT32) * 2;
501 m_value = (BYTE *)realloc(m_value, m_valueLength);
502 memset(m_value, 0, m_valueLength);
503 }
504 break;
505 case ASN_OCTET_STRING:
506 m_valueLength = (UINT32)_tcslen(value);
507 #ifdef UNICODE
508 m_value = (BYTE *)realloc(m_value, m_valueLength);
509 WideCharToMultiByte(CP_ACP, WC_COMPOSITECHECK | WC_DEFAULTCHAR,
510 value, (int)m_valueLength, (char *)m_value,
511 (int)m_valueLength, NULL, NULL);
512 #else
513 safe_free(m_value);
514 m_value = (BYTE *)nx_memdup(value, m_valueLength);
515 #endif
516 break;
517 default:
518 break;
519 }
520 }