1 /* 2 * wks.c -- Well-Known Services (WKS) RDATA parser 3 * 4 * Copyright (c) 2023, NLnet Labs. All rights reserved. 5 * 6 * SPDX-License-Identifier: BSD-3-Clause 7 * 8 */ 9 #ifndef WKS_H 10 #define WKS_H 11 12 // RFC1035 section 3.4.2: 13 // The purpose of WKS RRs is to provide availability information for servers 14 // for TCP and UDP. 15 // 16 // NSD and BIND use getprotobyname, which reads /etc/protocols (optimizations 17 // may be in place for TCP and UDP). Note that BIND passes the protocol to 18 // getservbyname for TCP and UDP only, NULL otherwise, which means any 19 // protocol matches. Unfortunately, getprotobyname is NOT thread-safe. 20 // getprotobyname_r exist on most BSDs and Linux, but not Windows. 21 // The list of known protocols and services also differs between operating 22 // systems and no list covers all IANA (links below) registered protocols 23 // and services, which may cause compatibility issues. Furthermore, even 24 // getprotobyname_r and getservbyname_r are marked locale, meaning the locale 25 // object is read without any form of synchronization, which may be an issue 26 // for a library. 27 // 28 // https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml 29 // https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml 30 // 31 // https://www.gnu.org/software/libc/manual/html_node/Protocols-Database.html 32 // https://www.gnu.org/software/libc/manual/html_node/Services-Database.html 33 // https://www.gnu.org/software/libc/manual/html_node/POSIX-Safety-Concepts.html 34 // https://www.gnu.org/software/libc/manual/html_node/Other-Safety-Remarks.html 35 // 36 // WKS RRs are rarely used and a document to deprecate the RRTYPE (among 37 // others) has been drafted (WKS removed from the second draft). 38 // 39 // https://datatracker.ietf.org/doc/html/draft-sury-deprecate-obsolete-resource-records-00 40 // https://mailarchive.ietf.org/arch/msg/dnsop/YCVvXuM8HbJLF2SoXyDqyOGao34/ 41 // 42 // 43 // WKS RRs have been said to be deprecated in an informational document (NOT 44 // a standard), although it wrongly claims WKS RRs are in fact deprecated. 45 // 46 // RFC1912 section 2.6.1 (informational): 47 // WKS records are deprecated in [RFC 1123]. They serve no known useful 48 // function, except internally among LISP machines. Don't use them. 49 // 50 // https://datatracker.ietf.org/doc/html/rfc1912 51 // 52 // RFC1123 section 2.2 (standard): 53 // An application SHOULD NOT rely on the ability to locate a WKS record 54 // containing an accurate listing of all services at a particular host 55 // address, since the WKS RR type is not often used by Internet sites. 56 // To confirm that a service is present, simply attempt to use it. 57 // 58 // https://datatracker.ietf.org/doc/html/rfc1123 59 // 60 // RFC1127 section 2 (informational): 61 // WKS Records Detracted [AS 2.2, 5.2.12, 6.1.3.6] 62 // Recommend against using WKS records from DNS. 63 // 64 // https://datatracker.ietf.org/doc/html/rfc1127 65 // 66 // 67 // Rather than supporting any protocol registered by IANA, support a small 68 // subset of mnemonics (TCP and UDP) as well as numeric values and add 69 // support (or remove it entirely) for additional protocols on demand. 70 71 #if BYTE_ORDER == LITTLE_ENDIAN 72 # define TCP (0x0000000000706374llu) 73 # define UDP (0x0000000000706475llu) 74 #elif BYTE_ORDER == BIG_ENDIAN 75 # define TCP (0x7463700000000000llu) 76 # define UDP (0x7564700000000000llu) 77 #else 78 # error "byte order unknown" 79 #endif 80 81 static really_inline int32_t scan_protocol( 82 const char *name, size_t length, uint8_t *protocol) 83 { 84 static const int8_t zero_masks[48] = { 85 -1, -1, -1, -1, -1, -1, -1, -1, 86 -1, -1, -1, -1, -1, -1, -1, -1, 87 -1, -1, -1, -1, -1, -1, -1, -1, 88 -1, -1, -1, -1, -1, -1, -1, -1, 89 0, 0, 0, 0, 0, 0, 0, 0, 90 0, 0, 0, 0, 0, 0, 0, 0 91 }; 92 93 uint64_t key; 94 uint64_t mask; 95 const int8_t *zero_mask = &zero_masks[32 - (length & 0x1f)]; 96 memcpy(&mask, zero_mask, 8); 97 98 memcpy(&key, name, sizeof(key)); // safe, input is padded 99 key |= (key & 0x4040404040404040) >> 1; // convert to lower case 100 key &= mask; 101 102 if (key == TCP) 103 return (void)(*protocol = 6), 1; 104 else if (key == UDP) 105 return (void)(*protocol = 17), 1; 106 else 107 return scan_int8(name, length, protocol); 108 } 109 110 typedef struct service service_t; 111 struct service { 112 struct { 113 const char name[16]; 114 size_t length; 115 } key; 116 uint16_t port; 117 }; 118 119 #define UNKNOWN_SERVICE() { { "", 0 }, 0 } 120 #define SERVICE(name, port) { { name, sizeof(name) - 1 }, port } 121 122 static const service_t services[64] = { 123 UNKNOWN_SERVICE(), 124 SERVICE("snmptrap", 162), 125 SERVICE("pop3s", 995), 126 SERVICE("pop3", 110), 127 SERVICE("ldaps", 636), 128 SERVICE("domain", 53), 129 SERVICE("nntps", 563), 130 SERVICE("nntp", 119), 131 UNKNOWN_SERVICE(), 132 UNKNOWN_SERVICE(), 133 UNKNOWN_SERVICE(), 134 UNKNOWN_SERVICE(), 135 SERVICE("ftps-data", 989), 136 UNKNOWN_SERVICE(), 137 UNKNOWN_SERVICE(), 138 SERVICE("imaps", 993), 139 SERVICE("imap", 143), 140 SERVICE("time", 37), 141 UNKNOWN_SERVICE(), 142 UNKNOWN_SERVICE(), 143 SERVICE("kerberos", 88), 144 UNKNOWN_SERVICE(), 145 UNKNOWN_SERVICE(), 146 SERVICE("ftp", 21), 147 SERVICE("ntp", 123), 148 SERVICE("whoispp", 63), 149 SERVICE("ssh", 22), 150 UNKNOWN_SERVICE(), 151 SERVICE("nicname", 43), 152 UNKNOWN_SERVICE(), 153 UNKNOWN_SERVICE(), 154 UNKNOWN_SERVICE(), 155 SERVICE("ptp-general", 320), 156 UNKNOWN_SERVICE(), 157 UNKNOWN_SERVICE(), 158 SERVICE("domain-s", 853), 159 SERVICE("ftp-data", 20), 160 SERVICE("ftps", 990), 161 UNKNOWN_SERVICE(), 162 UNKNOWN_SERVICE(), 163 SERVICE("snmp", 161), 164 UNKNOWN_SERVICE(), 165 UNKNOWN_SERVICE(), 166 UNKNOWN_SERVICE(), 167 UNKNOWN_SERVICE(), 168 SERVICE("bgmp", 264), 169 SERVICE("echo", 7), 170 UNKNOWN_SERVICE(), 171 SERVICE("nnsp", 433), 172 SERVICE("submission", 587), 173 // submissions cannot be distinguished from submission by hash value because 174 // the shared prefix is too long. include length to generate a unique key 175 SERVICE("submissions", 465), 176 UNKNOWN_SERVICE(), 177 SERVICE("ptp-event", 319), 178 UNKNOWN_SERVICE(), 179 SERVICE("npp", 92), 180 UNKNOWN_SERVICE(), 181 SERVICE("https", 443), 182 SERVICE("http", 80), 183 UNKNOWN_SERVICE(), 184 SERVICE("telnet", 23), 185 SERVICE("tcpmux", 1), 186 UNKNOWN_SERVICE(), 187 SERVICE("lmtp", 24), 188 SERVICE("smtp", 25) 189 }; 190 191 #undef SERVICE 192 #undef UNKNOWN_SERVICE 193 194 // magic (139898079) generated using wks-hash.c 195 static really_inline uint8_t service_hash(uint64_t input, size_t length) 196 { 197 // le64toh is required for big endian, no-op on little endian 198 input = le64toh(input); 199 uint32_t input32 = (uint32_t)((input >> 32) ^ input); 200 return (((input32 * 139898079llu) >> 32) + length) & 0x3f; 201 } 202 203 nonnull((1,4)) 204 static really_inline int32_t scan_service( 205 const char *data, size_t length, int32_t protocol, uint16_t *port) 206 { 207 uint8_t digit = (uint8_t)*data - '0'; 208 static const int8_t zero_masks[48] = { 209 -1, -1, -1, -1, -1, -1, -1, -1, 210 -1, -1, -1, -1, -1, -1, -1, -1, 211 -1, -1, -1, -1, -1, -1, -1, -1, 212 -1, -1, -1, -1, -1, -1, -1, -1, 213 0, 0, 0, 0, 0, 0, 0, 0, 214 0, 0, 0, 0, 0, 0, 0, 0 215 }; 216 217 (void)protocol; // all supported services map to tcp and udp 218 219 if (digit > 9) { 220 uint64_t input0, input1; 221 static const uint64_t upper_mask = 0xdfdfdfdfdfdfdfdfllu; 222 static const uint64_t letter_mask = 0x4040404040404040llu; 223 memcpy(&input0, data, 8); 224 memcpy(&input1, data+8, 8); 225 // convert to upper case, unconditionally transforms digits (0x30-0x39) 226 // and dash (0x2d), but does not introduce clashes 227 uint64_t key = input0 & upper_mask; 228 // zero out non-relevant bytes 229 uint64_t zero_mask0, zero_mask1; 230 const int8_t *zero_mask = &zero_masks[32 - (length & 0xf)]; 231 memcpy(&zero_mask0, zero_mask, 8); 232 memcpy(&zero_mask1, zero_mask+8, 8); 233 uint8_t index = service_hash(key & zero_mask0, length); 234 assert(index < 64); 235 236 input0 |= (input0 & letter_mask) >> 1; 237 input0 &= zero_mask0; 238 input1 |= (input1 & letter_mask) >> 1; 239 input1 &= zero_mask1; 240 241 uint64_t name0, name1; 242 memcpy(&name0, services[index].key.name, 8); 243 memcpy(&name1, services[index].key.name+8, 8); 244 245 *port = services[index].port; 246 return (input0 == name0) & 247 (input1 == name1) & 248 (services[index].key.length == length); 249 } 250 251 return scan_int16(data, length, port); 252 } 253 254 #endif // WKS_H 255