/*++ /* NAME /* inet_prefix_top 3 /* SUMMARY /* convert net/mask to printable string /* SYNOPSIS /* #include /* /* char *inet_prefix_top( /* int family, /* const void *src, /* int prefix_len) /* DESCRIPTION /* inet_prefix_top() prints the network portion of the specified /* IPv4 or IPv6 address, null bits for the host portion, and /* the prefix length if it is shorter than the address. /* The result should be passed to myfree(). The code can /* handle addresses of any length, and bytes of any width. /* /* Arguments: /* .IP af /* The address family, as with inet_ntop(). /* .IP src /* Pointer to storage for an IPv4 or IPv6 address, as with /* inet_ntop(). /* .IP prefix_len /* The number of most-significant bits in \fBsrc\fR that should /* not be cleared. /* DIAGNOSTICS /* Panic: unexpected protocol family, bad prefix length. Fatal /* errors: address conversion error. /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* AUTHOR(S) /* Wietse Venema /*--*/ /* * System library. */ #include #include #include #include #include /* * Utility library. */ #include #include #include #include #include /* inet_prefix_top - printable net/mask pattern */ char *inet_prefix_top(int af, const void *src, int prefix_len) { const char myname[] = "inet_prefix_top"; union { struct in_addr in_addr; struct in6_addr in6_addr; } u; VSTRING *buf; const INET_ADDR_SIZES *sp; /* * Sanity checks. XXX We use msg_fatal() because mail_conf_int() does not * (yet) support non-negative integers. */ if ((sp = inet_addr_sizes(af)) == 0) msg_panic("%s: unexpected address family: %d", myname, af); if (prefix_len > sp->addr_bitcount || prefix_len < 0) msg_fatal("%s: bad %s address prefix length: %d", myname, sp->ipproto_str, prefix_len); /* * Strip a copy of the input address. When allocating the result memory, * add 1 for the string terminator from inet_ntop(), or 1 for the '/' * before the prefix. We should not rely on vstring(3)'s safety byte. */ memcpy((void *) &u, src, sp->addr_bytecount); if (prefix_len < sp->addr_bitcount) { mask_addr((unsigned char *) &u, sp->addr_bytecount, prefix_len); buf = vstring_alloc(sp->addr_strlen + sp->addr_bitcount_strlen + 1); } else { buf = vstring_alloc(sp->addr_strlen + 1); } /* * Convert the result to string, and append the optional /prefix. */ if (inet_ntop(af, &u, vstring_str(buf), vstring_avail(buf)) == 0) msg_fatal("%s: inet_ntop: %m", myname); vstring_set_payload_size(buf, strlen(vstring_str(buf))); if (prefix_len < sp->addr_bitcount) vstring_sprintf_append(buf, "/%d", prefix_len); return (vstring_export(buf)); } #ifdef TEST #include #include #include /* * TODO: add test cases for fatal and panic errors, intercept msg_fatal() * and msg_panic(), and verify the expected error messages. */ typedef struct TEST_CASE { int in_af; int in_prefix_len; const char *exp_prefix; } TEST_CASE; static TEST_CASE test_cases[] = { AF_INET, 32, "255.255.255.255", AF_INET, 28, "255.255.255.240/28", AF_INET, 4, "240.0.0.0/4", AF_INET, 0, "0.0.0.0/0", AF_INET6, 128, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", AF_INET6, 124, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff0/124", AF_INET6, 4, "f000::/4", AF_INET6, 0, "::/0", }; const NAME_CODE af_map[] = { "AF_INET", AF_INET, "AF_INET6", AF_INET6, 0, }; #define TEST_CASE_COUNT (sizeof(test_cases) / sizeof(test_cases[0])) int main(int argc, char **argv) { TEST_CASE *tp; union { struct in_addr in_addr; struct in6_addr in6_addr; } u; char *act_prefix; int pass = 0; int fail = 0; msg_vstream_init(argv[0], VSTREAM_ERR); memset(&u, ~0, sizeof(u)); for (tp = test_cases; tp < test_cases + TEST_CASE_COUNT; tp++) { msg_info("RUN %s/%d", str_name_code(af_map, tp->in_af), tp->in_prefix_len); act_prefix = inet_prefix_top(tp->in_af, &u, tp->in_prefix_len); if (strcmp(act_prefix, tp->exp_prefix) != 0) { msg_warn("got \"%s\", want \"%s\"", act_prefix, tp->exp_prefix); fail += 1; msg_info("FAIL %s/%d", str_name_code(af_map, tp->in_af), tp->in_prefix_len); } else { pass += 1; msg_info("PASS %s/%d", str_name_code(af_map, tp->in_af), tp->in_prefix_len); } myfree(act_prefix); } msg_info("PASS=%d FAIL=%d", pass, fail); return (fail > 0); } #endif /* TEST */