1 /* $NetBSD: inet_prefix_top.c,v 1.2 2023/12/23 20:30:46 christos Exp $ */
2
3 /*++
4 /* NAME
5 /* inet_prefix_top 3
6 /* SUMMARY
7 /* convert net/mask to printable string
8 /* SYNOPSIS
9 /* #include <inet_prefix_top.h>
10 /*
11 /* char *inet_prefix_top(
12 /* int family,
13 /* const void *src,
14 /* int prefix_len)
15 /* DESCRIPTION
16 /* inet_prefix_top() prints the network portion of the specified
17 /* IPv4 or IPv6 address, null bits for the host portion, and
18 /* the prefix length if it is shorter than the address.
19 /* The result should be passed to myfree(). The code can
20 /* handle addresses of any length, and bytes of any width.
21 /*
22 /* Arguments:
23 /* .IP af
24 /* The address family, as with inet_ntop().
25 /* .IP src
26 /* Pointer to storage for an IPv4 or IPv6 address, as with
27 /* inet_ntop().
28 /* .IP prefix_len
29 /* The number of most-significant bits in \fBsrc\fR that should
30 /* not be cleared.
31 /* DIAGNOSTICS
32 /* Panic: unexpected protocol family, bad prefix length. Fatal
33 /* errors: address conversion error.
34 /* LICENSE
35 /* .ad
36 /* .fi
37 /* The Secure Mailer license must be distributed with this software.
38 /* AUTHOR(S)
39 /* Wietse Venema
40 /*--*/
41
42 /*
43 * System library.
44 */
45 #include <sys_defs.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <string.h>
50
51 /*
52 * Utility library.
53 */
54 #include <mask_addr.h>
55 #include <msg.h>
56 #include <inet_addr_sizes.h>
57 #include <inet_prefix_top.h>
58 #include <vstring.h>
59
60 /* inet_prefix_top - printable net/mask pattern */
61
inet_prefix_top(int af,const void * src,int prefix_len)62 char *inet_prefix_top(int af, const void *src, int prefix_len)
63 {
64 const char myname[] = "inet_prefix_top";
65 union {
66 struct in_addr in_addr;
67 struct in6_addr in6_addr;
68 } u;
69 VSTRING *buf;
70 const INET_ADDR_SIZES *sp;
71
72 /*
73 * Sanity checks. XXX We use msg_fatal() because mail_conf_int() does not
74 * (yet) support non-negative integers.
75 */
76 if ((sp = inet_addr_sizes(af)) == 0)
77 msg_panic("%s: unexpected address family: %d", myname, af);
78 if (prefix_len > sp->addr_bitcount || prefix_len < 0)
79 msg_fatal("%s: bad %s address prefix length: %d",
80 myname, sp->ipproto_str, prefix_len);
81
82 /*
83 * Strip a copy of the input address. When allocating the result memory,
84 * add 1 for the string terminator from inet_ntop(), or 1 for the '/'
85 * before the prefix. We should not rely on vstring(3)'s safety byte.
86 */
87 memcpy((void *) &u, src, sp->addr_bytecount);
88 if (prefix_len < sp->addr_bitcount) {
89 mask_addr((unsigned char *) &u, sp->addr_bytecount, prefix_len);
90 buf = vstring_alloc(sp->addr_strlen + sp->addr_bitcount_strlen + 1);
91 } else {
92 buf = vstring_alloc(sp->addr_strlen + 1);
93 }
94
95 /*
96 * Convert the result to string, and append the optional /prefix.
97 */
98 if (inet_ntop(af, &u, vstring_str(buf), vstring_avail(buf)) == 0)
99 msg_fatal("%s: inet_ntop: %m", myname);
100 vstring_set_payload_size(buf, strlen(vstring_str(buf)));
101 if (prefix_len < sp->addr_bitcount)
102 vstring_sprintf_append(buf, "/%d", prefix_len);
103 return (vstring_export(buf));
104 }
105
106 #ifdef TEST
107
108 #include <stdlib.h>
109 #include <msg_vstream.h>
110 #include <name_code.h>
111
112 /*
113 * TODO: add test cases for fatal and panic errors, intercept msg_fatal()
114 * and msg_panic(), and verify the expected error messages.
115 */
116 typedef struct TEST_CASE {
117 int in_af;
118 int in_prefix_len;
119 const char *exp_prefix;
120 } TEST_CASE;
121
122 static TEST_CASE test_cases[] = {
123 AF_INET, 32, "255.255.255.255",
124 AF_INET, 28, "255.255.255.240/28",
125 AF_INET, 4, "240.0.0.0/4",
126 AF_INET, 0, "0.0.0.0/0",
127 AF_INET6, 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
128 AF_INET6, 124, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0/124",
129 AF_INET6, 4, "f000::/4",
130 AF_INET6, 0, "::/0",
131 };
132
133 const NAME_CODE af_map[] = {
134 "AF_INET", AF_INET,
135 "AF_INET6", AF_INET6,
136 0,
137 };
138
139 #define TEST_CASE_COUNT (sizeof(test_cases) / sizeof(test_cases[0]))
140
main(int argc,char ** argv)141 int main(int argc, char **argv)
142 {
143 TEST_CASE *tp;
144 union {
145 struct in_addr in_addr;
146 struct in6_addr in6_addr;
147 } u;
148 char *act_prefix;
149 int pass = 0;
150 int fail = 0;
151
152 msg_vstream_init(argv[0], VSTREAM_ERR);
153 memset(&u, ~0, sizeof(u));
154
155 for (tp = test_cases; tp < test_cases + TEST_CASE_COUNT; tp++) {
156 msg_info("RUN %s/%d", str_name_code(af_map, tp->in_af),
157 tp->in_prefix_len);
158 act_prefix = inet_prefix_top(tp->in_af, &u, tp->in_prefix_len);
159 if (strcmp(act_prefix, tp->exp_prefix) != 0) {
160 msg_warn("got \"%s\", want \"%s\"", act_prefix, tp->exp_prefix);
161 fail += 1;
162 msg_info("FAIL %s/%d", str_name_code(af_map, tp->in_af),
163 tp->in_prefix_len);
164 } else {
165 pass += 1;
166 msg_info("PASS %s/%d", str_name_code(af_map, tp->in_af),
167 tp->in_prefix_len);
168 }
169 }
170 msg_info("PASS=%d FAIL=%d", pass, fail);
171 return (fail > 0);
172 }
173
174 #endif /* TEST */
175