1 #include <iostream>
2 #include <string>
3 
4 extern "C" {
5 #include "asn1.h"
6 #include "snmp.h"
7 #include "snmpclient.h"
8 };
9 
10 #include "catch.hpp"
11 
12 using namespace std::string_literals;
13 
14 static inline int
15 try_parse(struct snmp_client *sc, const char *str)
16 {
17 	const int r = snmp_parse_server(sc, str);
18 	if (false && r != 0)
19 		std::cout << "snmp_parse_server: " << sc->error << "\n";
20 	return r;
21 
22 }
23 
24 TEST_CASE("snmp_parse_server: empty string", "[snmp_parse_server]") {
25 	struct snmp_client sc;
26 	snmp_client_init(&sc);
27 
28 	REQUIRE(try_parse(&sc, "") == 0);
29 	REQUIRE(sc.trans == SNMP_TRANS_UDP);
30 	REQUIRE(sc.chost == ""s);
31 	REQUIRE(sc.cport == "snmp"s);
32 	REQUIRE(sc.read_community == "public"s);
33 	REQUIRE(sc.write_community == "private"s);
34 }
35 
36 TEST_CASE("snmp_parse_server: hostname only", "[snmp_parse_server]") {
37 	struct snmp_client sc;
38 	snmp_client_init(&sc);
39 
40 	SECTION("simple name without special characters") {
41 		const auto str = "somehost"s;
42 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
43 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
44 		REQUIRE(sc.chost == str);
45 		REQUIRE(sc.cport == "snmp"s);
46 		REQUIRE(sc.read_community == "public"s);
47 		REQUIRE(sc.write_community == "private"s);
48 	}
49 	SECTION("complex host name without special characters") {
50 		const auto str = "some.host.domain"s;
51 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
52 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
53 		REQUIRE(sc.chost == str);
54 		REQUIRE(sc.cport == "snmp"s);
55 		REQUIRE(sc.read_community == "public"s);
56 		REQUIRE(sc.write_community == "private"s);
57 	}
58 	SECTION("complex host name with special characters") {
59 		const auto str = "some-mul.host-32.domain."s;
60 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
61 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
62 		REQUIRE(sc.chost == str);
63 		REQUIRE(sc.cport == "snmp"s);
64 		REQUIRE(sc.read_community == "public"s);
65 		REQUIRE(sc.write_community == "private"s);
66 	}
67 	SECTION("relative path name") {
68 		const auto str = "foo/bar"s;
69 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
70 		REQUIRE(sc.trans == SNMP_TRANS_LOC_DGRAM);
71 		REQUIRE(sc.chost == str);
72 		REQUIRE(sc.cport == ""s);
73 		REQUIRE(sc.read_community == "public"s);
74 		REQUIRE(sc.write_community == "private"s);
75 	}
76 	SECTION("absolute path name") {
77 		const auto str = "/foo/bar"s;
78 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
79 		REQUIRE(sc.trans == SNMP_TRANS_LOC_DGRAM);
80 		REQUIRE(sc.chost == str);
81 		REQUIRE(sc.cport == ""s);
82 		REQUIRE(sc.read_community == "public"s);
83 		REQUIRE(sc.write_community == "private"s);
84 	}
85 }
86 
87 TEST_CASE("snmp_parse_server: ipv6 address only", "[snmp_parse_server]") {
88 	struct snmp_client sc;
89 	snmp_client_init(&sc);
90 
91 	SECTION("in6_addr_any") {
92 		const auto host = "::"s;
93 		const auto str = "[" + host + "]";
94 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
95 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
96 		REQUIRE(sc.chost == host);
97 		REQUIRE(sc.cport == "snmp"s);
98 		REQUIRE(sc.read_community == "public"s);
99 		REQUIRE(sc.write_community == "private"s);
100 	}
101 	SECTION("localhost") {
102 		const auto host = "::1"s;
103 		const auto str = "[" + host + "]";
104 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
105 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
106 		REQUIRE(sc.chost == host);
107 		REQUIRE(sc.cport == "snmp"s);
108 		REQUIRE(sc.read_community == "public"s);
109 		REQUIRE(sc.write_community == "private"s);
110 	}
111 	SECTION("link local address") {
112 		const auto host = "fc00:0:12::1"s;
113 		const auto str = "[" + host + "]";
114 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
115 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
116 		REQUIRE(sc.chost == host);
117 		REQUIRE(sc.cport == "snmp"s);
118 		REQUIRE(sc.read_community == "public"s);
119 		REQUIRE(sc.write_community == "private"s);
120 	}
121 	SECTION("illegal address (bad character)") {
122 		const auto host = "fc00:0:1x::1"s;
123 		const auto str = "[" + host + "]";
124 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
125 		REQUIRE(sc.error == host + ": Name does not resolve");
126 	}
127 	SECTION("illegal address (two double colons)") {
128 		const auto host = "fc00::0:12::1"s;
129 		const auto str = "[" + host + "]";
130 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
131 		REQUIRE(sc.error == host + ": Name does not resolve");
132 	}
133 	SECTION("illegal address (two many colons)") {
134 		const auto host = "1:2:3:4:5:6:7:8:9"s;
135 		const auto str = "[" + host + "]";
136 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
137 		REQUIRE(sc.error == host + ": Name does not resolve");
138 	}
139 	SECTION("ipv6 address and junk") {
140 		const auto host = "::"s;
141 		const auto str = "[" + host + "]" + "xxx";
142 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
143 		REQUIRE(sc.error == "junk at end of server specification 'xxx'"s);
144 	}
145 }
146 
147 TEST_CASE("snmp_parse_server: hostname and port", "[snmp_parse_server]") {
148 	struct snmp_client sc;
149 	snmp_client_init(&sc);
150 
151 	SECTION("simple name and numeric port") {
152 		const auto host = "somehost"s;
153 		const auto port = "10007"s;
154 		const auto str = host + ":" + port;
155 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
156 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
157 		REQUIRE(sc.chost == host);
158 		REQUIRE(sc.cport == port);
159 		REQUIRE(sc.read_community == "public"s);
160 		REQUIRE(sc.write_community == "private"s);
161 	}
162 	SECTION("simple name and string port") {
163 		const auto host = "somehost"s;
164 		const auto port = "telnet"s;
165 		const auto str = host + ":" + port;
166 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
167 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
168 		REQUIRE(sc.chost == host);
169 		REQUIRE(sc.cport == port);
170 		REQUIRE(sc.read_community == "public"s);
171 		REQUIRE(sc.write_community == "private"s);
172 	}
173 	SECTION("name with embedded colon and numeric port") {
174 		const auto host = "somehost:foo"s;
175 		const auto port = "10007"s;
176 		const auto str = host + ":" + port;
177 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
178 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
179 		REQUIRE(sc.chost == host);
180 		REQUIRE(sc.cport == port);
181 		REQUIRE(sc.read_community == "public"s);
182 		REQUIRE(sc.write_community == "private"s);
183 	}
184 	SECTION("FQDN with embedded colon and numeric port") {
185 		const auto host = "bla.blub:foo.baz."s;
186 		const auto port = "10007"s;
187 		const auto str = host + ":" + port;
188 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
189 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
190 		REQUIRE(sc.chost == host);
191 		REQUIRE(sc.cport == port);
192 		REQUIRE(sc.read_community == "public"s);
193 		REQUIRE(sc.write_community == "private"s);
194 	}
195 	SECTION("simple name and empty port") {
196 		const auto host = "somehost"s;
197 		const auto port = ""s;
198 		const auto str = host + ":" + port;
199 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
200 		REQUIRE(sc.error == "empty port name"s);
201 	}
202 }
203 
204 TEST_CASE("snmp_parse_server: ipv6 and port", "[snmp_parse_server]") {
205 	struct snmp_client sc;
206 	snmp_client_init(&sc);
207 
208 	SECTION("ANY address and numeric port") {
209 		const auto host = "::"s;
210 		const auto port = "10007"s;
211 		const auto str = "[" + host + "]:" + port;
212 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
213 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
214 		REQUIRE(sc.chost == host);
215 		REQUIRE(sc.cport == port);
216 		REQUIRE(sc.read_community == "public"s);
217 		REQUIRE(sc.write_community == "private"s);
218 	}
219 	SECTION("localhost address and string port") {
220 		const auto host = "::1"s;
221 		const auto port = "snmp"s;
222 		const auto str = "[" + host + "]:" + port;
223 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
224 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
225 		REQUIRE(sc.chost == host);
226 		REQUIRE(sc.cport == port);
227 		REQUIRE(sc.read_community == "public"s);
228 		REQUIRE(sc.write_community == "private"s);
229 	}
230 	SECTION("some address name and empty port") {
231 		const auto host = "fc00:00:01::2:3"s;
232 		const auto port = ""s;
233 		const auto str = "[" + host + "]:" + port;
234 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
235 		REQUIRE(sc.error == "empty port name"s);
236 	}
237 }
238 
239 TEST_CASE("snmp_parse_server: IPv4 address only", "[snmp_parse_server]") {
240 	struct snmp_client sc;
241 	snmp_client_init(&sc);
242 
243 	SECTION("single octet address") {
244 		const auto host = "127"s;
245 		const auto str = host;
246 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
247 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
248 		REQUIRE(sc.chost == host);
249 		REQUIRE(sc.cport == "snmp"s);
250 		REQUIRE(sc.read_community == "public"s);
251 		REQUIRE(sc.write_community == "private"s);
252 	}
253 	SECTION("two octet address") {
254 		const auto host = "127.1"s;
255 		const auto str = host;
256 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
257 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
258 		REQUIRE(sc.chost == host);
259 		REQUIRE(sc.cport == "snmp"s);
260 		REQUIRE(sc.read_community == "public"s);
261 		REQUIRE(sc.write_community == "private"s);
262 	}
263 	SECTION("three octet address") {
264 		const auto host = "127.23.1"s;
265 		const auto str = host;
266 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
267 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
268 		REQUIRE(sc.chost == host);
269 		REQUIRE(sc.cport == "snmp"s);
270 		REQUIRE(sc.read_community == "public"s);
271 		REQUIRE(sc.write_community == "private"s);
272 	}
273 	SECTION("four octet address") {
274 		const auto host = "127.18.23.1"s;
275 		const auto str = host;
276 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
277 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
278 		REQUIRE(sc.chost == host);
279 		REQUIRE(sc.cport == "snmp"s);
280 		REQUIRE(sc.read_community == "public"s);
281 		REQUIRE(sc.write_community == "private"s);
282 	}
283 	SECTION("four octet octal address") {
284 		const auto host = "0300.077.0377.01"s;
285 		const auto str = host;
286 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
287 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
288 		REQUIRE(sc.chost == host);
289 		REQUIRE(sc.cport == "snmp"s);
290 		REQUIRE(sc.read_community == "public"s);
291 		REQUIRE(sc.write_community == "private"s);
292 	}
293 	SECTION("four octet hex address") {
294 		const auto host = "x80.x12.xff.x1"s;
295 		const auto str = host;
296 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
297 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
298 		REQUIRE(sc.chost == host);
299 		REQUIRE(sc.cport == "snmp"s);
300 		REQUIRE(sc.read_community == "public"s);
301 		REQUIRE(sc.write_community == "private"s);
302 	}
303 }
304 
305 TEST_CASE("snmp_parse_server: transport and hostname", "[snmp_parse_server]") {
306 	struct snmp_client sc;
307 	snmp_client_init(&sc);
308 
309 	SECTION("udp and host") {
310 		const auto trans = "udp"s;
311 		const auto host = "somehost"s;
312 		const auto str = trans + "::" + host;
313 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
314 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
315 		REQUIRE(sc.chost == host);
316 		REQUIRE(sc.cport == "snmp"s);
317 		REQUIRE(sc.read_community == "public"s);
318 		REQUIRE(sc.write_community == "private"s);
319 	}
320 	SECTION("udp and ipv4 address") {
321 		const auto trans = "udp"s;
322 		const auto host = "240.0.1.2"s;
323 		const auto str = trans + "::" + host;
324 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
325 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
326 		REQUIRE(sc.chost == host);
327 		REQUIRE(sc.cport == "snmp"s);
328 		REQUIRE(sc.read_community == "public"s);
329 		REQUIRE(sc.write_community == "private"s);
330 	}
331 	SECTION("udp6 and host") {
332 		const auto trans = "udp6"s;
333 		const auto host = "somehost"s;
334 		const auto str = trans + "::" + host;
335 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
336 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
337 		REQUIRE(sc.chost == host);
338 		REQUIRE(sc.cport == "snmp"s);
339 		REQUIRE(sc.read_community == "public"s);
340 		REQUIRE(sc.write_community == "private"s);
341 	}
342 	SECTION("udp6 and ipv6 address") {
343 		const auto trans = "udp6"s;
344 		const auto host = "fec0:0:2::17"s;
345 		const auto str = trans + "::[" + host + "]";
346 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
347 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
348 		REQUIRE(sc.chost == host);
349 		REQUIRE(sc.cport == "snmp"s);
350 		REQUIRE(sc.read_community == "public"s);
351 		REQUIRE(sc.write_community == "private"s);
352 	}
353 	SECTION("dgram and host") {
354 		const auto trans = "dgram"s;
355 		const auto host = "somehost"s;
356 		const auto str = trans + "::" + host;
357 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
358 		REQUIRE(sc.trans == SNMP_TRANS_LOC_DGRAM);
359 		REQUIRE(sc.chost == host);
360 		REQUIRE(sc.cport == ""s);
361 		REQUIRE(sc.read_community == "public"s);
362 		REQUIRE(sc.write_community == "private"s);
363 	}
364 	SECTION("stream and host") {
365 		const auto trans = "stream"s;
366 		const auto host = "somehost"s;
367 		const auto str = trans + "::" + host;
368 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
369 		REQUIRE(sc.trans == SNMP_TRANS_LOC_STREAM);
370 		REQUIRE(sc.chost == host);
371 		REQUIRE(sc.cport == ""s);
372 		REQUIRE(sc.read_community == "public"s);
373 		REQUIRE(sc.write_community == "private"s);
374 	}
375 	SECTION("unknown transport and host") {
376 		const auto trans = "foo"s;
377 		const auto host = "somehost"s;
378 		const auto str = trans + "::" + host;
379 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
380 		REQUIRE(sc.error == "unknown transport specifier '" + trans + "'");
381 	}
382 	SECTION("empty transport and host") {
383 		const auto trans = ""s;
384 		const auto host = "somehost"s;
385 		const auto str = trans + "::" + host;
386 		REQUIRE(try_parse(&sc, str.c_str()) == -1);
387 		REQUIRE(sc.error == "empty transport specifier"s);
388 	}
389 }
390 
391 TEST_CASE("snmp_parse_server: transport, host and port", "[snmp_parse_server]") {
392 	struct snmp_client sc;
393 	snmp_client_init(&sc);
394 
395 	SECTION("udp, host and port") {
396 		const auto trans = "udp"s;
397 		const auto host = "somehost"s;
398 		const auto port = "ssh"s;
399 		const auto str = trans + "::" + host + ":" + port;
400 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
401 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
402 		REQUIRE(sc.chost == host);
403 		REQUIRE(sc.cport == port);
404 		REQUIRE(sc.read_community == "public"s);
405 		REQUIRE(sc.write_community == "private"s);
406 	}
407 	SECTION("udp, host with colon and port") {
408 		const auto trans = "udp"s;
409 		const auto host = "somehost:foo"s;
410 		const auto port = "ssh"s;
411 		const auto str = trans + "::" + host + ":" + port;
412 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
413 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
414 		REQUIRE(sc.chost == host);
415 		REQUIRE(sc.cport == port);
416 		REQUIRE(sc.read_community == "public"s);
417 		REQUIRE(sc.write_community == "private"s);
418 	}
419 	SECTION("udp and port") {
420 		const auto trans = "udp"s;
421 		const auto host = ""s;
422 		const auto port = "ssh"s;
423 		const auto str = trans + "::" + host + ":" + port;
424 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
425 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
426 		REQUIRE(sc.chost == host);
427 		REQUIRE(sc.cport == port);
428 		REQUIRE(sc.read_community == "public"s);
429 		REQUIRE(sc.write_community == "private"s);
430 	}
431 	SECTION("udp6, ipv6 and port") {
432 		const auto trans = "udp6"s;
433 		const auto host = "::1:2"s;
434 		const auto port = "ssh"s;
435 		const auto str = trans + "::[" + host + "]:" + port;
436 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
437 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
438 		REQUIRE(sc.chost == host);
439 		REQUIRE(sc.cport == port);
440 		REQUIRE(sc.read_community == "public"s);
441 		REQUIRE(sc.write_community == "private"s);
442 	}
443 }
444 
445 TEST_CASE("snmp_parse_server: community and host", "[snmp_parse_server]")
446 {
447 	struct snmp_client sc;
448 	snmp_client_init(&sc);
449 
450 	SECTION("community and host") {
451 		const auto comm = "public"s;
452 		const auto host = "server.com"s;
453 		const auto str = comm + "@" + host;
454 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
455 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
456 		REQUIRE(sc.chost == host);
457 		REQUIRE(sc.cport == "snmp"s);
458 		REQUIRE(sc.read_community == comm);
459 		REQUIRE(sc.write_community == comm);
460 	}
461 	SECTION("community with @ and host") {
462 		const auto comm = "public@bla"s;
463 		const auto host = "server.com"s;
464 		const auto str = comm + "@" + host;
465 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
466 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
467 		REQUIRE(sc.chost == host);
468 		REQUIRE(sc.cport == "snmp"s);
469 		REQUIRE(sc.read_community == comm);
470 		REQUIRE(sc.write_community == comm);
471 	}
472 	SECTION("empty community and host") {
473 		const auto comm = ""s;
474 		const auto host = "server.com"s;
475 		const auto str = comm + "@" + host;
476 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
477 		REQUIRE(sc.trans == SNMP_TRANS_UDP);
478 		REQUIRE(sc.chost == host);
479 		REQUIRE(sc.cport == "snmp"s);
480 		REQUIRE(sc.read_community == comm);
481 		REQUIRE(sc.write_community == comm);
482 	}
483 }
484 
485 TEST_CASE("snmp_parse_server: transport, community, host and port", "[snmp_parse_server]")
486 {
487 	struct snmp_client sc;
488 	snmp_client_init(&sc);
489 
490 	SECTION("transport, community, host and numeric port") {
491 		const auto trans = "udp6"s;
492 		const auto comm = "public"s;
493 		const auto host = "server.com"s;
494 		const auto port = "65000"s;
495 		const auto str = trans + "::" + comm + "@" + host + ":" + port;
496 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
497 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
498 		REQUIRE(sc.chost == host);
499 		REQUIRE(sc.cport == port);
500 		REQUIRE(sc.read_community == comm);
501 		REQUIRE(sc.write_community == comm);
502 	}
503 	SECTION("transport, community, ipv4 and symbolic port") {
504 		const auto trans = "udp6"s;
505 		const auto comm = "public"s;
506 		const auto host = "127.1"s;
507 		const auto port = "ftp"s;
508 		const auto str = trans + "::" + comm + "@" + host + ":" + port;
509 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
510 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
511 		REQUIRE(sc.chost == host);
512 		REQUIRE(sc.cport == port);
513 		REQUIRE(sc.read_community == comm);
514 		REQUIRE(sc.write_community == comm);
515 	}
516 	SECTION("transport, community, ipv6 and symbolic port") {
517 		const auto trans = "udp"s;
518 		const auto comm = "public"s;
519 		const auto host = "fe80::1:2"s;
520 		const auto port = "ftp"s;
521 		const auto str = trans + "::" + comm + "@[" + host + "]:" + port;
522 		REQUIRE(try_parse(&sc, str.c_str()) == 0);
523 		REQUIRE(sc.trans == SNMP_TRANS_UDP6);
524 		REQUIRE(sc.chost == host);
525 		REQUIRE(sc.cport == port);
526 		REQUIRE(sc.read_community == comm);
527 		REQUIRE(sc.write_community == comm);
528 	}
529 }
530