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 implements ISnmpClient {
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 @Override
309 public void freeResources() {
310 if (contextv1 != null) {
311 contextv1.destroy();
312 contextv1 = null;
313 }
314 if (contextv2c != null) {
315 contextv2c.destroy();
316 contextv2c = null;
317 }
318 if (contextv3 != null) {
319 contextv3.destroy();
320 contextv3 = null;
321 }
322
323 if (pdu != null) {
324 pdu = null;
325 }
326 }
327
328
329
330
331
332
333
334 private void createPdu() {
335
336 if (snmpVersion == SNMP_V2C) {
337 pdu = new BlockPdu(contextv2c);
338 } else if (snmpVersion == SNMP_V3) {
339 pdu = new BlockPdu(contextv3);
340 } else {
341 pdu = new BlockPdu(contextv1);
342 }
343
344
345 if (retryIntervals != null) {
346 pdu.setRetryIntervals(retryIntervals);
347 }
348 }
349
350
351
352
353
354
355
356
357 public String get(String oid) throws Exception {
358 createPdu();
359 pdu.setPduType(BlockPdu.GET);
360 pdu.addOid(oid);
361 return sendRequest().value;
362 }
363
364
365
366
367
368
369
370
371
372
373 public String getWithDetails(String oid) throws Exception {
374 createPdu();
375 pdu.setPduType(BlockPdu.GET);
376 pdu.addOid(oid);
377 SnmpResult result = sendRequest();
378 return result.oid + "\t" + result.type + "\t" + result.value;
379 }
380
381
382
383
384
385
386
387
388
389 public String getNext(String oid) throws Exception {
390 createPdu();
391 pdu.setPduType(BlockPdu.GETNEXT);
392 pdu.addOid(oid);
393 SnmpResult result = sendRequest();
394 return result.oid + "\t" + result.type + "\t" + result.value;
395 }
396
397
398
399
400
401
402
403
404
405
406
407 public String walk(String oid) throws Exception {
408
409 StringBuilder walkResult = new StringBuilder();
410 String currentOID;
411 SnmpResult getNextResult;
412
413
414 if (oid == null) {
415 throw new IllegalArgumentException("Invalid SNMP Walk OID: null");
416 }
417 if (oid.length() < 3) {
418 throw new IllegalArgumentException("Invalid SNMP Walk OID: \"" + oid + "\"");
419 }
420
421
422
423
424
425
426
427
428
429
430
431
432 getNext(oid);
433
434 currentOID = oid;
435 do {
436 createPdu();
437 pdu.setPduType(BlockPdu.GETNEXT);
438 pdu.addOid(currentOID);
439 try {
440 getNextResult = sendRequest();
441 } catch (Exception e) {
442
443 break;
444 }
445
446 currentOID = getNextResult.oid;
447 if (!currentOID.startsWith(oid)) {
448
449 break;
450 }
451
452
453 walkResult.append(currentOID + "\t" + getNextResult.type + "\t" + getNextResult.value + "\n");
454
455 } while (walkResult.length() < 10 * 1048576);
456
457
458
459 int resultLength = walkResult.length();
460 if (resultLength > 0) {
461 return walkResult.substring(0, resultLength - 1);
462 }
463
464
465 return "";
466 }
467
468
469
470
471
472
473
474
475
476
477
478 public List<List<String>> table(String rootOID, String[] selectColumnArray) throws Exception {
479
480
481 if (rootOID == null) {
482 throw new IllegalArgumentException("Invalid SNMP Table OID: null");
483 }
484 if (rootOID.length() < 3) {
485 throw new IllegalArgumentException("Invalid SNMP Table OID: \"" + rootOID + "\"");
486 }
487 if (selectColumnArray == null) {
488 throw new IllegalArgumentException("Invalid SNMP Table column numbers: null");
489 }
490 if (selectColumnArray.length < 1) {
491 throw new IllegalArgumentException("Invalid SNMP Table column numbers: none");
492 }
493
494
495
496
497 createPdu();
498 pdu.setPduType(BlockPdu.GETNEXT);
499 pdu.addOid(rootOID);
500 String firstValueOid = sendRequest().oid;
501 if (firstValueOid.isEmpty() || !firstValueOid.startsWith(rootOID)) {
502
503 return new ArrayList<>();
504 }
505
506 int tempIndex = firstValueOid.indexOf(".", rootOID.length() + 2);
507 if (tempIndex < 0) {
508
509
510 return new ArrayList<>();
511 }
512 String firstColumnOid = firstValueOid.substring(0, tempIndex);
513 int firstColumnOidLength = firstColumnOid.length();
514
515
516
517 ArrayList<String> IDArray = new ArrayList<String>(0);
518 String currentOID = firstColumnOid;
519 SnmpResult getNextResult;
520 do {
521
522 createPdu();
523 pdu.setPduType(BlockPdu.GETNEXT);
524 pdu.addOid(currentOID);
525 getNextResult = sendRequest();
526
527 currentOID = getNextResult.oid;
528
529
530 if (!currentOID.startsWith(firstColumnOid)) {
531 break;
532 }
533
534
535
536 IDArray.add(currentOID.substring(firstColumnOidLength + 1));
537
538 } while (IDArray.size() < 10000);
539
540
541 List<List<String>> tableResult = new ArrayList<>();
542 for (String ID : IDArray) {
543
544 List<String> row = new ArrayList<>();
545 for (String column : selectColumnArray) {
546
547
548
549 if (column.equals("ID")) {
550 row.add(ID);
551 } else {
552
553 try {
554 row.add(get(rootOID + "." + column + "." + ID));
555 } catch (Exception e) {
556 row.add("");
557 }
558 }
559 }
560 tableResult.add(row);
561 }
562
563
564 return tableResult;
565 }
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580 private SnmpResult sendRequest() throws PduException, IOException, Exception {
581
582
583 SnmpResult result = new SnmpResult();
584
585
586 varbind var = pdu.getResponseVariableBinding();
587
588
589 AsnObjectId oid = var.getOid();
590 AsnObject value = var.getValue();
591
592
593
594 byte valueType = value.getRespType();
595 if (valueType == SnmpConstants.SNMP_VAR_NOSUCHOBJECT ||
596 valueType == SnmpConstants.SNMP_VAR_NOSUCHINSTANCE ||
597 valueType == SnmpConstants.SNMP_VAR_ENDOFMIBVIEW) {
598 throw new Exception(value.getRespTypeString());
599 }
600
601
602 else if (valueType == SnmpConstants.ASN_NULL) {
603 result.oid = oid.toString();
604 result.type = "null";
605 }
606
607
608
609
610
611 else if (valueType == SnmpConstants.ASN_OCTET_STR) {
612
613 result.oid = oid.toString();
614 result.type = "ASN_OCTET_STR";
615
616
617 AsnOctets octetStringValue = (AsnOctets) value;
618
619
620
621
622
623
624
625 String octetStringValuetoString = octetStringValue.toString();
626
627
628
629 String octetStringValuetoHex = octetStringValue.toHex().replace(':', ' ');
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645 if (octetStringValuetoString.isEmpty() && octetStringValue.getBytes().length > 0) {
646 result.value = octetStringValuetoHex;
647 } else if (octetStringValuetoString.length() == octetStringValuetoHex.length()) {
648 result.value = octetStringValuetoHex;
649 } else {
650 result.value = octetStringValuetoString;
651 }
652 }
653
654
655 else {
656 result.oid = oid.toString();
657 result.type = value.getRespTypeString();
658 result.value = value.toString();
659 }
660
661 return result;
662
663 }
664
665 }