license changed to LGPL for libnxcl, libnxsnmp, libnxlp, libnxsl, and libnxmap
[public/netxms.git] / src / snmp / libnxsnmp / ber.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** SNMP support library
4 ** Copyright (C) 2003-2010 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: ber.cpp
21 **
22 **/
23
24 #include "libnxsnmp.h"
25
26
27 //
28 // Decode BER-encoded variable
29 //
30
31 BOOL BER_DecodeIdentifier(BYTE *pRawData, DWORD dwRawSize, DWORD *pdwType,
32 DWORD *pdwLength, BYTE **pData, DWORD *pdwIdLength)
33 {
34 BOOL bResult = FALSE;
35 BYTE *pbCurrPos = pRawData;
36 DWORD dwIdLength = 0;
37
38 *pdwType = (DWORD)(*pbCurrPos);
39 pbCurrPos++;
40 dwIdLength++;
41
42 // Get length
43 if ((*pbCurrPos & 0x80) == 0)
44 {
45 *pdwLength = (DWORD)(*pbCurrPos);
46 pbCurrPos++;
47 dwIdLength++;
48 bResult = TRUE;
49 }
50 else
51 {
52 DWORD dwLength = 0;
53 BYTE *pbTemp;
54 int iNumBytes;
55
56 iNumBytes = *pbCurrPos & 0x7F;
57 pbCurrPos++;
58 dwIdLength++;
59 pbTemp = ((BYTE *)&dwLength) + (4 - iNumBytes);
60 if ((iNumBytes >= 1) && (iNumBytes <= 4))
61 {
62 while(iNumBytes > 0)
63 {
64 *pbTemp++ = *pbCurrPos++;
65 dwIdLength++;
66 iNumBytes--;
67 }
68 *pdwLength = ntohl(dwLength);
69 bResult = TRUE;
70 }
71 }
72
73 // Set pointer to variable's data
74 *pData = pbCurrPos;
75 *pdwIdLength = dwIdLength;
76 return bResult;
77 }
78
79
80 //
81 // Decode content of specified types
82 //
83
84 BOOL BER_DecodeContent(DWORD dwType, BYTE *pData, DWORD dwLength, BYTE *pBuffer)
85 {
86 BOOL bResult = TRUE;
87
88 switch(dwType)
89 {
90 case ASN_INTEGER:
91 case ASN_COUNTER32:
92 case ASN_GAUGE32:
93 case ASN_TIMETICKS:
94 case ASN_UINTEGER32:
95 if ((dwLength >= 1) && (dwLength <= 5))
96 {
97 DWORD dwValue;
98 BYTE *pbTemp;
99
100 // Pre-fill buffer with 1's for negative values and 0's for positive
101 dwValue = (*pData & 0x80) ? 0xFFFFFFFF : 0;
102
103 // For large unsigned integers, we can have length of 5, and first byte
104 // is usually 0. In this case, we skip first byte.
105 if (dwLength == 5)
106 {
107 pData++;
108 dwLength--;
109 }
110
111 pbTemp = ((BYTE *)&dwValue) + (4 - dwLength);
112 while(dwLength > 0)
113 {
114 *pbTemp++ = *pData++;
115 dwLength--;
116 }
117 dwValue = ntohl(dwValue);
118 memcpy(pBuffer, &dwValue, sizeof(DWORD));
119 }
120 else
121 {
122 bResult = FALSE; // We didn't expect more than 32 bit integers
123 }
124 break;
125 case ASN_COUNTER64:
126 if ((dwLength >= 1) && (dwLength <= 9))
127 {
128 QWORD qwValue;
129 BYTE *pbTemp;
130
131 // Pre-fill buffer with 1's for negative values and 0's for positive
132 qwValue = (*pData & 0x80) ? _ULL(0xFFFFFFFFFFFFFFFF) : 0;
133
134 // For large unsigned integers, we can have length of 9, and first byte
135 // is usually 0. In this case, we skip first byte.
136 if (dwLength == 9)
137 {
138 pData++;
139 dwLength--;
140 }
141
142 pbTemp = ((BYTE *)&qwValue) + (8 - dwLength);
143 while(dwLength > 0)
144 {
145 *pbTemp++ = *pData++;
146 dwLength--;
147 }
148 qwValue = ntohq(qwValue);
149 memcpy(pBuffer, &qwValue, sizeof(QWORD));
150 }
151 else
152 {
153 bResult = FALSE; // We didn't expect more than 64 bit integers
154 }
155 break;
156 case ASN_OBJECT_ID:
157 if (dwLength > 0)
158 {
159 SNMP_OID *oid;
160 DWORD dwValue;
161
162 oid = (SNMP_OID *)pBuffer;
163 oid->pdwValue = (DWORD *)malloc(sizeof(DWORD) * (dwLength + 1));
164
165 // First octet need special handling
166 oid->pdwValue[0] = *pData / 40;
167 oid->pdwValue[1] = *pData % 40;
168 pData++;
169 oid->dwLength = 2;
170 dwLength--;
171
172 // Parse remaining octets
173 while(dwLength > 0)
174 {
175 dwValue = 0;
176
177 // Loop through octets with 8th bit set to 1
178 while((*pData & 0x80) && (dwLength > 0))
179 {
180 dwValue = (dwValue << 7) | (*pData & 0x7F);
181 pData++;
182 dwLength--;
183 }
184
185 // Last octet in element
186 if (dwLength > 0)
187 {
188 oid->pdwValue[oid->dwLength++] = (dwValue << 7) | *pData;
189 pData++;
190 dwLength--;
191 }
192 }
193 }
194 break;
195 default: // For unknown types, simply move content to buffer
196 memcpy(pBuffer, pData, dwLength);
197 break;
198 }
199 return bResult;
200 }
201
202
203 //
204 // Encode content
205 //
206
207 static LONG EncodeContent(DWORD dwType, BYTE *pData, DWORD dwDataLength, BYTE *pResult)
208 {
209 LONG nBytes = 0;
210 DWORD dwTemp;
211 QWORD qwTemp;
212 BYTE *pTemp, sign;
213 int i, iOidLength;
214
215 switch(dwType)
216 {
217 case ASN_NULL:
218 break;
219 case ASN_INTEGER:
220 dwTemp = htonl(*((DWORD *)pData));
221 pTemp = (BYTE *)&dwTemp;
222 sign = (*pTemp & 0x80) ? 0xFF : 0;
223 for(nBytes = 4; (*pTemp == sign) && (nBytes > 1); pTemp++, nBytes--);
224 if ((*pTemp & 0x80) != (sign & 0x80))
225 {
226 memcpy(&pResult[1], pTemp, nBytes);
227 pResult[0] = sign;
228 nBytes++;
229 }
230 else
231 {
232 memcpy(pResult, pTemp, nBytes);
233 }
234 break;
235 case ASN_COUNTER32:
236 case ASN_GAUGE32:
237 case ASN_TIMETICKS:
238 case ASN_UINTEGER32:
239 dwTemp = htonl(*((DWORD *)pData));
240 pTemp = (BYTE *)&dwTemp;
241 for(nBytes = 4; (*pTemp == 0) && (nBytes > 1); pTemp++, nBytes--);
242 if (*pTemp & 0x80)
243 {
244 memcpy(&pResult[1], pTemp, nBytes);
245 pResult[0] = 0;
246 nBytes++;
247 }
248 else
249 {
250 memcpy(pResult, pTemp, nBytes);
251 }
252 break;
253 case ASN_COUNTER64:
254 qwTemp = htonq(*((QWORD *)pData));
255 pTemp = (BYTE *)&qwTemp;
256 for(nBytes = 8; (*pTemp == 0) && (nBytes > 1); pTemp++, nBytes--);
257 if (*pTemp & 0x80)
258 {
259 memcpy(&pResult[1], pTemp, nBytes);
260 pResult[0] = 0;
261 nBytes++;
262 }
263 else
264 {
265 memcpy(pResult, pTemp, nBytes);
266 }
267 break;
268 case ASN_OBJECT_ID:
269 iOidLength = dwDataLength / sizeof(DWORD);
270 if (iOidLength > 1)
271 {
272 BYTE *pbCurrPos = pResult;
273 DWORD j, dwValue, dwSize, *pdwCurrId = (DWORD *)pData;
274 static DWORD dwLengthMask[5] = { 0x0000007F, 0x00003FFF, 0x001FFFFF, 0x0FFFFFFF, 0xFFFFFFFF };
275
276 // First two ids encoded in one byte
277 *pbCurrPos = (BYTE)pdwCurrId[0] * 40 + (BYTE)pdwCurrId[1];
278 pbCurrPos++;
279 pdwCurrId += 2;
280 nBytes++;
281
282 // Encode other ids
283 for(i = 2; i < iOidLength; i++, pdwCurrId++)
284 {
285 dwValue = *pdwCurrId;
286
287 // Determine size of oid
288 for(dwSize = 0; (dwLengthMask[dwSize] & dwValue) != dwValue; dwSize++);
289 dwSize++; // Size is at least one byte...
290
291 if (dwSize > 1)
292 {
293 // Encode by 7 bits
294 pbCurrPos += (dwSize - 1);
295 *pbCurrPos-- = (BYTE)(dwValue & 0x7F);
296 for(j = dwSize - 1; j > 0; j--)
297 {
298 dwValue >>= 7;
299 *pbCurrPos-- = (BYTE)(dwValue & 0x7F) | 0x80;
300 }
301 pbCurrPos += (dwSize + 1);
302 }
303 else
304 {
305 *pbCurrPos++ = (BYTE)(dwValue & 0x7F);
306 }
307 nBytes += dwSize;
308 }
309 }
310 else if (iOidLength == 1)
311 {
312 *pResult = (BYTE)(*((DWORD *)pData)) * 40;
313 nBytes++;
314 }
315 break;
316 default:
317 memcpy(pResult, pData, dwDataLength);
318 nBytes = dwDataLength;
319 break;
320 }
321 return nBytes;
322 }
323
324
325 //
326 // Encode identifier and content
327 // Return value is size of encoded identifier and content in buffer
328 // or 0 if there are not enough place in buffer or type is unknown
329 //
330
331 DWORD BER_Encode(DWORD dwType, BYTE *pData, DWORD dwDataLength,
332 BYTE *pBuffer, DWORD dwBufferSize)
333 {
334 DWORD dwBytes = 0;
335 BYTE *pbCurrPos = pBuffer, *pEncodedData;
336 LONG nDataBytes;
337
338 if (dwBufferSize < 2)
339 return 0;
340
341 *pbCurrPos++ = (BYTE)dwType;
342 dwBytes++;
343
344 // Encode content
345 pEncodedData = (BYTE *)malloc(dwDataLength);
346 nDataBytes = EncodeContent(dwType, pData, dwDataLength, pEncodedData);
347
348 // Encode length
349 if (nDataBytes < 128)
350 {
351 *pbCurrPos++ = (BYTE)nDataBytes;
352 dwBytes++;
353 }
354 else
355 {
356 BYTE bLength[8];
357 LONG nHdrBytes;
358 int i;
359
360 *((DWORD *)bLength) = htonl((DWORD)nDataBytes);
361 for(i = 0, nHdrBytes = 4; (bLength[i] == 0) && (nHdrBytes > 1); i++, nHdrBytes--);
362 if (i > 0)
363 memmove(bLength, &bLength[i], nHdrBytes);
364
365 // Check for available buffer size
366 if (dwBufferSize < (DWORD)nHdrBytes + dwBytes + 1)
367 {
368 free(pEncodedData);
369 return 0;
370 }
371
372 // Write length field
373 *pbCurrPos++ = (BYTE)(0x80 | nHdrBytes);
374 memcpy(pbCurrPos, bLength, nHdrBytes);
375 pbCurrPos += nHdrBytes;
376 dwBytes += nHdrBytes + 1;
377 }
378
379 // Copy encoded data to buffer
380 if (dwBufferSize >= dwBytes + nDataBytes)
381 {
382 memcpy(pbCurrPos, pEncodedData, nDataBytes);
383 dwBytes += nDataBytes;
384 }
385 else
386 {
387 dwBytes = 0; // Buffer is too small
388 }
389
390 free(pEncodedData);
391 return dwBytes;
392 }