Simon Kelley | 9e4abcb | 2004-01-22 19:47:41 +0000 | [diff] [blame] | 1 | /* dnsmasq is Copyright (c) 2000-2003 Simon Kelley |
| 2 | |
| 3 | This program is free software; you can redistribute it and/or modify |
| 4 | it under the terms of the GNU General Public License as published by |
| 5 | the Free Software Foundation; version 2 dated June, 1991. |
| 6 | |
| 7 | This program is distributed in the hope that it will be useful, |
| 8 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 9 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 10 | GNU General Public License for more details. |
| 11 | */ |
| 12 | |
| 13 | /* Author's email: simon@thekelleys.org.uk */ |
| 14 | |
| 15 | #include "dnsmasq.h" |
| 16 | |
| 17 | #define BOOTREQUEST 1 |
| 18 | #define BOOTREPLY 2 |
| 19 | #define DHCP_COOKIE 0x63825363 |
| 20 | |
| 21 | #define OPTION_PAD 0 |
| 22 | #define OPTION_NETMASK 1 |
| 23 | #define OPTION_ROUTER 3 |
| 24 | #define OPTION_DNSSERVER 6 |
| 25 | #define OPTION_HOSTNAME 12 |
| 26 | #define OPTION_DOMAINNAME 15 |
| 27 | #define OPTION_BROADCAST 28 |
| 28 | #define OPTION_CLIENT_ID 61 |
| 29 | #define OPTION_REQUESTED_IP 50 |
| 30 | #define OPTION_LEASE_TIME 51 |
| 31 | #define OPTION_OVERLOAD 52 |
| 32 | #define OPTION_MESSAGE_TYPE 53 |
| 33 | #define OPTION_SERVER_IDENTIFIER 54 |
| 34 | #define OPTION_REQUESTED_OPTIONS 55 |
| 35 | #define OPTION_MAXMESSAGE 57 |
| 36 | #define OPTION_END 255 |
| 37 | |
| 38 | #define DHCPDISCOVER 1 |
| 39 | #define DHCPOFFER 2 |
| 40 | #define DHCPREQUEST 3 |
| 41 | #define DHCPDECLINE 4 |
| 42 | #define DHCPACK 5 |
| 43 | #define DHCPNAK 6 |
| 44 | #define DHCPRELEASE 7 |
| 45 | #define DHCPINFORM 8 |
| 46 | |
| 47 | static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val); |
| 48 | static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname); |
| 49 | static int option_len(unsigned char *opt); |
| 50 | static void *option_ptr(unsigned char *opt); |
| 51 | static struct in_addr option_addr(unsigned char *opt); |
| 52 | static unsigned int option_uint(unsigned char *opt); |
| 53 | static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface); |
| 54 | static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type); |
| 55 | static unsigned char *do_req_options(struct dhcp_context *context, |
| 56 | unsigned char *p, unsigned char *end, |
| 57 | unsigned char *req_options, |
| 58 | struct dhcp_opt *config_opts, |
| 59 | char *domainname, char *hostname); |
| 60 | |
| 61 | |
| 62 | int dhcp_reply(struct dhcp_context *context, struct dhcp_packet *mess, |
| 63 | unsigned int sz, time_t now, char *namebuff, |
| 64 | struct dhcp_opt *dhcp_opts, struct dhcp_config *dhcp_configs, |
| 65 | char *domain_suffix, char *dhcp_file, char *dhcp_sname, |
| 66 | struct in_addr dhcp_next_server) |
| 67 | { |
| 68 | unsigned char *opt, *clid; |
| 69 | struct dhcp_lease *lease; |
| 70 | int clid_len; |
| 71 | unsigned char *p = mess->options; |
| 72 | char *hostname = NULL; |
| 73 | char *req_options = NULL; |
| 74 | unsigned int renewal_time, expires_time, def_time; |
| 75 | struct dhcp_config *config; |
| 76 | |
| 77 | if (mess->op != BOOTREQUEST || |
| 78 | mess->htype != ARPHRD_ETHER || |
| 79 | mess->hlen != ETHER_ADDR_LEN || |
| 80 | mess->cookie != htonl(DHCP_COOKIE)) |
| 81 | return 0; |
| 82 | |
| 83 | mess->op = BOOTREPLY; |
| 84 | |
| 85 | /* If there is no client identifier option, use the hardware address */ |
| 86 | if ((opt = option_find(mess, sz, OPTION_CLIENT_ID))) |
| 87 | { |
| 88 | clid = option_ptr(opt); |
| 89 | clid_len = option_len(opt); |
| 90 | } |
| 91 | else |
| 92 | { |
| 93 | clid = mess->chaddr; |
| 94 | clid_len = 0; |
| 95 | } |
| 96 | |
| 97 | /* do we have a lease in store? */ |
| 98 | lease = lease_find_by_client(clid, clid_len); |
| 99 | |
| 100 | if ((opt = option_find(mess, sz, OPTION_REQUESTED_OPTIONS))) |
| 101 | { |
| 102 | int len = option_len(opt); |
| 103 | req_options = namebuff; |
| 104 | memcpy(req_options, option_ptr(opt), len); |
| 105 | req_options[len] = OPTION_END; |
| 106 | } |
| 107 | |
| 108 | if ((config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, NULL)) && |
| 109 | config->hostname) |
| 110 | hostname = config->hostname; |
| 111 | else if ((opt = option_find(mess, sz, OPTION_HOSTNAME))) |
| 112 | { |
| 113 | int len = option_len(opt); |
| 114 | /* namebuff is 1K long, use half for requested options and half for hostname */ |
| 115 | /* len < 256 by definition */ |
| 116 | hostname = namebuff + 500; |
| 117 | memcpy(hostname, option_ptr(opt), len); |
| 118 | /* May not be zero terminated */ |
| 119 | hostname[len] = 0; |
| 120 | /* ensure there are no strange chars in there */ |
| 121 | if (!canonicalise(hostname)) |
| 122 | hostname = NULL; |
| 123 | } |
| 124 | |
| 125 | if (hostname) |
| 126 | { |
| 127 | char *dot = strchr(hostname, '.'); |
| 128 | if (dot) |
| 129 | { |
| 130 | if (!domain_suffix || !hostname_isequal(dot+1, domain_suffix)) |
| 131 | { |
| 132 | syslog(LOG_WARNING, "Ignoring DHCP host name %s because it has an illegal domain part", hostname); |
| 133 | hostname = NULL; |
| 134 | } |
| 135 | else |
| 136 | *dot = 0; /* truncate */ |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | /* search again now we have a hostname */ |
| 141 | config = find_config(dhcp_configs, context, clid, clid_len, mess->chaddr, hostname); |
Simon Kelley | 1ab84e2 | 2004-01-29 16:48:35 +0000 | [diff] [blame] | 142 | def_time = config && config->lease_time ? config->lease_time : context->lease_time; |
Simon Kelley | 9e4abcb | 2004-01-22 19:47:41 +0000 | [diff] [blame] | 143 | |
| 144 | if ((opt = option_find(mess, sz, OPTION_LEASE_TIME))) |
| 145 | { |
| 146 | unsigned int req_time = option_uint(opt); |
| 147 | |
| 148 | if (def_time == 0xffffffff || |
| 149 | (req_time != 0xffffffff && req_time < def_time)) |
| 150 | expires_time = renewal_time = req_time; |
| 151 | else |
| 152 | expires_time = renewal_time = def_time; |
| 153 | } |
| 154 | else |
| 155 | { |
| 156 | renewal_time = def_time; |
| 157 | if (lease) |
| 158 | expires_time = (unsigned int)difftime(lease->expires, now); |
| 159 | else |
| 160 | expires_time = def_time; |
| 161 | } |
| 162 | |
| 163 | if (!(opt = option_find(mess, sz, OPTION_MESSAGE_TYPE))) |
| 164 | return 0; |
| 165 | |
| 166 | switch (opt[2]) |
| 167 | { |
| 168 | case DHCPRELEASE: |
| 169 | if (lease) |
| 170 | { |
| 171 | log_packet("RELEASE", &lease->addr, mess->chaddr, context->iface); |
| 172 | lease_prune(lease, now); |
| 173 | } |
| 174 | return 0; |
| 175 | |
| 176 | case DHCPDISCOVER: |
| 177 | |
| 178 | if ((opt = option_find(mess, sz, OPTION_REQUESTED_IP))) |
| 179 | mess->yiaddr = option_addr(opt); |
| 180 | |
| 181 | log_packet("DISCOVER", opt ? &mess->yiaddr : NULL, mess->chaddr, context->iface); |
| 182 | |
| 183 | if (lease) |
| 184 | mess->yiaddr = lease->addr; |
| 185 | else if (config && config->addr.s_addr && !lease_find_by_addr(config->addr)) |
| 186 | mess->yiaddr = config->addr; |
| 187 | else if ((!opt || !address_available(context, mess->yiaddr)) && |
| 188 | !address_allocate(context, dhcp_configs, &mess->yiaddr)) |
| 189 | { |
| 190 | syslog(LOG_WARNING, "address pool exhausted"); |
| 191 | return 0; |
| 192 | } |
| 193 | |
| 194 | bootp_option_put(mess, dhcp_file, dhcp_sname); |
| 195 | mess->siaddr = dhcp_next_server; |
| 196 | p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPOFFER); |
| 197 | p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr)); |
| 198 | p = option_put(p, &mess->options[308], OPTION_LEASE_TIME, 4, expires_time); |
| 199 | p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, NULL); |
| 200 | p = option_put(p, &mess->options[308], OPTION_END, 0, 0); |
| 201 | |
| 202 | log_packet("OFFER" , &mess->yiaddr, mess->chaddr, context->iface); |
| 203 | return p - (unsigned char *)mess; |
| 204 | |
| 205 | |
| 206 | case DHCPREQUEST: |
| 207 | if (mess->ciaddr.s_addr) |
| 208 | { |
| 209 | /* RENEWING or REBINDING */ |
| 210 | /* Must exist a lease for this address */ |
| 211 | log_packet("REQUEST", &mess->ciaddr, mess->chaddr, context->iface); |
| 212 | |
| 213 | if (!lease || mess->ciaddr.s_addr != lease->addr.s_addr) |
| 214 | { |
| 215 | log_packet("NAK", &mess->ciaddr, mess->chaddr, context->iface); |
| 216 | |
| 217 | mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0; |
| 218 | bootp_option_put(mess, NULL, NULL); |
| 219 | p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPNAK); |
| 220 | p = option_put(p, &mess->options[308], OPTION_END, 0, 0); |
| 221 | |
| 222 | return (unsigned char *)mess - p; /* -ve to force bcast */ |
| 223 | } |
| 224 | |
| 225 | mess->yiaddr = mess->ciaddr; |
| 226 | } |
| 227 | else |
| 228 | { |
| 229 | /* SELECTING or INIT_REBOOT */ |
| 230 | if ((opt = option_find(mess, sz, OPTION_SERVER_IDENTIFIER)) && |
| 231 | (context->serv_addr.s_addr != option_addr(opt).s_addr)) |
| 232 | return 0; |
| 233 | |
| 234 | if (!(opt = option_find(mess, sz, OPTION_REQUESTED_IP))) |
| 235 | return 0; |
| 236 | |
| 237 | mess->yiaddr = option_addr(opt); |
| 238 | log_packet("REQUEST", &mess->yiaddr, mess->chaddr, context->iface); |
| 239 | |
| 240 | /* If a lease exists for this host and another address, squash it. */ |
| 241 | if (lease && lease->addr.s_addr != mess->yiaddr.s_addr) |
| 242 | { |
| 243 | lease_prune(lease, now); |
| 244 | lease = NULL; |
| 245 | } |
| 246 | |
| 247 | /* accept addresses in the dynamic range or ones allocated statically to |
| 248 | particular hosts or an address which the host already has. */ |
| 249 | if (!lease && |
| 250 | !address_available(context, mess->yiaddr) && |
| 251 | (!config || config->addr.s_addr == 0 || config->addr.s_addr != mess->yiaddr.s_addr)) |
| 252 | { |
| 253 | log_packet("NAK", &mess->yiaddr, mess->chaddr, context->iface); |
| 254 | |
| 255 | mess->siaddr.s_addr = mess->yiaddr.s_addr = mess->ciaddr.s_addr = 0; |
| 256 | bootp_option_put(mess, NULL, NULL); |
| 257 | p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPNAK); |
| 258 | p = option_put(p, &mess->options[308], OPTION_END, 0, 0); |
| 259 | |
| 260 | return (unsigned char *)mess - p; /* -ve to force bcast */ |
| 261 | } |
| 262 | |
| 263 | if (!lease && |
| 264 | !(lease = lease_allocate(clid, clid_len, mess->yiaddr))) |
| 265 | return 0; |
| 266 | } |
| 267 | |
| 268 | lease_set_hwaddr(lease, mess->chaddr); |
| 269 | lease_set_hostname(lease, hostname, domain_suffix); |
| 270 | lease_set_expires(lease, renewal_time == 0xffffffff ? 0 : now + (time_t)renewal_time); |
| 271 | |
| 272 | bootp_option_put(mess, dhcp_file, dhcp_sname); |
| 273 | mess->siaddr = dhcp_next_server; |
| 274 | p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPACK); |
| 275 | p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr)); |
| 276 | p = option_put(p, &mess->options[308], OPTION_LEASE_TIME, 4, renewal_time); |
| 277 | p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, hostname); |
| 278 | p = option_put(p, &mess->options[308], OPTION_END, 0, 0); |
| 279 | |
| 280 | log_packet("ACK", &mess->yiaddr, mess->chaddr, context->iface); |
| 281 | return p - (unsigned char *)mess; |
| 282 | |
| 283 | case DHCPINFORM: |
| 284 | log_packet("INFORM", &mess->ciaddr, mess->chaddr, context->iface); |
| 285 | |
| 286 | p = option_put(p, &mess->options[308], OPTION_MESSAGE_TYPE, 1, DHCPACK); |
| 287 | p = option_put(p, &mess->options[308], OPTION_SERVER_IDENTIFIER, INADDRSZ, ntohl(context->serv_addr.s_addr)); |
| 288 | p = do_req_options(context, p, &mess->options[308], req_options, dhcp_opts, domain_suffix, hostname); |
| 289 | p = option_put(p, &mess->options[308], OPTION_END, 0, 0); |
| 290 | |
| 291 | log_packet("ACK", &mess->ciaddr, mess->chaddr, context->iface); |
| 292 | return p - (unsigned char *)mess; |
| 293 | } |
| 294 | |
| 295 | return 0; |
| 296 | } |
| 297 | |
| 298 | static void log_packet(char *type, struct in_addr *addr, unsigned char *hwaddr, char *interface) |
| 299 | { |
| 300 | syslog(LOG_INFO, "DHCP%s(%s)%s%s hwaddr=%.2x:%.2x:%.2x:%.2x:%.2x:%.2x", |
| 301 | type, |
| 302 | interface, |
| 303 | addr ? " " : "", |
| 304 | addr ? inet_ntoa(*addr) : "", |
| 305 | hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]); |
| 306 | } |
| 307 | |
| 308 | static int option_len(unsigned char *opt) |
| 309 | { |
| 310 | return opt[1]; |
| 311 | } |
| 312 | |
| 313 | static void *option_ptr(unsigned char *opt) |
| 314 | { |
| 315 | return &opt[2]; |
| 316 | } |
| 317 | |
| 318 | static struct in_addr option_addr(unsigned char *opt) |
| 319 | { |
| 320 | /* this worries about unaligned data in the option. */ |
| 321 | /* struct in_addr is network byte order */ |
| 322 | struct in_addr ret; |
| 323 | |
| 324 | memcpy(&ret, option_ptr(opt), INADDRSZ); |
| 325 | |
| 326 | return ret; |
| 327 | } |
| 328 | |
| 329 | static unsigned int option_uint(unsigned char *opt) |
| 330 | { |
| 331 | /* this worries about unaligned data and byte order */ |
| 332 | unsigned int ret; |
| 333 | |
| 334 | memcpy(&ret, option_ptr(opt), sizeof(unsigned int)); |
| 335 | |
| 336 | return ntohl(ret); |
| 337 | } |
| 338 | |
| 339 | static void bootp_option_put(struct dhcp_packet *mess, char *filename, char *sname) |
| 340 | { |
| 341 | memset(mess->sname, 0, sizeof(mess->sname)); |
| 342 | memset(mess->file, 0, sizeof(mess->file)); |
| 343 | if (sname) |
| 344 | strncpy(mess->sname, sname, sizeof(mess->sname)-1); |
| 345 | if (filename) |
| 346 | strncpy(mess->file, filename, sizeof(mess->file)-1); |
| 347 | } |
| 348 | |
| 349 | static unsigned char *option_put(unsigned char *p, unsigned char *end, int opt, int len, unsigned int val) |
| 350 | { |
| 351 | int i; |
| 352 | |
| 353 | if (p + len + 2 < end) |
| 354 | { |
| 355 | *(p++) = opt; |
| 356 | *(p++) = len; |
| 357 | |
| 358 | for (i = 0; i < len; i++) |
| 359 | *(p++) = val >> (8 * (len - (i + 1))); |
| 360 | } |
| 361 | return p; |
| 362 | } |
| 363 | |
| 364 | static unsigned char *option_find1(unsigned char *p, unsigned char *end, int opt, int *overload) |
| 365 | { |
| 366 | |
| 367 | if (!p) |
| 368 | return NULL; |
| 369 | |
| 370 | while (*p != OPTION_END) |
| 371 | { |
| 372 | if (end && (p >= end)) |
| 373 | return 0; /* malformed packet */ |
| 374 | else if (*p == OPTION_PAD) |
| 375 | p++; |
| 376 | else if (*p == OPTION_OVERLOAD) |
| 377 | { |
| 378 | if (end && (p >= end - 3)) |
| 379 | return 0; /* malformed packet */ |
| 380 | if (overload) |
| 381 | *overload = *(p+2); |
| 382 | p += 3; |
| 383 | } |
| 384 | else |
| 385 | { |
| 386 | int opt_len;; |
| 387 | if (end && (p >= end - 2)) |
| 388 | return 0; /* malformed packet */ |
| 389 | opt_len = option_len(p); |
| 390 | if (end && (p >= end - (2 + opt_len))) |
| 391 | return 0; /* malformed packet */ |
| 392 | if (*p == opt) |
| 393 | return p; |
| 394 | p += opt_len + 2; |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | return NULL; |
| 399 | } |
| 400 | |
| 401 | static unsigned char *option_find(struct dhcp_packet *mess, int size, int opt_type) |
| 402 | { |
| 403 | int overload = 0; |
| 404 | unsigned char *ret; |
| 405 | |
| 406 | ret = option_find1(&mess->options[0], ((unsigned char *)mess) + size, opt_type, &overload); |
| 407 | |
| 408 | if (!ret && (overload & 1)) |
| 409 | ret = option_find1(&mess->file[0], &mess->file[128], opt_type, &overload); |
| 410 | |
| 411 | if (!ret && (overload & 2)) |
| 412 | ret = option_find1(&mess->sname[0], &mess->file[64], opt_type, &overload); |
| 413 | |
| 414 | return ret; |
| 415 | } |
| 416 | |
| 417 | static int in_list(unsigned char *list, int opt) |
| 418 | { |
| 419 | int i; |
| 420 | |
| 421 | for (i = 0; list[i] != OPTION_END; i++) |
| 422 | if (opt == list[i]) |
| 423 | return 1; |
| 424 | |
| 425 | return 0; |
| 426 | } |
| 427 | |
| 428 | static struct dhcp_opt *option_find2(struct dhcp_opt *opts, int opt) |
| 429 | { |
| 430 | for (; opts; opts = opts->next) |
| 431 | if (opts->opt == opt) |
| 432 | return opts; |
| 433 | return NULL; |
| 434 | } |
| 435 | |
| 436 | static unsigned char *do_req_options(struct dhcp_context *context, |
| 437 | unsigned char *p, unsigned char *end, |
| 438 | unsigned char *req_options, |
| 439 | struct dhcp_opt *config_opts, |
| 440 | char *domainname, char *hostname) |
| 441 | { |
| 442 | int i; |
| 443 | |
| 444 | if (!req_options) |
| 445 | return p; |
| 446 | |
Simon Kelley | 1ab84e2 | 2004-01-29 16:48:35 +0000 | [diff] [blame] | 447 | if (in_list(req_options, OPTION_MAXMESSAGE)) |
| 448 | p = option_put(p, end, OPTION_MAXMESSAGE, 2, sizeof(struct udp_dhcp_packet)); |
| 449 | |
Simon Kelley | 9e4abcb | 2004-01-22 19:47:41 +0000 | [diff] [blame] | 450 | if (in_list(req_options, OPTION_NETMASK) && |
| 451 | !option_find2(config_opts, OPTION_NETMASK)) |
| 452 | p = option_put(p, end, OPTION_NETMASK, INADDRSZ, ntohl(context->netmask.s_addr)); |
| 453 | |
| 454 | if (in_list(req_options, OPTION_BROADCAST) && |
| 455 | !option_find2(config_opts, OPTION_BROADCAST)) |
| 456 | p = option_put(p, end, OPTION_BROADCAST, INADDRSZ, ntohl(context->broadcast.s_addr)); |
| 457 | |
| 458 | if (in_list(req_options, OPTION_ROUTER) && |
| 459 | !option_find2(config_opts, OPTION_ROUTER)) |
| 460 | p = option_put(p, end, OPTION_ROUTER, INADDRSZ, ntohl(context->serv_addr.s_addr)); |
| 461 | |
| 462 | if (in_list(req_options, OPTION_DNSSERVER) && |
| 463 | !option_find2(config_opts, OPTION_DNSSERVER)) |
| 464 | p = option_put(p, end, OPTION_DNSSERVER, INADDRSZ, ntohl(context->serv_addr.s_addr)); |
| 465 | |
| 466 | if (in_list(req_options, OPTION_DOMAINNAME) && |
| 467 | !option_find2(config_opts, OPTION_DOMAINNAME) && |
| 468 | domainname && (p + strlen(domainname) + 2 < end)) |
| 469 | { |
| 470 | *(p++) = OPTION_DOMAINNAME; |
| 471 | *(p++) = strlen(domainname); |
| 472 | memcpy(p, domainname, strlen(domainname)); |
| 473 | p += strlen(domainname); |
| 474 | } |
| 475 | |
| 476 | /* Note that we ignore attempts to set the hostname using |
| 477 | --dhcp-option=12,<name> */ |
| 478 | if (in_list(req_options, OPTION_HOSTNAME) && |
| 479 | hostname && (p + strlen(hostname) + 2 < end)) |
| 480 | { |
| 481 | *(p++) = OPTION_HOSTNAME; |
| 482 | *(p++) = strlen(hostname); |
| 483 | memcpy(p, hostname, strlen(hostname)); |
| 484 | p += strlen(hostname); |
| 485 | } |
| 486 | |
| 487 | for (i = 0; req_options[i] != OPTION_END; i++) |
| 488 | { |
| 489 | struct dhcp_opt *opt = option_find2(config_opts, req_options[i]); |
Simon Kelley | 1ab84e2 | 2004-01-29 16:48:35 +0000 | [diff] [blame] | 490 | if (req_options[i] != OPTION_HOSTNAME && |
| 491 | req_options[i] != OPTION_MAXMESSAGE && |
| 492 | opt && (p + opt->len + 2 < end)) |
Simon Kelley | 9e4abcb | 2004-01-22 19:47:41 +0000 | [diff] [blame] | 493 | { |
| 494 | *(p++) = opt->opt; |
| 495 | *(p++) = opt->len; |
Simon Kelley | 1ab84e2 | 2004-01-29 16:48:35 +0000 | [diff] [blame] | 496 | if (opt->len != 0) |
| 497 | { |
| 498 | if (opt->is_addr) |
| 499 | { |
| 500 | int j; |
| 501 | struct in_addr *a = (struct in_addr *)opt->val; |
| 502 | for (j = 0; j < opt->len; j+=INADDRSZ, a++) |
| 503 | { |
| 504 | /* zero means "self" */ |
| 505 | if (a->s_addr == 0) |
| 506 | memcpy(p, &context->serv_addr, INADDRSZ); |
| 507 | else |
| 508 | memcpy(p, a, INADDRSZ); |
| 509 | p += INADDRSZ; |
| 510 | } |
| 511 | } |
| 512 | else |
| 513 | { |
| 514 | memcpy(p, opt->val, opt->len); |
| 515 | p += opt->len; |
| 516 | } |
| 517 | } |
Simon Kelley | 9e4abcb | 2004-01-22 19:47:41 +0000 | [diff] [blame] | 518 | } |
| 519 | } |
| 520 | |
| 521 | return p; |
| 522 | } |
| 523 | |
| 524 | |