1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.metricshub.snmp.client;
24
25 import uk.co.westhawk.snmp.pdu.BlockPdu;
26 import uk.co.westhawk.snmp.stack.AsnObject;
27 import uk.co.westhawk.snmp.stack.AsnObjectId;
28 import uk.co.westhawk.snmp.stack.AsnOctets;
29 import uk.co.westhawk.snmp.stack.PduException;
30 import uk.co.westhawk.snmp.stack.SnmpConstants;
31 import uk.co.westhawk.snmp.stack.SnmpContext;
32 import uk.co.westhawk.snmp.stack.SnmpContextv2c;
33 import uk.co.westhawk.snmp.stack.SnmpContextv3;
34 import uk.co.westhawk.snmp.stack.SnmpContextv3Face;
35 import uk.co.westhawk.snmp.stack.varbind;
36 import java.io.IOException;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collections;
40 import java.util.HashSet;
41 import java.util.List;
42 import java.util.Set;
43
44 public class SnmpClient {
45
46
47 public static final int SNMP_PORT = 161;
48
49
50 public static final int SNMP_V1 = 1;
51 public static final int SNMP_V2C = 2;
52 public static final int SNMP_V3 = 3;
53 public static final String SNMP_AUTH_MD5 = "MD5";
54 public static final String SNMP_AUTH_SHA = "SHA";
55 public static final String SNMP_AUTH_SHA256 = "SHA256";
56 public static final String SNMP_AUTH_SHA512 = "SHA512";
57 public static final String SNMP_AUTH_SHA224 = "SHA224";
58 public static final String SNMP_AUTH_SHA384 = "SHA384";
59 public static final String SNMP_PRIVACY_DES = "DES";
60 public static final String SNMP_PRIVACY_AES = "AES";
61 public static final String SNMP_PRIVACY_AES192 = "AES192";
62 public static final String SNMP_PRIVACY_AES256 = "AES256";
63 public static final String SNMP_NONE = "None";
64
65 public static final Set<String> SNMP_PRIVACY_PROTOCOLS = Collections.unmodifiableSet( new HashSet<>(
66 Arrays.asList(SNMP_PRIVACY_DES, SNMP_PRIVACY_AES, SNMP_PRIVACY_AES192, SNMP_PRIVACY_AES256)));
67
68 public static final Set<String> SNMP_AUTH_PROTOCOLS = Collections.unmodifiableSet( new HashSet<>(
69 Arrays.asList(SNMP_AUTH_MD5, SNMP_AUTH_SHA, SNMP_AUTH_SHA256, SNMP_AUTH_SHA512, SNMP_AUTH_SHA224, SNMP_AUTH_SHA384)));
70
71 private SnmpContext contextv1 = null;
72 private SnmpContextv2c contextv2c = null;
73 private SnmpContextv3 contextv3 = null;
74 private BlockPdu pdu;
75 private String host;
76 private int port;
77 private String community;
78 private int snmpVersion;
79 private String authUsername;
80 private String authType;
81 private String authPassword;
82 private String privacyType;
83 private String privacyPassword;
84 private int[] retryIntervals;
85 private String contextName;
86 private byte[] contextEngineID;
87 public static final String SOCKET_TYPE = "Standard";
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117 public SnmpClient(String host, int port, int version, int[] retryIntervals,
118 String community,
119 String authType, String authUsername, String authPassword,
120 String privacyType, String privacyPassword,
121 String contextName, byte[] contextID) throws IOException {
122
123 validate(version, authType, privacyType);
124
125
126 this.host = host;
127 this.port = port;
128 this.snmpVersion = version;
129 this.retryIntervals = retryIntervals;
130 this.community = community;
131 this.authType = authType;
132 this.authUsername = authUsername;
133 this.authPassword = authPassword;
134 this.privacyType = privacyType;
135 this.privacyPassword = privacyPassword;
136 this.contextName = contextName;
137 this.contextEngineID = contextID;
138
139
140 initialize();
141 }
142
143
144
145
146
147
148
149
150
151 private void validate(int version, String authType, String privacyType) {
152
153
154 if (version == SNMP_V3) {
155 if (authType != null) {
156 if (!authType.isEmpty()) {
157 if (! SNMP_AUTH_PROTOCOLS.contains(authType)) {
158 throw new IllegalArgumentException("Invalid authentication method '" + authType + "'." +
159 " (Valid options are: '" + SNMP_AUTH_MD5
160 + "', '" + SNMP_AUTH_SHA
161 + "', '" + SNMP_AUTH_SHA256
162 + "', '" + SNMP_AUTH_SHA512
163 + "', '" + SNMP_AUTH_SHA224
164 + "', '" + SNMP_AUTH_SHA384
165 + "', or empty)");
166 }
167 }
168 }
169
170 if (privacyType != null) {
171 if (!privacyType.isEmpty()) {
172 if (! SNMP_PRIVACY_PROTOCOLS.contains(privacyType)) {
173 throw new IllegalArgumentException(
174 "Invalid privacy method '" + privacyType + "'." + " (Valid options are:'" + SNMP_PRIVACY_DES
175 + "', '" + SNMP_PRIVACY_AES + "', '" + SNMP_PRIVACY_AES192 + "', '" + SNMP_PRIVACY_AES256 + "', or empty)");
176 }
177 }
178 }
179 }
180 }
181
182
183
184
185
186
187
188
189
190
191
192 private void initialize() throws IOException {
193
194
195 if (snmpVersion == SNMP_V2C) {
196 contextv2c = new SnmpContextv2c(host, port, null, SOCKET_TYPE);
197 contextv2c.setCommunity(community);
198 }
199
200
201 else if (snmpVersion == SNMP_V3) {
202 int authProtocolCode = 0;
203 int privacyProtocolCode = 0;
204 boolean authenticate = false;
205 boolean privacy = false;
206
207
208 if (contextEngineID == null) {
209 contextEngineID = new byte[0];
210 }
211 if (contextName == null) {
212 contextName = "";
213 }
214
215
216 if (authUsername == null) {
217 authUsername = "";
218 }
219
220
221 if (authType == null || authUsername == null || authPassword == null) {
222 authenticate = false;
223 authProtocolCode = SnmpContextv3Face.NO_AUTH_PROTOCOL;
224 authPassword = "";
225 } else if (authType.isEmpty() || authUsername.isEmpty() || authPassword.isEmpty()) {
226 authenticate = false;
227 authProtocolCode = SnmpContextv3Face.NO_AUTH_PROTOCOL;
228 authPassword = "";
229 } else if (authType.equals(SNMP_AUTH_MD5)) {
230 authenticate = true;
231 authProtocolCode = SnmpContextv3Face.MD5_PROTOCOL;
232 } else if (authType.equals(SNMP_AUTH_SHA)) {
233 authenticate = true;
234 authProtocolCode = SnmpContextv3Face.SHA1_PROTOCOL;
235 } else if (authType.equals(SNMP_AUTH_SHA256)) {
236 authenticate = true;
237 authProtocolCode = SnmpContextv3Face.SHA256_PROTOCOL;
238 } else if (authType.equals(SNMP_AUTH_SHA512)) {
239 authenticate = true;
240 authProtocolCode = SnmpContextv3Face.SHA512_PROTOCOL;
241 } else if (authType.equals(SNMP_AUTH_SHA224)) {
242 authenticate = true;
243 authProtocolCode = SnmpContextv3Face.SHA224_PROTOCOL;
244
245 } else if (authType.equals(SNMP_AUTH_SHA384)) {
246 authenticate = true;
247 authProtocolCode = SnmpContextv3Face.SHA384_PROTOCOL;
248 }
249
250
251 if (privacyType == null || privacyPassword == null) {
252 privacy = false;
253 } else if (privacyType.isEmpty() || privacyPassword.isEmpty()) {
254 privacy = false;
255 } else if (privacyType.equals(SNMP_PRIVACY_DES)) {
256 privacy = true;
257 privacyProtocolCode = SnmpContextv3Face.DES_ENCRYPT;
258 } else if (privacyType.equals(SNMP_PRIVACY_AES)) {
259 privacy = true;
260 privacyProtocolCode = SnmpContextv3Face.AES_ENCRYPT;
261 } else if (privacyType.equals(SNMP_PRIVACY_AES192)) {
262 privacy = true;
263 privacyProtocolCode = SnmpContextv3Face.AES192_ENCRYPT;
264 } else if (privacyType.equals(SNMP_PRIVACY_AES256)) {
265 privacy = true;
266 privacyProtocolCode = SnmpContextv3Face.AES256_ENCRYPT;
267 }
268
269 if (privacy && !authenticate) {
270 throw new IllegalStateException("Authentication is required for privacy to be enforced");
271 }
272
273
274 contextv3 = new SnmpContextv3(host, port, SOCKET_TYPE);
275 contextv3.setContextEngineId(contextEngineID);
276 contextv3.setContextName(contextName);
277 contextv3.setUserName(authUsername);
278 contextv3.setUseAuthentication(authenticate);
279 if (authenticate) {
280 contextv3.setUserAuthenticationPassword(authPassword);
281 contextv3.setAuthenticationProtocol(authProtocolCode);
282 contextv3.setUsePrivacy(privacy);
283 if (privacy) {
284 contextv3.setPrivacyProtocol(privacyProtocolCode);
285 contextv3.setUserPrivacyPassword(privacyPassword);
286 }
287 }
288 }
289
290
291 else {
292 contextv1 = new SnmpContext(host, port, SOCKET_TYPE);
293 contextv1.setCommunity(community);
294 }
295
296
297
298 AsnOctets.setHexPrefix("");
299
300
301
302 }
303
304
305
306
307
308 public void freeResources() {
309 if (contextv1 != null) {
310 contextv1.destroy();
311 contextv1 = null;
312 }
313 if (contextv2c != null) {
314 contextv2c.destroy();
315 contextv2c = null;
316 }
317 if (contextv3 != null) {
318 contextv3.destroy();
319 contextv3 = null;
320 }
321
322 if (pdu != null) {
323 pdu = null;
324 }
325 }
326
327
328
329
330
331
332
333 private void createPdu() {
334
335 if (snmpVersion == SNMP_V2C) {
336 pdu = new BlockPdu(contextv2c);
337 } else if (snmpVersion == SNMP_V3) {
338 pdu = new BlockPdu(contextv3);
339 } else {
340 pdu = new BlockPdu(contextv1);
341 }
342
343
344 if (retryIntervals != null) {
345 pdu.setRetryIntervals(retryIntervals);
346 }
347 }
348
349
350
351
352
353
354
355
356 public String get(String oid) throws Exception {
357 createPdu();
358 pdu.setPduType(BlockPdu.GET);
359 pdu.addOid(oid);
360 return sendRequest().value;
361 }
362
363
364
365
366
367
368
369
370
371
372 public String getWithDetails(String oid) throws Exception {
373 createPdu();
374 pdu.setPduType(BlockPdu.GET);
375 pdu.addOid(oid);
376 SnmpResult result = sendRequest();
377 return result.oid + "\t" + result.type + "\t" + result.value;
378 }
379
380
381
382
383
384
385
386
387
388 public String getNext(String oid) throws Exception {
389 createPdu();
390 pdu.setPduType(BlockPdu.GETNEXT);
391 pdu.addOid(oid);
392 SnmpResult result = sendRequest();
393 return result.oid + "\t" + result.type + "\t" + result.value;
394 }
395
396
397
398
399
400
401
402
403
404
405
406 public String walk(String oid) throws Exception {
407
408 StringBuilder walkResult = new StringBuilder();
409 String currentOID;
410 SnmpResult getNextResult;
411
412
413 if (oid == null) {
414 throw new IllegalArgumentException("Invalid SNMP Walk OID: null");
415 }
416 if (oid.length() < 3) {
417 throw new IllegalArgumentException("Invalid SNMP Walk OID: \"" + oid + "\"");
418 }
419
420
421
422
423
424
425
426
427
428
429
430
431 getNext(oid);
432
433 currentOID = oid;
434 do {
435 createPdu();
436 pdu.setPduType(BlockPdu.GETNEXT);
437 pdu.addOid(currentOID);
438 try {
439 getNextResult = sendRequest();
440 } catch (Exception e) {
441
442 break;
443 }
444
445 currentOID = getNextResult.oid;
446 if (!currentOID.startsWith(oid)) {
447
448 break;
449 }
450
451
452 walkResult.append(currentOID + "\t" + getNextResult.type + "\t" + getNextResult.value + "\n");
453
454 } while (walkResult.length() < 10 * 1048576);
455
456
457
458 int resultLength = walkResult.length();
459 if (resultLength > 0) {
460 return walkResult.substring(0, resultLength - 1);
461 }
462
463
464 return "";
465 }
466
467
468
469
470
471
472
473
474
475
476
477 public List<List<String>> table(String rootOID, String[] selectColumnArray) throws Exception {
478
479
480 if (rootOID == null) {
481 throw new IllegalArgumentException("Invalid SNMP Table OID: null");
482 }
483 if (rootOID.length() < 3) {
484 throw new IllegalArgumentException("Invalid SNMP Table OID: \"" + rootOID + "\"");
485 }
486 if (selectColumnArray == null) {
487 throw new IllegalArgumentException("Invalid SNMP Table column numbers: null");
488 }
489 if (selectColumnArray.length < 1) {
490 throw new IllegalArgumentException("Invalid SNMP Table column numbers: none");
491 }
492
493
494
495
496 createPdu();
497 pdu.setPduType(BlockPdu.GETNEXT);
498 pdu.addOid(rootOID);
499 String firstValueOid = sendRequest().oid;
500 if (firstValueOid.isEmpty() || !firstValueOid.startsWith(rootOID)) {
501
502 return new ArrayList<>();
503 }
504
505 int tempIndex = firstValueOid.indexOf(".", rootOID.length() + 2);
506 if (tempIndex < 0) {
507
508
509 return new ArrayList<>();
510 }
511 String firstColumnOid = firstValueOid.substring(0, tempIndex);
512 int firstColumnOidLength = firstColumnOid.length();
513
514
515
516 ArrayList<String> IDArray = new ArrayList<String>(0);
517 String currentOID = firstColumnOid;
518 SnmpResult getNextResult;
519 do {
520
521 createPdu();
522 pdu.setPduType(BlockPdu.GETNEXT);
523 pdu.addOid(currentOID);
524 getNextResult = sendRequest();
525
526 currentOID = getNextResult.oid;
527
528
529 if (!currentOID.startsWith(firstColumnOid)) {
530 break;
531 }
532
533
534
535 IDArray.add(currentOID.substring(firstColumnOidLength + 1));
536
537 } while (IDArray.size() < 10000);
538
539
540 List<List<String>> tableResult = new ArrayList<>();
541 for (String ID : IDArray) {
542
543 List<String> row = new ArrayList<>();
544 for (String column : selectColumnArray) {
545
546
547
548 if (column.equals("ID")) {
549 row.add(ID);
550 } else {
551
552 try {
553 row.add(get(rootOID + "." + column + "." + ID));
554 } catch (Exception e) {
555 row.add("");
556 }
557 }
558 }
559 tableResult.add(row);
560 }
561
562
563 return tableResult;
564 }
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579 private SnmpResult sendRequest() throws PduException, IOException, Exception {
580
581
582 SnmpResult result = new SnmpResult();
583
584
585 varbind var = pdu.getResponseVariableBinding();
586
587
588 AsnObjectId oid = var.getOid();
589 AsnObject value = var.getValue();
590
591
592
593 byte valueType = value.getRespType();
594 if (valueType == SnmpConstants.SNMP_VAR_NOSUCHOBJECT ||
595 valueType == SnmpConstants.SNMP_VAR_NOSUCHINSTANCE ||
596 valueType == SnmpConstants.SNMP_VAR_ENDOFMIBVIEW) {
597 throw new Exception(value.getRespTypeString());
598 }
599
600
601 else if (valueType == SnmpConstants.ASN_NULL) {
602 result.oid = oid.toString();
603 result.type = "null";
604 }
605
606
607
608
609
610 else if (valueType == SnmpConstants.ASN_OCTET_STR) {
611
612 result.oid = oid.toString();
613 result.type = "ASN_OCTET_STR";
614
615
616 AsnOctets octetStringValue = (AsnOctets) value;
617
618
619
620
621
622
623
624 String octetStringValuetoString = octetStringValue.toString();
625
626
627
628 String octetStringValuetoHex = octetStringValue.toHex().replace(':', ' ');
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644 if (octetStringValuetoString.isEmpty() && octetStringValue.getBytes().length > 0) {
645 result.value = octetStringValuetoHex;
646 } else if (octetStringValuetoString.length() == octetStringValuetoHex.length()) {
647 result.value = octetStringValuetoHex;
648 } else {
649 result.value = octetStringValuetoString;
650 }
651 }
652
653
654 else {
655 result.oid = oid.toString();
656 result.type = value.getRespTypeString();
657 result.value = value.toString();
658 }
659
660 return result;
661
662 }
663
664 }