xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/inet_prefix_top.c (revision c48c605c14fd8622b523d1d6a3f0c0bad133ea89)
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