]> The Tcpdump Group git mirrors - tcpdump/blobdiff - print-bootp.c
CI: Add warning exemptions for Sun C (suncc-5.14) on Solaris 10
[tcpdump] / print-bootp.c
index ce34d13dd0a173e0522e8362e0218e5307c82ac7..0e47ef508f61c8f304056c415cea8ef8f078235e 100644 (file)
 
 /* \summary: BOOTP and IPv4 DHCP printer */
 
-#ifdef HAVE_CONFIG_H
 #include <config.h>
-#endif
 
 #include "netdissect-stdinc.h"
 
 #include <string.h>
 
+#define ND_LONGJMP_FROM_TCHECK
 #include "netdissect.h"
 #include "addrtoname.h"
 #include "extract.h"
@@ -147,7 +146,7 @@ struct bootp {
 #define        TAG_NIS_P_DOMAIN        ((uint8_t)  64)
 #define        TAG_NIS_P_SERVERS       ((uint8_t)  65)
 #define        TAG_MOBILE_HOME         ((uint8_t)  68)
-#define        TAG_SMPT_SERVER         ((uint8_t)  69)
+#define        TAG_SMTP_SERVER         ((uint8_t)  69)
 #define        TAG_POP3_SERVER         ((uint8_t)  70)
 #define        TAG_NNTP_SERVER         ((uint8_t)  71)
 #define        TAG_WWW_SERVER          ((uint8_t)  72)
@@ -180,10 +179,12 @@ struct bootp {
 /* RFC 2485 */
 #define        TAG_OPEN_GROUP_UAP      ((uint8_t)  98)
 /* RFC 2563 */
-#define        TAG_DISABLE_AUTOCONF    ((uint8_t) 116)
+#define        TAG_AUTO_CONFIGURE      ((uint8_t) 116)
 /* RFC 2610 */
 #define        TAG_SLP_DA              ((uint8_t)  78)
 #define        TAG_SLP_SCOPE           ((uint8_t)  79)
+/* RFC 8925 */
+#define TAG_IPV6_ONLY_PREFERRED        ((uint8_t) 108)
 /* RFC 2937 */
 #define        TAG_NS_SEARCH           ((uint8_t) 117)
 /* RFC 3004 - The User Class Option for DHCP */
@@ -193,6 +194,8 @@ struct bootp {
 /* RFC 3442 */
 #define TAG_CLASSLESS_STATIC_RT        ((uint8_t) 121)
 #define TAG_CLASSLESS_STA_RT_MS        ((uint8_t) 249)
+/* RFC8572 */
+#define TAG_SZTP_REDIRECT      ((uint8_t) 143)
 /* RFC 5859 - TFTP Server Address Option for DHCPv4 */
 #define        TAG_TFTP_SERVER_ADDRESS ((uint8_t) 150)
 /* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml */
@@ -210,8 +213,8 @@ struct bootp {
 #define        TAG_CLIENT_GUID         ((uint8_t)  97)
 #define        TAG_LDAP_URL            ((uint8_t)  95)
 /* RFC 4833, TZ codes */
-#define        TAG_TZ_PCODE            ((uint8_t) 100)
-#define        TAG_TZ_TCODE            ((uint8_t) 101)
+#define        TAG_TZ_PCODE            ((uint8_t) 100)
+#define        TAG_TZ_TCODE            ((uint8_t) 101)
 #define        TAG_NETINFO_PARENT      ((uint8_t) 112)
 #define        TAG_NETINFO_PARENT_TAG  ((uint8_t) 113)
 #define        TAG_URL                 ((uint8_t) 114)
@@ -260,9 +263,16 @@ struct cmu_vend {
 #define CLIENT_FQDN_FLAGS_N    0x08
 /* end of original bootp.h */
 
+static const struct tok fqdn_flags_bm[] = {
+       { CLIENT_FQDN_FLAGS_S, "S" },
+       { CLIENT_FQDN_FLAGS_O, "O" },
+       { CLIENT_FQDN_FLAGS_E, "E" },
+       { CLIENT_FQDN_FLAGS_N, "N" },
+       { 0, NULL }
+};
+
 static void rfc1048_print(netdissect_options *, const u_char *);
 static void cmu_print(netdissect_options *, const u_char *);
-static char *client_fqdn_flags(u_int flags);
 
 static const struct tok bootp_flag_values[] = {
        { 0x8000,       "Broadcast" },
@@ -295,8 +305,8 @@ bootp_print(netdissect_options *ndo,
 
        bp_htype = GET_U_1(bp->bp_htype);
        bp_hlen = GET_U_1(bp->bp_hlen);
-       if (bp_htype == 1 && bp_hlen == 6 && bp_op == BOOTPREQUEST) {
-               ND_PRINT(" from %s", GET_ETHERADDR_STRING(bp->bp_chaddr));
+       if (bp_htype == 1 && bp_hlen == MAC48_LEN && bp_op == BOOTPREQUEST) {
+               ND_PRINT(" from %s", GET_MAC48_STRING(bp->bp_chaddr));
        }
 
        ND_PRINT(", length %u", length);
@@ -311,7 +321,7 @@ bootp_print(netdissect_options *ndo,
                ND_PRINT(", htype %u", bp_htype);
 
        /* The usual length for 10Mb Ethernet address is 6 bytes */
-       if (bp_htype != 1 || bp_hlen != 6)
+       if (bp_htype != 1 || bp_hlen != MAC48_LEN)
                ND_PRINT(", hlen %u", bp_hlen);
 
        /* Only print interesting fields */
@@ -344,27 +354,27 @@ bootp_print(netdissect_options *ndo,
                ND_PRINT("\n\t  Gateway-IP %s", GET_IPADDR_STRING(bp->bp_giaddr));
 
        /* Client's Ethernet address */
-       if (bp_htype == 1 && bp_hlen == 6) {
-               ND_PRINT("\n\t  Client-Ethernet-Address %s", GET_ETHERADDR_STRING(bp->bp_chaddr));
+       if (bp_htype == 1 && bp_hlen == MAC48_LEN) {
+               ND_PRINT("\n\t  Client-Ethernet-Address %s", GET_MAC48_STRING(bp->bp_chaddr));
        }
 
        if (GET_U_1(bp->bp_sname)) {    /* get first char only */
                ND_PRINT("\n\t  sname \"");
                if (nd_printztn(ndo, bp->bp_sname, (u_int)sizeof(bp->bp_sname),
-                               ndo->ndo_snapend) == 0) {
+                               NULL) == 0) {
+                       /* Within the buffer, but not NUL-terminated. */
                        ND_PRINT("\"");
-                       nd_print_trunc(ndo);
-                       return;
+                       goto invalid;
                }
                ND_PRINT("\"");
        }
        if (GET_U_1(bp->bp_file)) {     /* get first char only */
                ND_PRINT("\n\t  file \"");
                if (nd_printztn(ndo, bp->bp_file, (u_int)sizeof(bp->bp_file),
-                               ndo->ndo_snapend) == 0) {
+                               NULL) == 0) {
+                       /* Ditto. */
                        ND_PRINT("\"");
-                       nd_print_trunc(ndo);
-                       return;
+                       goto invalid;
                }
                ND_PRINT("\"");
        }
@@ -384,19 +394,18 @@ bootp_print(netdissect_options *ndo,
                if (ul != 0)
                        ND_PRINT("\n\t  Vendor-#0x%x", ul);
        }
-
        return;
-trunc:
-       nd_print_trunc(ndo);
+invalid:
+       nd_print_invalid(ndo);
 }
 
 /*
  * The first character specifies the format to print:
  *     i - ip address (32 bits)
  *     p - ip address pairs (32 bits + 32 bits)
- *     l - long (32 bits)
- *     L - unsigned long (32 bits)
- *     s - short (16 bits)
+ *     l - unsigned longs (32 bits)
+ *     L - longs (32 bits)
+ *     s - unsigned shorts (16 bits)
  *     b - period-separated decimal bytes (variable length)
  *     x - colon-separated hex bytes (variable length)
  *     a - ASCII string (variable length)
@@ -461,7 +470,7 @@ static const struct tok tag2str[] = {
        { TAG_NIS_P_DOMAIN,     "sN+D" },
        { TAG_NIS_P_SERVERS,    "iN+S" },
        { TAG_MOBILE_HOME,      "iMH" },
-       { TAG_SMPT_SERVER,      "iSMTP" },
+       { TAG_SMTP_SERVER,      "iSMTP" },
        { TAG_POP3_SERVER,      "iPOP3" },
        { TAG_NNTP_SERVER,      "iNNTP" },
        { TAG_WWW_SERVER,       "iWWW" },
@@ -486,7 +495,7 @@ static const struct tok tag2str[] = {
 /* RFC 2485 */
        { TAG_OPEN_GROUP_UAP,   "aUAP" },
 /* RFC 2563 */
-       { TAG_DISABLE_AUTOCONF, "BNOAUTO" },
+       { TAG_AUTO_CONFIGURE,   "BAuto-Configure" },
 /* RFC 2610 */
        { TAG_SLP_DA,           "bSLP-DA" },    /*"b" is a little wrong */
        { TAG_SLP_SCOPE,        "bSLP-SCOPE" }, /*"b" is a little wrong */
@@ -499,6 +508,8 @@ static const struct tok tag2str[] = {
 /* RFC 3442 */
        { TAG_CLASSLESS_STATIC_RT, "$Classless-Static-Route" },
        { TAG_CLASSLESS_STA_RT_MS, "$Classless-Static-Route-Microsoft" },
+/* RFC 8572 */
+       { TAG_SZTP_REDIRECT,    "$SZTP-Redirect" },
 /* RFC 5859 - TFTP Server Address Option for DHCPv4 */
        { TAG_TFTP_SERVER_ADDRESS, "iTFTP-Server-Address" },
 /* https://www.iana.org/assignments/bootp-dhcp-parameters/bootp-dhcp-parameters.xhtml#options */
@@ -509,18 +520,19 @@ static const struct tok tag2str[] = {
        { TAG_TZ_STRING,        "aTZSTR" },
        { TAG_FQDN_OPTION,      "bFQDNS" },     /* XXX 'b' */
        { TAG_AUTH,             "bAUTH" },      /* XXX 'b' */
-       { TAG_CLIENT_LAST_TRANSACTION_TIME, "LLast-Transaction-Time" },
+       { TAG_CLIENT_LAST_TRANSACTION_TIME, "lLast-Transaction-Time" },
        { TAG_ASSOCIATED_IP,    "iAssociated-IP" },
        { TAG_CLIENT_ARCH,      "sARCH" },
        { TAG_CLIENT_NDI,       "bNDI" },       /* XXX 'b' */
        { TAG_CLIENT_GUID,      "bGUID" },      /* XXX 'b' */
        { TAG_LDAP_URL,         "aLDAP" },
-       { TAG_TZ_PCODE,         "aPOSIX-TZ" },
-       { TAG_TZ_TCODE,         "aTZ-Name" },
+       { TAG_TZ_PCODE,         "aPOSIX-TZ" },
+       { TAG_TZ_TCODE,         "aTZ-Name" },
        { TAG_NETINFO_PARENT,   "iNI" },
        { TAG_NETINFO_PARENT_TAG, "aNITAG" },
        { TAG_URL,              "aURL" },
        { TAG_MUDURL,           "aMUD-URL" },
+       { TAG_IPV6_ONLY_PREFERRED, "$IPv6-Only-Preferred" },
        { 0, NULL }
 };
 
@@ -640,7 +652,7 @@ rfc1048_print(netdissect_options *ndo,
 
                if (tag == TAG_PARM_REQUEST) {
                        idx = 0;
-                       while (len > 0) {
+                       while (len != 0) {
                                uint8_t innertag = GET_U_1(bp);
                                bp++;
                                len--;
@@ -671,10 +683,7 @@ rfc1048_print(netdissect_options *ndo,
                case 'a':
                        /* ASCII strings */
                        ND_PRINT("\"");
-                       if (nd_printn(ndo, bp, len, ndo->ndo_snapend)) {
-                               ND_PRINT("\"");
-                               goto trunc;
-                       }
+                       nd_printjn(ndo, bp, len);
                        ND_PRINT("\"");
                        bp += len;
                        len = 0;
@@ -701,6 +710,14 @@ rfc1048_print(netdissect_options *ndo,
 
                case 'p':
                        /* IP address pairs */
+                       /* this option should be N x 8 bytes long */
+                       if (len < 8 || len % 8 != 0) {
+                               ND_PRINT("%s[length != N x 8 bytes]",
+                                        len == 0 ? " " : "");
+                               bp += len;
+                               len = 0;
+                               break;
+                       }
                        while (len >= 2*4) {
                                if (!first)
                                        ND_PRINT(",");
@@ -715,7 +732,7 @@ rfc1048_print(netdissect_options *ndo,
                        break;
 
                case 's':
-                       /* shorts */
+                       /* unsigned shorts */
                        while (len >= 2) {
                                if (!first)
                                        ND_PRINT(",");
@@ -728,33 +745,39 @@ rfc1048_print(netdissect_options *ndo,
 
                case 'B':
                        /* boolean */
-                       while (len > 0) {
-                               uint8_t bool_value;
-                               if (!first)
-                                       ND_PRINT(",");
-                               bool_value = GET_U_1(bp);
-                               switch (bool_value) {
-                               case 0:
-                                       ND_PRINT("N");
-                                       break;
-                               case 1:
-                                       ND_PRINT("Y");
-                                       break;
-                               default:
-                                       ND_PRINT("%u?", bool_value);
-                                       break;
-                               }
-                               ++bp;
-                               --len;
-                               first = 0;
+                   {
+                       /* this option should be 1 byte long */
+                       if (len != 1) {
+                               ND_PRINT("[length != 1 byte]");
+                               nd_print_invalid(ndo);
+                               bp += len;
+                               len = 0;
+                               break;
                        }
+
+                       uint8_t bool_value;
+                       bool_value = GET_U_1(bp);
+                       switch (bool_value) {
+                       case 0:
+                               ND_PRINT("N");
+                               break;
+                       case 1:
+                               ND_PRINT("Y");
+                               break;
+                       default:
+                               ND_PRINT("%u?", bool_value);
+                               break;
+                       }
+                       ++bp;
+                       --len;
                        break;
+                   }
 
                case 'b':
                case 'x':
                default:
                        /* Bytes */
-                       while (len > 0) {
+                       while (len != 0) {
                                uint8_t byte_value;
                                if (!first)
                                        ND_PRINT(c == 'x' ? ":" : ".");
@@ -776,7 +799,8 @@ rfc1048_print(netdissect_options *ndo,
                        case TAG_NETBIOS_NODE:
                                /* this option should be at least 1 byte long */
                                if (len < 1) {
-                                       ND_PRINT("[ERROR: length < 1 bytes]");
+                                       ND_PRINT("[length < 1 byte]");
+                                       nd_print_invalid(ndo);
                                        break;
                                }
                                tag = GET_U_1(bp);
@@ -788,7 +812,8 @@ rfc1048_print(netdissect_options *ndo,
                        case TAG_OPT_OVERLOAD:
                                /* this option should be at least 1 byte long */
                                if (len < 1) {
-                                       ND_PRINT("[ERROR: length < 1 bytes]");
+                                       ND_PRINT("[length < 1 byte]");
+                                       nd_print_invalid(ndo);
                                        break;
                                }
                                tag = GET_U_1(bp);
@@ -800,28 +825,27 @@ rfc1048_print(netdissect_options *ndo,
                        case TAG_CLIENT_FQDN:
                                /* this option should be at least 3 bytes long */
                                if (len < 3) {
-                                       ND_PRINT("[ERROR: length < 3 bytes]");
+                                       ND_PRINT("[length < 3 bytes]");
+                                       nd_print_invalid(ndo);
                                        bp += len;
                                        len = 0;
                                        break;
                                }
                                if (GET_U_1(bp) & 0xf0) {
-                                       ND_PRINT("[ERROR: MBZ nibble 0x%x != 0] ",
+                                       ND_PRINT("[MBZ nibble 0x%x != 0] ",
                                                 (GET_U_1(bp) & 0xf0) >> 4);
+                                       nd_print_invalid(ndo);
                                }
                                if (GET_U_1(bp) & 0x0f)
                                        ND_PRINT("[%s] ",
-                                                client_fqdn_flags(GET_U_1(bp)));
+                                                bittok2str_nosep(fqdn_flags_bm, "", (GET_U_1(bp))));
                                bp++;
                                if (GET_U_1(bp) || GET_U_1(bp + 1))
                                        ND_PRINT("%u/%u ", GET_U_1(bp),
                                                 GET_U_1(bp + 1));
                                bp += 2;
                                ND_PRINT("\"");
-                               if (nd_printn(ndo, bp, len - 3, ndo->ndo_snapend)) {
-                                       ND_PRINT("\"");
-                                       goto trunc;
-                               }
+                               nd_printjn(ndo, bp, len - 3);
                                ND_PRINT("\"");
                                bp += len - 3;
                                len = 0;
@@ -833,7 +857,8 @@ rfc1048_print(netdissect_options *ndo,
 
                                /* this option should be at least 1 byte long */
                                if (len < 1) {
-                                       ND_PRINT("[ERROR: length < 1 bytes]");
+                                       ND_PRINT("[length < 1 byte]");
+                                       nd_print_invalid(ndo);
                                        break;
                                }
                                type = GET_U_1(bp);
@@ -841,17 +866,14 @@ rfc1048_print(netdissect_options *ndo,
                                len--;
                                if (type == 0) {
                                        ND_PRINT("\"");
-                                       if (nd_printn(ndo, bp, len, ndo->ndo_snapend)) {
-                                               ND_PRINT("\"");
-                                               goto trunc;
-                                       }
+                                       nd_printjn(ndo, bp, len);
                                        ND_PRINT("\"");
                                        bp += len;
                                        len = 0;
                                        break;
                                } else {
                                        ND_PRINT("%s ", tok2str(arp2str, "hardware-type %u,", type));
-                                       while (len > 0) {
+                                       while (len != 0) {
                                                if (!first)
                                                        ND_PRINT(":");
                                                ND_PRINT("%02x", GET_U_1(bp));
@@ -887,8 +909,7 @@ rfc1048_print(netdissect_options *ndo,
                                        case AGENT_SUBOPTION_CIRCUIT_ID: /* fall through */
                                        case AGENT_SUBOPTION_REMOTE_ID:
                                        case AGENT_SUBOPTION_SUBSCRIBER_ID:
-                                               if (nd_printn(ndo, bp, suboptlen, ndo->ndo_snapend))
-                                                       goto trunc;
+                                               nd_printjn(ndo, bp, suboptlen);
                                                break;
 
                                        default:
@@ -907,12 +928,13 @@ rfc1048_print(netdissect_options *ndo,
 
                                /* this option should be at least 5 bytes long */
                                if (len < 5) {
-                                       ND_PRINT("[ERROR: length < 5 bytes]");
+                                       ND_PRINT("[length < 5 bytes]");
+                                       nd_print_invalid(ndo);
                                        bp += len;
                                        len = 0;
                                        break;
                                }
-                               while (len > 0) {
+                               while (len != 0) {
                                        if (!first)
                                                ND_PRINT(",");
                                        mask_width = GET_U_1(bp);
@@ -920,7 +942,8 @@ rfc1048_print(netdissect_options *ndo,
                                        len--;
                                        /* mask_width <= 32 */
                                        if (mask_width > 32) {
-                                               ND_PRINT("[ERROR: Mask width (%u) > 32]", mask_width);
+                                               ND_PRINT("[Mask width (%u) > 32]", mask_width);
+                                               nd_print_invalid(ndo);
                                                bp += len;
                                                len = 0;
                                                break;
@@ -928,7 +951,8 @@ rfc1048_print(netdissect_options *ndo,
                                        significant_octets = (mask_width + 7) / 8;
                                        /* significant octets + router(4) */
                                        if (len < significant_octets + 4) {
-                                               ND_PRINT("[ERROR: Remaining length (%u) < %u bytes]", len, significant_octets + 4);
+                                               ND_PRINT("[Remaining length (%u) < %u bytes]", len, significant_octets + 4);
+                                               nd_print_invalid(ndo);
                                                bp += len;
                                                len = 0;
                                                break;
@@ -962,34 +986,35 @@ rfc1048_print(netdissect_options *ndo,
 
                                first = 1;
                                if (len < 2) {
-                                       ND_PRINT("[ERROR: length < 2 bytes]");
+                                       ND_PRINT("[length < 2 bytes]");
+                                       nd_print_invalid(ndo);
                                        bp += len;
                                        len = 0;
                                        break;
                                }
-                               while (len > 0) {
+                               while (len != 0) {
                                        suboptlen = GET_U_1(bp);
                                        bp++;
                                        len--;
                                        ND_PRINT("\n\t      ");
                                        ND_PRINT("instance#%u: ", suboptnumber);
                                        if (suboptlen == 0) {
-                                               ND_PRINT("[ERROR: suboption length must be non-zero]");
+                                               ND_PRINT("[suboption length == 0]");
+                                               nd_print_invalid(ndo);
                                                bp += len;
                                                len = 0;
                                                break;
                                        }
                                        if (len < suboptlen) {
-                                               ND_PRINT("[ERROR: invalid option]");
+                                               ND_PRINT("[length %u < suboption length %u",
+                                                        len, suboptlen);
+                                               nd_print_invalid(ndo);
                                                bp += len;
                                                len = 0;
                                                break;
                                        }
                                        ND_PRINT("\"");
-                                       if (nd_printn(ndo, bp, suboptlen, ndo->ndo_snapend)) {
-                                               ND_PRINT("\"");
-                                               goto trunc;
-                                       }
+                                       nd_printjn(ndo, bp, suboptlen);
                                        ND_PRINT("\"");
                                        ND_PRINT(", length %u", suboptlen);
                                        suboptnumber++;
@@ -999,6 +1024,55 @@ rfc1048_print(netdissect_options *ndo,
                                break;
                            }
 
+
+                       case TAG_SZTP_REDIRECT:
+                               /* as per https://datatracker.ietf.org/doc/html/rfc8572#section-8.3
+                                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+                                |        uri-length             |          URI                  |
+                                +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-...-+-+-+-+-+-+-+
+
+                                * uri-length: 2 octets long; specifies the length of the URI data.
+                                * URI: URI of the SZTP bootstrap server.
+                                */
+                               while (len >= 2) {
+                                       uint16_t suboptlen2;
+
+                                       suboptlen2 = GET_BE_U_2(bp);
+                                       bp += 2;
+                                       len -= 2;
+                                       ND_PRINT("\n\t      ");
+                                       ND_PRINT("length %u: ", suboptlen2);
+                                       if (len < suboptlen2) {
+                                               ND_PRINT("length goes past end of option");
+                                               bp += len;
+                                               len = 0;
+                                               break;
+                                       }
+                                       ND_PRINT("\"");
+                                       nd_printjn(ndo, bp, suboptlen2);
+                                       ND_PRINT("\"");
+                                       len -= suboptlen2;
+                                       bp += suboptlen2;
+                               }
+                               if (len != 0) {
+                                       ND_PRINT("[length < 2 bytes]");
+                                       nd_print_invalid(ndo);
+                               }
+                               break;
+
+                       case TAG_IPV6_ONLY_PREFERRED:
+                               /* this option should be 4 bytes long */
+                               if (len != 4) {
+                                       ND_PRINT("[length != 4 bytes]");
+                                       nd_print_invalid(ndo);
+                                       bp += len;
+                                       len = 0;
+                               }
+                               ND_PRINT("%u", GET_BE_U_4(bp));
+                               bp += 4;
+                               len -= 4;
+                               break;
+
                        default:
                                ND_PRINT("[unknown special tag %u, size %u]",
                                          tag, len);
@@ -1014,9 +1088,6 @@ rfc1048_print(netdissect_options *ndo,
                        bp += len;
                }
        }
-       return;
-trunc:
-       nd_print_trunc(ndo);
 }
 
 #define PRINTCMUADDR(m, s) { ND_TCHECK_4(cmu->m); \
@@ -1046,29 +1117,6 @@ cmu_print(netdissect_options *ndo,
        PRINTCMUADDR(v_ins2, "IEN2");
        PRINTCMUADDR(v_ts1, "TS1");
        PRINTCMUADDR(v_ts2, "TS2");
-       return;
-
-trunc:
-       nd_print_trunc(ndo);
 }
 
 #undef PRINTCMUADDR
-
-static char *
-client_fqdn_flags(u_int flags)
-{
-       static char buf[8+1];
-       int i = 0;
-
-       if (flags & CLIENT_FQDN_FLAGS_S)
-               buf[i++] = 'S';
-       if (flags & CLIENT_FQDN_FLAGS_O)
-               buf[i++] = 'O';
-       if (flags & CLIENT_FQDN_FLAGS_E)
-               buf[i++] = 'E';
-       if (flags & CLIENT_FQDN_FLAGS_N)
-               buf[i++] = 'N';
-       buf[i] = '\0';
-
-       return buf;
-}