1#include <iostream>
2#include <string>
3
4extern "C" {
5#include "asn1.h"
6#include "snmp.h"
7#include "snmpclient.h"
8};
9
10#include "catch.hpp"
11
12using namespace std::string_literals;
13
14static inline int
15try_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
24TEST_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
36TEST_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
87TEST_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
147TEST_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
204TEST_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
239TEST_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
305TEST_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
391TEST_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
445TEST_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
485TEST_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