-/* $OpenBSD: options.c,v 1.96 2017/07/08 00:36:10 krw Exp $ */
+/* $OpenBSD: options.c,v 1.97 2017/07/08 20:38:31 krw Exp $ */
/* DHCP options parsing and reassembly. */
int parse_option_buffer(struct option_data *, unsigned char *, int);
int expand_search_domain_name(unsigned char *, size_t, int *, unsigned char *);
+/*
+ * DHCP Option names, formats and codes, from RFC1533.
+ *
+ * Format codes:
+ *
+ * e - end of data
+ * I - IP address
+ * l - 32-bit signed integer
+ * L - 32-bit unsigned integer
+ * S - 16-bit unsigned integer
+ * B - 8-bit unsigned integer
+ * t - ASCII text
+ * f - flag (true or false)
+ * A - array of whatever precedes (e.g., IA means array of IP addresses)
+ * C - CIDR description
+ */
+
+static const struct {
+ char *name;
+ char *format;
+} dhcp_options[DHO_COUNT] = {
+ /* 0 */ { "pad", "" },
+ /* 1 */ { "subnet-mask", "I" },
+ /* 2 */ { "time-offset", "l" },
+ /* 3 */ { "routers", "IA" },
+ /* 4 */ { "time-servers", "IA" },
+ /* 5 */ { "ien116-name-servers", "IA" },
+ /* 6 */ { "domain-name-servers", "IA" },
+ /* 7 */ { "log-servers", "IA" },
+ /* 8 */ { "cookie-servers", "IA" },
+ /* 9 */ { "lpr-servers", "IA" },
+ /* 10 */ { "impress-servers", "IA" },
+ /* 11 */ { "resource-location-servers", "IA" },
+ /* 12 */ { "host-name", "t" },
+ /* 13 */ { "boot-size", "S" },
+ /* 14 */ { "merit-dump", "t" },
+ /* 15 */ { "domain-name", "t" },
+ /* 16 */ { "swap-server", "I" },
+ /* 17 */ { "root-path", "t" },
+ /* 18 */ { "extensions-path", "t" },
+ /* 19 */ { "ip-forwarding", "f" },
+ /* 20 */ { "non-local-source-routing", "f" },
+ /* 21 */ { "policy-filter", "IIA" },
+ /* 22 */ { "max-dgram-reassembly", "S" },
+ /* 23 */ { "default-ip-ttl", "B" },
+ /* 24 */ { "path-mtu-aging-timeout", "L" },
+ /* 25 */ { "path-mtu-plateau-table", "SA" },
+ /* 26 */ { "interface-mtu", "S" },
+ /* 27 */ { "all-subnets-local", "f" },
+ /* 28 */ { "broadcast-address", "I" },
+ /* 29 */ { "perform-mask-discovery", "f" },
+ /* 30 */ { "mask-supplier", "f" },
+ /* 31 */ { "router-discovery", "f" },
+ /* 32 */ { "router-solicitation-address", "I" },
+ /* 33 */ { "static-routes", "IIA" },
+ /* 34 */ { "trailer-encapsulation", "f" },
+ /* 35 */ { "arp-cache-timeout", "L" },
+ /* 36 */ { "ieee802-3-encapsulation", "f" },
+ /* 37 */ { "default-tcp-ttl", "B" },
+ /* 38 */ { "tcp-keepalive-interval", "L" },
+ /* 39 */ { "tcp-keepalive-garbage", "f" },
+ /* 40 */ { "nis-domain", "t" },
+ /* 41 */ { "nis-servers", "IA" },
+ /* 42 */ { "ntp-servers", "IA" },
+ /* 43 */ { "vendor-encapsulated-options", "X" },
+ /* 44 */ { "netbios-name-servers", "IA" },
+ /* 45 */ { "netbios-dd-server", "IA" },
+ /* 46 */ { "netbios-node-type", "B" },
+ /* 47 */ { "netbios-scope", "t" },
+ /* 48 */ { "font-servers", "IA" },
+ /* 49 */ { "x-display-manager", "IA" },
+ /* 50 */ { "dhcp-requested-address", "I" },
+ /* 51 */ { "dhcp-lease-time", "L" },
+ /* 52 */ { "dhcp-option-overload", "B" },
+ /* 53 */ { "dhcp-message-type", "B" },
+ /* 54 */ { "dhcp-server-identifier", "I" },
+ /* 55 */ { "dhcp-parameter-request-list", "BA" },
+ /* 56 */ { "dhcp-message", "t" },
+ /* 57 */ { "dhcp-max-message-size", "S" },
+ /* 58 */ { "dhcp-renewal-time", "L" },
+ /* 59 */ { "dhcp-rebinding-time", "L" },
+ /* 60 */ { "dhcp-class-identifier", "t" },
+ /* 61 */ { "dhcp-client-identifier", "X" },
+ /* 62 */ { NULL, NULL },
+ /* 63 */ { NULL, NULL },
+ /* 64 */ { "nisplus-domain", "t" },
+ /* 65 */ { "nisplus-servers", "IA" },
+ /* 66 */ { "tftp-server-name", "t" },
+ /* 67 */ { "bootfile-name", "t" },
+ /* 68 */ { "mobile-ip-home-agent", "IA" },
+ /* 69 */ { "smtp-server", "IA" },
+ /* 70 */ { "pop-server", "IA" },
+ /* 71 */ { "nntp-server", "IA" },
+ /* 72 */ { "www-server", "IA" },
+ /* 73 */ { "finger-server", "IA" },
+ /* 74 */ { "irc-server", "IA" },
+ /* 75 */ { "streettalk-server", "IA" },
+ /* 76 */ { "streettalk-directory-assistance-server", "IA" },
+ /* 77 */ { "user-class", "t" },
+ /* 78 */ { NULL, NULL },
+ /* 79 */ { NULL, NULL },
+ /* 80 */ { NULL, NULL },
+ /* 81 */ { NULL, NULL },
+ /* 82 */ { "relay-agent-information", "X" },
+ /* 83 */ { NULL, NULL },
+ /* 84 */ { NULL, NULL },
+ /* 85 */ { "nds-servers", "IA" },
+ /* 86 */ { "nds-tree-name", "X" },
+ /* 87 */ { "nds-context", "X" },
+ /* 88 */ { NULL, NULL },
+ /* 89 */ { NULL, NULL },
+ /* 90 */ { NULL, NULL },
+ /* 91 */ { NULL, NULL },
+ /* 92 */ { NULL, NULL },
+ /* 93 */ { NULL, NULL },
+ /* 94 */ { NULL, NULL },
+ /* 95 */ { NULL, NULL },
+ /* 96 */ { NULL, NULL },
+ /* 97 */ { NULL, NULL },
+ /* 98 */ { NULL, NULL },
+ /* 99 */ { NULL, NULL },
+ /* 100 */ { NULL, NULL },
+ /* 101 */ { NULL, NULL },
+ /* 102 */ { NULL, NULL },
+ /* 103 */ { NULL, NULL },
+ /* 104 */ { NULL, NULL },
+ /* 105 */ { NULL, NULL },
+ /* 106 */ { NULL, NULL },
+ /* 107 */ { NULL, NULL },
+ /* 108 */ { NULL, NULL },
+ /* 109 */ { NULL, NULL },
+ /* 110 */ { NULL, NULL },
+ /* 111 */ { NULL, NULL },
+ /* 112 */ { NULL, NULL },
+ /* 113 */ { NULL, NULL },
+ /* 114 */ { NULL, NULL },
+ /* 115 */ { NULL, NULL },
+ /* 116 */ { NULL, NULL },
+ /* 117 */ { NULL, NULL },
+ /* 118 */ { NULL, NULL },
+ /* 119 */ { "domain-search", "X" },
+ /* 120 */ { NULL, NULL },
+ /* 121 */ { "classless-static-routes", "CIA" },
+ /* 122 */ { NULL, NULL },
+ /* 123 */ { NULL, NULL },
+ /* 124 */ { NULL, NULL },
+ /* 125 */ { NULL, NULL },
+ /* 126 */ { NULL, NULL },
+ /* 127 */ { NULL, NULL },
+ /* 128 */ { NULL, NULL },
+ /* 129 */ { NULL, NULL },
+ /* 130 */ { NULL, NULL },
+ /* 131 */ { NULL, NULL },
+ /* 132 */ { NULL, NULL },
+ /* 133 */ { NULL, NULL },
+ /* 134 */ { NULL, NULL },
+ /* 135 */ { NULL, NULL },
+ /* 136 */ { NULL, NULL },
+ /* 137 */ { NULL, NULL },
+ /* 138 */ { NULL, NULL },
+ /* 139 */ { NULL, NULL },
+ /* 140 */ { NULL, NULL },
+ /* 141 */ { NULL, NULL },
+ /* 142 */ { NULL, NULL },
+ /* 143 */ { NULL, NULL },
+ /* 144 */ { "tftp-config-file", "t" },
+ /* 145 */ { NULL, NULL },
+ /* 146 */ { NULL, NULL },
+ /* 147 */ { NULL, NULL },
+ /* 148 */ { NULL, NULL },
+ /* 149 */ { NULL, NULL },
+ /* 150 */ { "voip-configuration-server", "IA" },
+ /* 151 */ { NULL, NULL },
+ /* 152 */ { NULL, NULL },
+ /* 153 */ { NULL, NULL },
+ /* 154 */ { NULL, NULL },
+ /* 155 */ { NULL, NULL },
+ /* 156 */ { NULL, NULL },
+ /* 157 */ { NULL, NULL },
+ /* 158 */ { NULL, NULL },
+ /* 159 */ { NULL, NULL },
+ /* 160 */ { NULL, NULL },
+ /* 161 */ { NULL, NULL },
+ /* 162 */ { NULL, NULL },
+ /* 163 */ { NULL, NULL },
+ /* 164 */ { NULL, NULL },
+ /* 165 */ { NULL, NULL },
+ /* 166 */ { NULL, NULL },
+ /* 167 */ { NULL, NULL },
+ /* 168 */ { NULL, NULL },
+ /* 169 */ { NULL, NULL },
+ /* 170 */ { NULL, NULL },
+ /* 171 */ { NULL, NULL },
+ /* 172 */ { NULL, NULL },
+ /* 173 */ { NULL, NULL },
+ /* 174 */ { NULL, NULL },
+ /* 175 */ { NULL, NULL },
+ /* 176 */ { NULL, NULL },
+ /* 177 */ { NULL, NULL },
+ /* 178 */ { NULL, NULL },
+ /* 179 */ { NULL, NULL },
+ /* 180 */ { NULL, NULL },
+ /* 181 */ { NULL, NULL },
+ /* 182 */ { NULL, NULL },
+ /* 183 */ { NULL, NULL },
+ /* 184 */ { NULL, NULL },
+ /* 185 */ { NULL, NULL },
+ /* 186 */ { NULL, NULL },
+ /* 187 */ { NULL, NULL },
+ /* 188 */ { NULL, NULL },
+ /* 189 */ { NULL, NULL },
+ /* 190 */ { NULL, NULL },
+ /* 191 */ { NULL, NULL },
+ /* 192 */ { NULL, NULL },
+ /* 193 */ { NULL, NULL },
+ /* 194 */ { NULL, NULL },
+ /* 195 */ { NULL, NULL },
+ /* 196 */ { NULL, NULL },
+ /* 197 */ { NULL, NULL },
+ /* 198 */ { NULL, NULL },
+ /* 199 */ { NULL, NULL },
+ /* 200 */ { NULL, NULL },
+ /* 201 */ { NULL, NULL },
+ /* 202 */ { NULL, NULL },
+ /* 203 */ { NULL, NULL },
+ /* 204 */ { NULL, NULL },
+ /* 205 */ { NULL, NULL },
+ /* 206 */ { NULL, NULL },
+ /* 207 */ { NULL, NULL },
+ /* 208 */ { NULL, NULL },
+ /* 209 */ { NULL, NULL },
+ /* 210 */ { NULL, NULL },
+ /* 211 */ { NULL, NULL },
+ /* 212 */ { NULL, NULL },
+ /* 213 */ { NULL, NULL },
+ /* 214 */ { NULL, NULL },
+ /* 215 */ { NULL, NULL },
+ /* 216 */ { NULL, NULL },
+ /* 217 */ { NULL, NULL },
+ /* 218 */ { NULL, NULL },
+ /* 219 */ { NULL, NULL },
+ /* 220 */ { NULL, NULL },
+ /* 221 */ { NULL, NULL },
+ /* 222 */ { NULL, NULL },
+ /* 223 */ { NULL, NULL },
+ /* 224 */ { NULL, NULL },
+ /* 225 */ { NULL, NULL },
+ /* 226 */ { NULL, NULL },
+ /* 227 */ { NULL, NULL },
+ /* 228 */ { NULL, NULL },
+ /* 229 */ { NULL, NULL },
+ /* 230 */ { NULL, NULL },
+ /* 231 */ { NULL, NULL },
+ /* 232 */ { NULL, NULL },
+ /* 233 */ { NULL, NULL },
+ /* 234 */ { NULL, NULL },
+ /* 235 */ { NULL, NULL },
+ /* 236 */ { NULL, NULL },
+ /* 237 */ { NULL, NULL },
+ /* 238 */ { NULL, NULL },
+ /* 239 */ { NULL, NULL },
+ /* 240 */ { NULL, NULL },
+ /* 241 */ { NULL, NULL },
+ /* 242 */ { NULL, NULL },
+ /* 243 */ { NULL, NULL },
+ /* 244 */ { NULL, NULL },
+ /* 245 */ { NULL, NULL },
+ /* 246 */ { NULL, NULL },
+ /* 247 */ { NULL, NULL },
+ /* 248 */ { NULL, NULL },
+ /* 249 */ { "classless-ms-static-routes", "CIA" },
+ /* 250 */ { NULL, NULL },
+ /* 251 */ { NULL, NULL },
+ /* 252 */ { "autoproxy-script", "t" },
+ /* 253 */ { NULL, NULL },
+ /* 254 */ { NULL, NULL },
+ /* 255 */ { "option-end", "e" },
+};
+
+char *
+code_to_name(int code)
+{
+ static char unknown[11]; /* "option-NNN" */
+ int ret;
+
+ if (code < 0 || code >= DHO_COUNT)
+ return "";
+
+ if (dhcp_options[code].name != NULL)
+ return dhcp_options[code].name;
+
+ ret = snprintf(unknown, sizeof(unknown), "option-%d", code);
+ if (ret == -1 || ret >= (int)sizeof(unknown))
+ return "";
+
+ return unknown;
+}
+
+int
+name_to_code(char *name)
+{
+ char unknown[11]; /* "option-NNN" */
+ int code, ret;
+
+ for (code = 1; code < DHO_END; code++) {
+ if (dhcp_options[code].name == NULL) {
+ ret = snprintf(unknown, sizeof(unknown), "option-%d",
+ code);
+ if (ret == -1 || ret >= (int)sizeof(unknown))
+ return DHO_END;
+ if (strcasecmp(unknown, name) == 0)
+ return code;
+ } else if (strcasecmp(dhcp_options[code].name, name) == 0) {
+ return code;
+ }
+ }
+
+ return DHO_END;
+}
+
+char *
+code_to_format(int code)
+{
+ if (code < 0 || code >= DHO_COUNT)
+ return "";
+
+ if (dhcp_options[code].format == NULL)
+ return "X";
+
+ return dhcp_options[code].format;
+}
+
/*
* Parse options out of the specified buffer, storing addresses of
* option values in options. Return 0 if errors, 1 if not.
parse_option_buffer(struct option_data *options, unsigned char *buffer,
int length)
{
- unsigned char *s, *t, *end = buffer + length;
- int len, code;
+ unsigned char *s, *t, *end = buffer + length;
+ char *name, *fmt;
+ int len, code;
for (s = buffer; *s != DHO_END && s < end; ) {
code = s[0];
continue;
}
+ name = code_to_name(code);
+ fmt = code_to_format(code);
+
/*
* All options other than DHO_PAD and DHO_END have a one-byte
* length field. It could be 0! Make sure that the length byte
; /* option data is all there. */
} else {
log_warnx("option %s (%d) larger than buffer.",
- dhcp_options[code].name, len);
+ name, len);
return (0);
}
} else {
- log_warnx("option %s has no length field.",
- dhcp_options[code].name);
+ log_warnx("option %s has no length field.", name);
return (0);
}
* a trailing NULL; however, the receiver of such options
* MUST be prepared to delete trailing nulls if they exist."
*/
- if (dhcp_options[code].format[0] == 't') {
+ if (fmt[0] == 't') {
while (len > 0 && s[len + 1] == '\0')
len--;
}
if (!options[code].data) {
if (!(t = calloc(1, len + 1)))
fatalx("Can't allocate storage for option %s.",
- dhcp_options[code].name);
+ name);
/*
* Copy and NUL-terminate the option (in case
* it's an ASCII string).
t = calloc(1, len + options[code].len + 1);
if (!t)
fatalx("Can't expand storage for option %s.",
- dhcp_options[code].name);
+ name);
memcpy(t, options[code].data, options[code].len);
memcpy(t + options[code].len, &s[2], len);
options[code].len += len;
struct in_addr foo;
unsigned char *data = option->data;
unsigned char *dp = data;
- char *op = optbuf, *buf;
+ char *op = optbuf, *buf, *name, *fmt;
int hunksize = 0, numhunk = -1, numelem = 0;
int i, j, k, opleft = sizeof(optbuf);
int len = option->len;
break;
}
+ name = code_to_name(code);
+ fmt = code_to_format(code);
+
/* Figure out the size of the data. */
- for (i = 0; dhcp_options[code].format[i]; i++) {
+ for (i = 0; fmt[i]; i++) {
if (!numhunk) {
log_warnx("%s: Excess information in format string: "
- "%s", dhcp_options[code].name,
- &(dhcp_options[code].format[i]));
+ "%s", name, &fmt[i]);
goto done;
}
numelem++;
- fmtbuf[i] = dhcp_options[code].format[i];
- switch (dhcp_options[code].format[i]) {
+ fmtbuf[i] = fmt[i];
+ switch (fmt[i]) {
case 'A':
--numelem;
fmtbuf[i] = 0;
numhunk = 0;
if (hunksize == 0) {
log_warnx("%s: no size indicator before A"
- " in format string: %s",
- dhcp_options[code].name,
- dhcp_options[code].format);
+ " in format string: %s", name, fmt);
goto done;
}
break;
case 'e':
break;
default:
- log_warnx("%s: garbage in format string: %s",
- dhcp_options[code].name,
- &(dhcp_options[code].format[i]));
+ log_warnx("%s: garbage in format string: %s", name,
+ &fmt[i]);
goto done;
}
}
/* Check for too few bytes. */
if (hunksize > len) {
- log_warnx("%s: expecting at least %d bytes; got %d",
- dhcp_options[code].name, hunksize, len);
+ log_warnx("%s: expecting at least %d bytes; got %d", name,
+ hunksize, len);
goto done;
}
/* Check for too many bytes. */
if (numhunk == -1 && hunksize < len) {
- log_warnx("%s: expecting only %d bytes: got %d",
- dhcp_options[code].name, hunksize, len);
+ log_warnx("%s: expecting only %d bytes: got %d", name,
+ hunksize, len);
goto done;
}
numhunk = len / hunksize;
/* See if we got an exact number of hunks. */
if (numhunk > 0 && numhunk * hunksize != len) {
- log_warnx("%s: expecting %d bytes: got %d",
- dhcp_options[code].name, numhunk * hunksize, len);
+ log_warnx("%s: expecting %d bytes: got %d", name,
+ numhunk * hunksize, len);
goto done;
}