+static void
+print_eopt_ecs(netdissect_options *ndo, const u_char *cp,
+ u_int data_len)
+{
+ u_int family, addr_bits, src_len, scope_len;
+
+ u_char padded[32];
+ char addr[INET6_ADDRSTRLEN];
+
+ /* ecs option must at least contain family, src len, and scope len */
+ if (data_len < 4) {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ family = GET_BE_U_2(cp);
+ cp += 2;
+ src_len = GET_U_1(cp);
+ cp += 1;
+ scope_len = GET_U_1(cp);
+ cp += 1;
+
+ if (family == 1)
+ addr_bits = 32;
+ else if (family == 2)
+ addr_bits = 128;
+ else {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ if (data_len - 4 > (addr_bits / 8)) {
+ nd_print_invalid(ndo);
+ return;
+ }
+ /* checks for invalid ecs scope or source length */
+ if (src_len > addr_bits || scope_len > addr_bits || ((src_len + 7) / 8) != (data_len - 4)) {
+ nd_print_invalid(ndo);
+ return;
+ }
+
+ /* pad the truncated address from ecs with zeros */
+ memset(padded, 0, sizeof(padded));
+ memcpy(padded, cp, data_len - 4);
+
+
+ if (family == 1)
+ ND_PRINT("%s/%d/%d", addrtostr(padded, addr, INET_ADDRSTRLEN),
+ src_len, scope_len);
+ else
+ ND_PRINT("%s/%d/%d", addrtostr6(padded, addr, INET6_ADDRSTRLEN),
+ src_len, scope_len);
+
+}
+
+extern const struct tok edns_opt2str[];
+extern const struct tok dau_alg2str[];
+extern const struct tok dhu_alg2str[];
+extern const struct tok n3u_alg2str[];
+
+
+/* print an <EDNS-option> */
+static const u_char *
+eopt_print(netdissect_options *ndo,
+ const u_char *cp)
+{
+ u_int opt, data_len, i;
+
+ if (!ND_TTEST_2(cp))
+ return (NULL);
+ opt = GET_BE_U_2(cp);
+ cp += 2;
+ ND_PRINT("%s", tok2str(edns_opt2str, "Opt%u", opt));
+ if (!ND_TTEST_2(cp))
+ return (NULL);
+ data_len = GET_BE_U_2(cp);
+ cp += 2;
+
+ ND_TCHECK_LEN(cp, data_len);
+
+ if (data_len > 0) {
+ ND_PRINT(" ");
+ switch (opt) {
+
+ case E_ECS:
+ print_eopt_ecs(ndo, cp, data_len);
+ break;
+ case E_COOKIE:
+ if (data_len < 8 || (data_len > 8 && data_len < 16) || data_len > 40)
+ nd_print_invalid(ndo);
+ else {
+ for (i = 0; i < data_len; ++i) {
+ /* split client and server cookie */
+ if (i == 8)
+ ND_PRINT(" ");
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ }
+ }
+ break;
+ case E_KEEPALIVE:
+ if (data_len != 2)
+ nd_print_invalid(ndo);
+ else
+ /* keepalive is in increments of 100ms. Convert to seconds */
+ ND_PRINT("%0.1f sec", (GET_BE_U_2(cp) / 10.0));
+ break;
+ case E_EXPIRE:
+ if (data_len != 4)
+ nd_print_invalid(ndo);
+ else
+ ND_PRINT("%u sec", GET_BE_U_4(cp));
+ break;
+ case E_PADDING:
+ /* ignore contents and just print length */
+ ND_PRINT("(%u)", data_len);
+ break;
+ case E_KEYTAG:
+ if (data_len % 2 != 0)
+ nd_print_invalid(ndo);
+ else
+ for (i = 0; i < data_len; i += 2) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%u", GET_BE_U_2(cp + i));
+ }
+ break;
+ case E_DAU:
+ for (i = 0; i < data_len; ++i) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%s", tok2str(dau_alg2str, "Alg_%u", GET_U_1(cp + i)));
+ }
+ break;
+ case E_DHU:
+ for (i = 0; i < data_len; ++i) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%s", tok2str(dhu_alg2str, "Alg_%u", GET_U_1(cp + i)));
+ }
+ break;
+ case E_N3U:
+ for (i = 0; i < data_len; ++i) {
+ if (i > 0)
+ ND_PRINT(" ");
+ ND_PRINT("%s", tok2str(n3u_alg2str, "Alg_%u", GET_U_1(cp + i)));
+ }
+ break;
+ case E_CHAIN:
+ fqdn_print(ndo, cp, cp + data_len);
+ break;
+ case E_NSID:
+ /* intentional fall-through. NSID is an undefined byte string */
+ default:
+ for (i = 0; i < data_len; ++i)
+ ND_PRINT("%02x", GET_U_1(cp + i));
+ break;
+ }
+ }
+ return (cp + data_len);
+
+ trunc:
+ return (NULL);
+
+}
+
+
+
+extern const struct tok ns_type2str[];
+
+/* https://www.iana.org/assignments/dns-parameters */