diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:20 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 18:28:20 -0800 |
commit | d97c47cad830d00c9da685cc4ea157d6185f6c97 (patch) | |
tree | 4b825dc642cb6eb9a060e54bf8d69288fbee4904 /dhcp.c | |
parent | 4c5a5fb53bccceff331bae70f748bf9b4609fe0a (diff) | |
download | android_external_dhcpcd-d97c47cad830d00c9da685cc4ea157d6185f6c97.tar.gz android_external_dhcpcd-d97c47cad830d00c9da685cc4ea157d6185f6c97.tar.bz2 android_external_dhcpcd-d97c47cad830d00c9da685cc4ea157d6185f6c97.zip |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'dhcp.c')
-rw-r--r-- | dhcp.c | 1251 |
1 files changed, 0 insertions, 1251 deletions
@@ -1,1251 +0,0 @@ -/* - * dhcpcd - DHCP client daemon - * Copyright 2006-2008 Roy Marples <roy@marples.name> - * All rights reserved - - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <ctype.h> -#include <errno.h> -#include <fcntl.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "config.h" -#include "common.h" -#include "dhcp.h" - -#define REQUEST (1 << 0) -#define UINT8 (1 << 1) -#define UINT16 (1 << 2) -#define SINT16 (1 << 3) -#define UINT32 (1 << 4) -#define SINT32 (1 << 5) -#define IPV4 (1 << 6) -#define STRING (1 << 7) -#define PAIR (1 << 8) -#define ARRAY (1 << 9) -#define RFC3361 (1 << 10) -#define RFC3397 (1 << 11) -#define RFC3442 (1 << 12) - -#define IPV4R IPV4 | REQUEST - -/* Our aggregate option buffer. - * We ONLY use this when options are split, which for most purposes is - * practically never. See RFC3396 for details. */ -static uint8_t *dhcp_opt_buffer = NULL; - -struct dhcp_opt { - uint8_t option; - int type; - const char *var; -}; - -static const struct dhcp_opt const dhcp_opts[] = { - { 1, IPV4 | REQUEST, "subnet_mask" }, - { 2, UINT32, "time_offset" }, - { 3, IPV4 | ARRAY | REQUEST, "routers" }, - { 4, IPV4 | ARRAY, "time_servers" }, - { 5, IPV4 | ARRAY, "ien116_name_servers" }, - { 6, IPV4 | ARRAY, "domain_name_servers" }, - { 7, IPV4 | ARRAY, "log_servers" }, - { 8, IPV4 | ARRAY, "cookie_servers" }, - { 9, IPV4 | ARRAY, "lpr_servers" }, - { 10, IPV4 | ARRAY, "impress_servers" }, - { 11, IPV4 | ARRAY, "resource_location_servers" }, - { 12, STRING, "host_name" }, - { 13, UINT16, "boot_size" }, - { 14, STRING, "merit_dump" }, - { 15, STRING, "domain_name" }, - { 16, IPV4, "swap_server" }, - { 17, STRING, "root_path" }, - { 18, STRING, "extensions_path" }, - { 19, UINT8, "ip_forwarding" }, - { 20, UINT8, "non_local_source_routing" }, - { 21, IPV4 | ARRAY, "policy_filter" }, - { 22, SINT16, "max_dgram_reassembly" }, - { 23, UINT16, "default_ip_ttl" }, - { 24, UINT32, "path_mtu_aging_timeout" }, - { 25, UINT16 | ARRAY, "path_mtu_plateau_table" }, - { 26, UINT16, "interface_mtu" }, - { 27, UINT8, "all_subnets_local" }, - { 28, IPV4 | REQUEST, "broadcast_address" }, - { 29, UINT8, "perform_mask_discovery" }, - { 30, UINT8, "mask_supplier" }, - { 31, UINT8, "router_discovery" }, - { 32, IPV4, "router_solicitation_address" }, - { 33, IPV4 | ARRAY | REQUEST, "static_routes" }, - { 34, UINT8, "trailer_encapsulation" }, - { 35, UINT32, "arp_cache_timeout" }, - { 36, UINT16, "ieee802_3_encapsulation" }, - { 37, UINT8, "default_tcp_ttl" }, - { 38, UINT32, "tcp_keepalive_interval" }, - { 39, UINT8, "tcp_keepalive_garbage" }, - { 40, STRING, "nis_domain" }, - { 41, IPV4 | ARRAY, "nis_servers" }, - { 42, IPV4 | ARRAY, "ntp_servers" }, - { 43, STRING, "vendor_encapsulated_options" }, - { 44, IPV4 | ARRAY, "netbios_name_servers" }, - { 45, IPV4, "netbios_dd_server" }, - { 46, UINT8, "netbios_node_type" }, - { 47, STRING, "netbios_scope" }, - { 48, IPV4 | ARRAY, "font_servers" }, - { 49, IPV4 | ARRAY, "x_display_manager" }, - { 50, IPV4, "dhcp_requested_address" }, - { 51, UINT32 | REQUEST, "dhcp_lease_time" }, - { 52, UINT8, "dhcp_option_overload" }, - { 53, UINT8, "dhcp_message_type" }, - { 54, IPV4, "dhcp_server_identifier" }, - { 55, UINT8 | ARRAY, "dhcp_parameter_request_list" }, - { 56, STRING, "dhcp_message" }, - { 57, UINT16, "dhcp_max_message_size" }, - { 58, UINT32 | REQUEST, "dhcp_renewal_time" }, - { 59, UINT32 | REQUEST, "dhcp_rebinding_time" }, - { 64, STRING, "nisplus_domain" }, - { 65, IPV4 | ARRAY, "nisplus_servers" }, - { 66, STRING, "tftp_server_name" }, - { 67, STRING, "bootfile_name" }, - { 68, IPV4 | ARRAY, "mobile_ip_home_agent" }, - { 69, IPV4 | ARRAY, "smtp_server" }, - { 70, IPV4 | ARRAY, "pop_server" }, - { 71, IPV4 | ARRAY, "nntp_server" }, - { 72, IPV4 | ARRAY, "www_server" }, - { 73, IPV4 | ARRAY, "finger_server" }, - { 74, IPV4 | ARRAY, "irc_server" }, - { 75, IPV4 | ARRAY, "streettalk_server" }, - { 76, IPV4 | ARRAY, "streettalk_directory_assistance_server" }, - { 77, STRING, "user_class" }, - { 81, STRING | RFC3397, "fqdn_name" }, - { 85, IPV4 | ARRAY, "nds_servers" }, - { 86, STRING, "nds_tree_name" }, - { 87, STRING, "nds_context" }, - { 88, STRING | RFC3397, "bcms_controller_names" }, - { 89, IPV4 | ARRAY, "bcms_controller_address" }, - { 91, UINT32, "client_last_transaction_time" }, - { 92, IPV4 | ARRAY, "associated_ip" }, - { 98, STRING, "uap_servers" }, - { 112, IPV4 | ARRAY, "netinfo_server_address" }, - { 113, STRING, "netinfo_server_tag" }, - { 114, STRING, "default_url" }, - { 118, IPV4, "subnet_selection" }, - { 119, STRING | RFC3397, "domain_search" }, - { 121, RFC3442 | REQUEST, "classless_static_routes" }, - { 249, RFC3442, "ms_classless_static_routes" }, - { 0, 0, NULL } -}; - -void -print_options(void) -{ - const struct dhcp_opt *opt; - - for (opt = dhcp_opts; opt->option; opt++) - if (opt->var) - printf("%03d %s\n", opt->option, opt->var); -} - -int make_option_mask(uint8_t *mask, char **opts, int add) -{ - char *token, *p = *opts, *t; - const struct dhcp_opt *opt; - int match, n; - - while ((token = strsep(&p, ", "))) { - if (*token == '\0') - continue; - for (opt = dhcp_opts; opt->option; opt++) { - if (!opt->var) - continue; - match = 0; - if (strcmp(opt->var, token) == 0) - match = 1; - else { - errno = 0; - n = strtol(token, &t, 0); - if (errno == 0 && !*t) - if (opt->option == n) - match = 1; - } - if (match) { - if (add == 1) - add_option_mask(mask, - opt->option); - else - del_option_mask(mask, - opt->option); - break; - } - } - if (!opt->option) { - *opts = token; - errno = ENOENT; - return -1; - } - } - return 0; -} - -static int -valid_length(uint8_t option, int dl, int *type) -{ - const struct dhcp_opt *opt; - ssize_t sz; - - if (dl == 0) - return -1; - - for (opt = dhcp_opts; opt->option; opt++) { - if (opt->option != option) - continue; - - if (type) - *type = opt->type; - - if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442) - return 0; - - sz = 0; - if (opt->type & UINT32 || opt->type & IPV4) - sz = sizeof(uint32_t); - if (opt->type & UINT16) - sz = sizeof(uint16_t); - if (opt->type & UINT8) - sz = sizeof(uint8_t); - if (opt->type & IPV4 || opt->type & ARRAY) - return dl % sz; - return (dl == sz ? 0 : -1); - } - - /* unknown option, so let it pass */ - return 0; -} - -static void -free_option_buffer(void) -{ - free(dhcp_opt_buffer); -} - -#define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL) -static const uint8_t * -get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type) -{ - const uint8_t *p = dhcp->options; - const uint8_t *e = p + sizeof(dhcp->options); - uint8_t l, ol = 0; - uint8_t o = 0; - uint8_t overl = 0; - uint8_t *bp = NULL; - const uint8_t *op = NULL; - int bl = 0; - - while (p < e) { - o = *p++; - if (o == opt) { - if (op) { - if (!dhcp_opt_buffer) { - dhcp_opt_buffer = xmalloc(sizeof(struct dhcp_message)); - atexit(free_option_buffer); - } - if (!bp) - bp = dhcp_opt_buffer; - memcpy(bp, op, ol); - bp += ol; - } - ol = *p; - op = p + 1; - bl += ol; - } - switch (o) { - case DHO_PAD: - continue; - case DHO_END: - if (overl & 1) { - /* bit 1 set means parse boot file */ - overl &= ~1; - p = dhcp->bootfile; - e = p + sizeof(dhcp->bootfile); - } else if (overl & 2) { - /* bit 2 set means parse server name */ - overl &= ~2; - p = dhcp->servername; - e = p + sizeof(dhcp->servername); - } else - goto exit; - break; - case DHO_OPTIONSOVERLOADED: - /* Ensure we only get this option once */ - if (!overl) - overl = p[1]; - break; - } - l = *p++; - p += l; - } - -exit: - if (valid_length(opt, bl, type) == -1) { - errno = EINVAL; - return NULL; - } - if (len) - *len = bl; - if (bp) { - memcpy(bp, op, ol); - return (const uint8_t *)&dhcp_opt_buffer; - } - if (op) - return op; - errno = ENOENT; - return NULL; -} - -int -get_option_addr(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option) -{ - const uint8_t *p = get_option_raw(dhcp, option); - - if (!p) - return -1; - memcpy(a, p, sizeof(*a)); - return 0; -} - -int -get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option) -{ - uint32_t a; - - if (get_option_addr(&a, dhcp, option) == -1) - return -1; - - *i = ntohl(a); - return 0; -} - -int -get_option_uint16(uint16_t *i, const struct dhcp_message *dhcp, uint8_t option) -{ - const uint8_t *p = get_option_raw(dhcp, option); - uint16_t d; - - if (!p) - return -1; - memcpy(&d, p, sizeof(d)); - *i = ntohs(d); - return 0; -} - -int -get_option_uint8(uint8_t *i, const struct dhcp_message *dhcp, uint8_t option) -{ - const uint8_t *p = get_option_raw(dhcp, option); - - if (!p) - return -1; - *i = *(p); - return 0; -} - -/* Decode an RFC3397 DNS search order option into a space - * seperated string. Returns length of string (including - * terminating zero) or zero on error. out may be NULL - * to just determine output length. */ -static ssize_t -decode_rfc3397(char *out, ssize_t len, int pl, const uint8_t *p) -{ - const uint8_t *r, *q = p; - int count = 0, l, hops; - uint8_t ltype; - - while (q - p < pl) { - r = NULL; - hops = 0; - /* We check we are inside our length again incase - * the data is NOT terminated correctly. */ - while ((l = *q++) && q - p < pl) { - ltype = l & 0xc0; - if (ltype == 0x80 || ltype == 0x40) - return 0; - else if (ltype == 0xc0) { /* pointer */ - l = (l & 0x3f) << 8; - l |= *q++; - /* save source of first jump. */ - if (!r) - r = q; - hops++; - if (hops > 255) - return 0; - q = p + l; - if (q - p >= pl) - return 0; - } else { - /* straightforward name segment, add with '.' */ - count += l + 1; - if (out) { - if ((ssize_t)l + 1 > len) { - errno = ENOBUFS; - return -1; - } - memcpy(out, q, l); - out += l; - *out++ = '.'; - len -= l; - len--; - } - q += l; - } - } - /* change last dot to space */ - if (out) - *(out - 1) = ' '; - if (r) - q = r; - } - - /* change last space to zero terminator */ - if (out) - *(out - 1) = 0; - - return count; -} - -static ssize_t -decode_rfc3442(char *out, ssize_t len, int pl, const uint8_t *p) -{ - const uint8_t *e; - ssize_t bytes = 0; - ssize_t b; - uint8_t cidr; - uint8_t ocets; - struct in_addr addr; - char *o = out; - - /* Minimum is 5 -first is CIDR and a router length of 4 */ - if (pl < 5) { - errno = EINVAL; - return -1; - } - - e = p + pl; - while (p < e) { - cidr = *p++; - if (cidr > 32) { - errno = EINVAL; - return -1; - } - ocets = (cidr + 7) / 8; - if (!out) { - p += 4 + ocets; - bytes += ((4 * 4) * 2) + 4; - continue; - } - if ((((4 * 4) * 2) + 4) > len) { - errno = ENOBUFS; - return -1; - } - if (o != out) { - *o++ = ' '; - len--; - } - /* If we have ocets then we have a destination and netmask */ - if (ocets > 0) { - addr.s_addr = 0; - memcpy(&addr.s_addr, p, (size_t)ocets); - b = snprintf(o, len, "%s/%d", inet_ntoa(addr), cidr); - p += ocets; - } else - b = snprintf(o, len, "0.0.0.0/0"); - o += b; - len -= b; - - /* Finally, snag the router */ - memcpy(&addr.s_addr, p, 4); - p += 4; - b = snprintf(o, len, " %s", inet_ntoa(addr)); - o += b; - len -= b; - } - - if (out) - return o - out; - return bytes; -} - -static struct rt * -decode_rfc3442_rt(int dl, const uint8_t *data) -{ - const uint8_t *p = data; - const uint8_t *e; - uint8_t cidr; - uint8_t ocets; - struct rt *routes = NULL; - struct rt *rt = NULL; - - /* Minimum is 5 -first is CIDR and a router length of 4 */ - if (dl < 5) - return NULL; - - e = p + dl; - while (p < e) { - cidr = *p++; - if (cidr > 32) { - free_routes(routes); - errno = EINVAL; - return NULL; - } - - if (rt) { - rt->next = xzalloc(sizeof(*rt)); - rt = rt->next; - } else { - routes = rt = xzalloc(sizeof(*routes)); - } - rt->next = NULL; - - ocets = (cidr + 7) / 8; - /* If we have ocets then we have a destination and netmask */ - if (ocets > 0) { - memcpy(&rt->dest.s_addr, p, (size_t)ocets); - memset(&rt->net.s_addr, 255, (size_t)ocets - 1); - memset((uint8_t *)&rt->net.s_addr + - (ocets - 1), - (256 - (1 << (32 - cidr) % 8)), 1); - p += ocets; - } else { - rt->dest.s_addr = 0; - rt->net.s_addr = 0; - } - - /* Finally, snag the router */ - memcpy(&rt->gate.s_addr, p, 4); - p += 4; - } - return routes; -} - -static char * -decode_rfc3361(int dl, const uint8_t *data) -{ - uint8_t enc; - unsigned int l; - char *sip = NULL; - struct in_addr addr; - char *p; - - if (dl < 2) { - errno = EINVAL; - return 0; - } - - enc = *data++; - dl--; - switch (enc) { - case 0: - if ((l = decode_rfc3397(NULL, 0, dl, data)) > 0) { - sip = xmalloc(l); - decode_rfc3397(sip, l, dl, data); - } - break; - case 1: - if (dl == 0 || dl % 4 != 0) { - errno = EINVAL; - break; - } - addr.s_addr = INADDR_BROADCAST; - l = ((dl / sizeof(addr.s_addr)) * ((4 * 4) + 1)) + 1; - sip = p = xmalloc(l); - while (l != 0) { - memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); - data += sizeof(addr.s_addr); - p += snprintf(p, l - (p - sip), "%s ", inet_ntoa(addr)); - l -= sizeof(addr.s_addr); - } - *--p = '\0'; - break; - default: - errno = EINVAL; - return 0; - } - - return sip; -} - -char * -get_option_string(const struct dhcp_message *dhcp, uint8_t option) -{ - int type = 0; - int len; - const uint8_t *p; - char *s; - - p = get_option(dhcp, option, &len, &type); - if (!p || *p == '\0') - return NULL; - - if (type & RFC3397) { - type = decode_rfc3397(NULL, 0, len, p); - if (!type) { - errno = EINVAL; - return NULL; - } - s = xmalloc(sizeof(char) * type); - decode_rfc3397(s, type, len, p); - return s; - } - - if (type & RFC3361) - return decode_rfc3361(len, p); - - s = xmalloc(sizeof(char) * (len + 1)); - memcpy(s, p, len); - s[len] = '\0'; - return s; -} - -/* This calculates the netmask that we should use for static routes. - * This IS different from the calculation used to calculate the netmask - * for an interface address. */ -static uint32_t -route_netmask(uint32_t ip_in) -{ - /* used to be unsigned long - check if error */ - uint32_t p = ntohl(ip_in); - uint32_t t; - - if (IN_CLASSA(p)) - t = ~IN_CLASSA_NET; - else { - if (IN_CLASSB(p)) - t = ~IN_CLASSB_NET; - else { - if (IN_CLASSC(p)) - t = ~IN_CLASSC_NET; - else - t = 0; - } - } - - while (t & p) - t >>= 1; - - return (htonl(~t)); -} - -/* We need to obey routing options. - * If we have a CSR then we only use that. - * Otherwise we add static routes and then routers. */ -struct rt * -get_option_routes(const struct dhcp_message *dhcp) -{ - const uint8_t *p; - const uint8_t *e; - struct rt *routes = NULL; - struct rt *route = NULL; - int len; - - /* If we have CSR's then we MUST use these only */ - p = get_option(dhcp, DHO_CSR, &len, NULL); - /* Check for crappy MS option */ - if (!p) - p = get_option(dhcp, DHO_MSCSR, &len, NULL); - if (p) { - routes = decode_rfc3442_rt(len, p); - if (routes) - return routes; - } - - /* OK, get our static routes first. */ - p = get_option(dhcp, DHO_STATICROUTE, &len, NULL); - if (p) { - e = p + len; - while (p < e) { - if (route) { - route->next = xmalloc(sizeof(*route)); - route = route->next; - } else - routes = route = xmalloc(sizeof(*routes)); - route->next = NULL; - memcpy(&route->dest.s_addr, p, 4); - p += 4; - memcpy(&route->gate.s_addr, p, 4); - p += 4; - route->net.s_addr = route_netmask(route->dest.s_addr); - } - } - - /* Now grab our routers */ - p = get_option(dhcp, DHO_ROUTER, &len, NULL); - if (p) { - e = p + len; - while (p < e) { - if (route) { - route->next = xzalloc(sizeof(*route)); - route = route->next; - } else - routes = route = xzalloc(sizeof(*route)); - memcpy(&route->gate.s_addr, p, 4); - p += 4; - } - } - - return routes; -} - -static size_t -encode_rfc1035(const char *src, uint8_t *dst, size_t len) -{ - const char *c = src; - uint8_t *p = dst; - uint8_t *lp = p++; - - if (len == 0) - return 0; - while (c < src + len) { - if (*c == '\0') - break; - if (*c == '.') { - /* Skip the trailing . */ - if (c == src + len - 1) - break; - *lp = p - lp - 1; - if (*lp == '\0') - return p - dst; - lp = p++; - } else - *p++ = (uint8_t) *c; - c++; - } - *lp = p - lp - 1; - *p++ = '\0'; - return p - dst; -} - -ssize_t -make_message(struct dhcp_message **message, - const struct interface *iface, const struct dhcp_lease *lease, - uint32_t xid, uint8_t type, const struct options *options) -{ - struct dhcp_message *dhcp; - uint8_t *m, *lp, *p; - uint8_t *n_params = NULL; - time_t up = uptime() - iface->start_uptime; - uint32_t ul; - uint16_t sz; - const struct dhcp_opt *opt; - - dhcp = xzalloc(sizeof (*dhcp)); - m = (uint8_t *)dhcp; - p = dhcp->options; - - if ((type == DHCP_INFORM || - type == DHCP_RELEASE || - type == DHCP_REQUEST) && - !IN_LINKLOCAL(ntohl(iface->addr.s_addr))) - { - dhcp->ciaddr = iface->addr.s_addr; - /* Just incase we haven't actually configured the address yet */ - if (type == DHCP_INFORM && iface->addr.s_addr == 0) - dhcp->ciaddr = lease->addr.s_addr; - /* Zero the address if we're currently on a different subnet */ - if (type == DHCP_REQUEST && - iface->net.s_addr != lease->net.s_addr) - dhcp->ciaddr = 0; - } - - dhcp->op = DHCP_BOOTREQUEST; - dhcp->hwtype = iface->family; - switch (iface->family) { - case ARPHRD_ETHER: - case ARPHRD_IEEE802: - dhcp->hwlen = ETHER_ADDR_LEN; - memcpy(&dhcp->chaddr, &iface->hwaddr, ETHER_ADDR_LEN); - break; - case ARPHRD_IEEE1394: - case ARPHRD_INFINIBAND: - dhcp->hwlen = 0; - if (dhcp->ciaddr == 0) - dhcp->flags = htons(BROADCAST_FLAG); - break; - } - - if (up < 0 || up > (time_t)UINT16_MAX) - dhcp->secs = htons((uint16_t)UINT16_MAX); - else - dhcp->secs = htons(up); - dhcp->xid = xid; - dhcp->cookie = htonl(MAGIC_COOKIE); - - *p++ = DHO_MESSAGETYPE; - *p++ = 1; - *p++ = type; - - if (type == DHCP_REQUEST) { - *p++ = DHO_MAXMESSAGESIZE; - *p++ = 2; - sz = get_mtu(iface->name); - if (sz < MTU_MIN) { - if (set_mtu(iface->name, MTU_MIN) == 0) - sz = MTU_MIN; - } - sz = htons(sz); - memcpy(p, &sz, 2); - p += 2; - } - - if (iface->clientid) { - *p++ = DHO_CLIENTID; - memcpy(p, iface->clientid, iface->clientid[0] + 1); - p += iface->clientid[0] + 1; - } - - if (type != DHCP_DECLINE && type != DHCP_RELEASE) { - if (options->userclass[0]) { - *p++ = DHO_USERCLASS; - memcpy(p, options->userclass, options->userclass[0] + 1); - p += options->userclass[0] + 1; - } - - if (options->vendorclassid[0]) { - *p++ = DHO_VENDORCLASSID; - memcpy(p, options->vendorclassid, - options->vendorclassid[0] + 1); - p += options->vendorclassid[0] + 1; - } - } - - if (type == DHCP_DISCOVER || type == DHCP_REQUEST) { -#define PUTADDR(_type, _val) \ - { \ - *p++ = _type; \ - *p++ = 4; \ - memcpy(p, &_val.s_addr, 4); \ - p += 4; \ - } - if (lease->addr.s_addr && - lease->addr.s_addr != iface->addr.s_addr && - !IN_LINKLOCAL(ntohl(lease->addr.s_addr))) - { - PUTADDR(DHO_IPADDRESS, lease->addr); - if (lease->server.s_addr) - PUTADDR(DHO_SERVERID, lease->server); - } -#undef PUTADDR - - if (options->leasetime != 0) { - *p++ = DHO_LEASETIME; - *p++ = 4; - ul = htonl(options->leasetime); - memcpy(p, &ul, 4); - p += 4; - } - } - - if (type == DHCP_DISCOVER || - type == DHCP_INFORM || - type == DHCP_REQUEST) - { - if (options->hostname[0]) { - *p++ = DHO_HOSTNAME; - memcpy(p, options->hostname, options->hostname[0] + 1); - p += options->hostname[0] + 1; - } - if (options->fqdn != FQDN_DISABLE) { - /* IETF DHC-FQDN option (81), RFC4702 */ - *p++ = DHO_FQDN; - lp = p; - *p++ = 3; - /* - * Flags: 0000NEOS - * S: 1 => Client requests Server to update - * a RR in DNS as well as PTR - * O: 1 => Server indicates to client that - * DNS has been updated - * E: 1 => Name data is DNS format - * N: 1 => Client requests Server to not - * update DNS - */ - *p++ = (options->fqdn & 0x09) | 0x04; - *p++ = 0; /* from server for PTR RR */ - *p++ = 0; /* from server for A RR if S=1 */ - ul = encode_rfc1035(options->hostname + 1, p, - options->hostname[0]); - *lp += ul; - p += ul; - } - - /* vendor is already encoded correctly, so just add it */ - if (options->vendor[0]) { - *p++ = DHO_VENDOR; - memcpy(p, options->vendor, options->vendor[0] + 1); - p += options->vendor[0] + 1; - } - - *p++ = DHO_PARAMETERREQUESTLIST; - n_params = p; - *p++ = 0; - for (opt = dhcp_opts; opt->option; opt++) { - if (!(opt->type & REQUEST || - has_option_mask(options->requestmask, opt->option))) - continue; - switch (opt->option) { - case DHO_RENEWALTIME: /* FALLTHROUGH */ - case DHO_REBINDTIME: - if (type == DHCP_INFORM) - continue; - break; - } - *p++ = opt->option; - } - *n_params = p - n_params - 1; - } - *p++ = DHO_END; - -#ifdef BOOTP_MESSAGE_LENTH_MIN - /* Some crappy DHCP servers think they have to obey the BOOTP minimum - * message length. - * They are wrong, but we should still cater for them. */ - while (p - m < BOOTP_MESSAGE_LENTH_MIN) - *p++ = DHO_PAD; -#endif - - *message = dhcp; - return p - m; -} - -ssize_t -write_lease(const struct interface *iface, const struct dhcp_message *dhcp) -{ - int fd; - ssize_t bytes = sizeof(*dhcp); - const uint8_t *p = dhcp->options; - const uint8_t *e = p + sizeof(dhcp->options); - uint8_t l; - uint8_t o = 0; - - fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400); -#ifdef ANDROID - if (fd == -1 && errno == EACCES) { - /* the lease file might have been created when dhcpcd was running as root */ - unlink(iface->leasefile); - fd = open(iface->leasefile, O_WRONLY | O_CREAT | O_TRUNC, 0400); - } -#endif - if (fd == -1) - return -1; - - /* Only write as much as we need */ - while (p < e) { - o = *p; - if (o == DHO_END) { - bytes = p - (const uint8_t *)dhcp; - break; - } - p++; - if (o != DHO_PAD) { - l = *p++; - p += l; - } - } - bytes = write(fd, dhcp, bytes); - close(fd); - return bytes; -} - -struct dhcp_message * -read_lease(const struct interface *iface) -{ - int fd; - struct dhcp_message *dhcp; - ssize_t bytes; - - fd = open(iface->leasefile, O_RDONLY); - if (fd == -1) - return NULL; - dhcp = xmalloc(sizeof(*dhcp)); - memset(dhcp, 0, sizeof(*dhcp)); - bytes = read(fd, dhcp, sizeof(*dhcp)); - close(fd); - if (bytes < 0) { - free(dhcp); - dhcp = NULL; - } - return dhcp; -} - -static ssize_t -print_string(char *s, ssize_t len, int dl, const uint8_t *data) -{ - uint8_t c; - const uint8_t *e; - ssize_t bytes = 0; - ssize_t r; - - e = data + dl; - while (data < e) { - c = *data++; - if (!isascii(c) || !isprint(c)) { - if (s) { - if (len < 5) { - errno = ENOBUFS; - return -1; - } - r = snprintf(s, len, "\\%03o", c); - len -= r; - bytes += r; - s += r; - } else - bytes += 4; - continue; - } - switch (c) { - case '"': /* FALLTHROUGH */ - case '\'': /* FALLTHROUGH */ - case '$': /* FALLTHROUGH */ - case '`': /* FALLTHROUGH */ - case '\\': /* FALLTHROUGH */ - if (s) { - if (len < 3) { - errno = ENOBUFS; - return -1; - } - *s++ = '\\'; - len--; - } - bytes++; - break; - } - if (s) { - *s++ = c; - len--; - } - bytes++; - } - - /* NULL */ - if (s) - *s = '\0'; - bytes++; - return bytes; -} - -static ssize_t -print_option(char *s, ssize_t len, int type, int dl, const uint8_t *data) -{ - const uint8_t *e, *t; - uint16_t u16; - int16_t s16; - uint32_t u32; - int32_t s32; - struct in_addr addr; - ssize_t bytes = 0; - ssize_t l; - char *tmp; - - if (type & RFC3397) { - l = decode_rfc3397(NULL, 0, dl, data); - if (l < 1) - return l; - tmp = xmalloc(l); - decode_rfc3397(tmp, l, dl, data); - l = print_string(s, len, l - 1, (uint8_t *)tmp); - free(tmp); - return l; - } - - if (type & RFC3442) - return decode_rfc3442(s, len, dl, data); - - if (type & STRING) { - /* Some DHCP servers return NULL strings */ - if (*data == '\0') - return 0; - return print_string(s, len, dl, data); - } - - if (!s) { - if (type & UINT8) - l = 3; - else if (type & UINT16) - l = 5; - else if (type & SINT16) - l = 6; - else if (type & UINT32) - l = 10; - else if (type & SINT32) - l = 11; - else if (type & IPV4) - l = 16; - else { - errno = EINVAL; - return -1; - } - return (l + 1) * dl; - } - - t = data; - e = data + dl; - while (data < e) { - if (data != t) { - *s++ = ' '; - bytes++; - len--; - } - if (type & UINT8) { - l = snprintf(s, len, "%d", *data); - data++; - } else if (type & UINT16) { - memcpy(&u16, data, sizeof(u16)); - u16 = ntohs(u16); - l = snprintf(s, len, "%d", u16); - data += sizeof(u16); - } else if (type & SINT16) { - memcpy(&s16, data, sizeof(s16)); - s16 = ntohs(s16); - l = snprintf(s, len, "%d", s16); - data += sizeof(s16); - } else if (type & UINT32) { - memcpy(&u32, data, sizeof(u32)); - u32 = ntohl(u32); - l = snprintf(s, len, "%d", u32); - data += sizeof(u32); - } else if (type & SINT32) { - memcpy(&s32, data, sizeof(s32)); - s32 = ntohl(s32); - l = snprintf(s, len, "%d", s32); - data += sizeof(s32); - } else if (type & IPV4) { - memcpy(&addr.s_addr, data, sizeof(addr.s_addr)); - l = snprintf(s, len, "%s", inet_ntoa(addr)); - data += sizeof(addr.s_addr); - } else - l = 0; - len -= l; - bytes += l; - s += l; - } - - return bytes; -} - -static void -setvar(char ***e, const char *prefix, const char *var, const char *value) -{ - size_t len = strlen(prefix) + strlen(var) + strlen(value) + 4; - - **e = xmalloc(len); - snprintf(**e, len, "%s_%s=%s", prefix, var, value); - (*e)++; -} - -ssize_t -configure_env(char **env, const char *prefix, const struct dhcp_message *dhcp, - const struct options *options) -{ - unsigned int i; - const uint8_t *p; - int pl; - struct in_addr addr; - struct in_addr net; - struct in_addr brd; - char *val, *v; - const struct dhcp_opt *opt; - ssize_t len, e = 0; - char **ep; - char cidr[4]; - uint8_t overl = 0; - - get_option_uint8(&overl, dhcp, DHO_OPTIONSOVERLOADED); - - if (!env) { - for (opt = dhcp_opts; opt->option; opt++) { - if (!opt->var) - continue; - if (has_option_mask(options->nomask, opt->option)) - continue; - if (get_option_raw(dhcp, opt->option)) - e++; - } - if (dhcp->yiaddr) - e += 5; - if (*dhcp->bootfile && !(overl & 1)) - e++; - if (*dhcp->servername && !(overl & 2)) - e++; - return e; - } - - ep = env; - if (dhcp->yiaddr) { - /* Set some useful variables that we derive from the DHCP - * message but are not necessarily in the options */ - addr.s_addr = dhcp->yiaddr; - setvar(&ep, prefix, "ip_address", inet_ntoa(addr)); - if (get_option_addr(&net.s_addr, dhcp, DHO_SUBNETMASK) == -1) { - net.s_addr = get_netmask(addr.s_addr); - setvar(&ep, prefix, "subnet_mask", inet_ntoa(net)); - } - i = inet_ntocidr(net); - snprintf(cidr, sizeof(cidr), "%d", inet_ntocidr(net)); - setvar(&ep, prefix, "subnet_cidr", cidr); - if (get_option_addr(&brd.s_addr, dhcp, DHO_BROADCAST) == -1) { - brd.s_addr = addr.s_addr | ~net.s_addr; - setvar(&ep, prefix, "broadcast_address", inet_ntoa(net)); - } - addr.s_addr = dhcp->yiaddr & net.s_addr; - setvar(&ep, prefix, "network_number", inet_ntoa(addr)); - } - - if (*dhcp->bootfile && !(overl & 1)) - setvar(&ep, prefix, "filename", (const char *)dhcp->bootfile); - if (*dhcp->servername && !(overl & 2)) - setvar(&ep, prefix, "server_name", (const char *)dhcp->servername); - - for (opt = dhcp_opts; opt->option; opt++) { - if (!opt->var) - continue; - if (has_option_mask(options->nomask, opt->option)) - continue; - val = NULL; - p = get_option(dhcp, opt->option, &pl, NULL); - if (!p) - continue; - /* We only want the FQDN name */ - if (opt->option == DHO_FQDN) { - p += 3; - pl -= 3; - } - len = print_option(NULL, 0, opt->type, pl, p); - if (len < 0) - return -1; - e = strlen(prefix) + strlen(opt->var) + len + 4; - v = val = *ep++ = xmalloc(e); - v += snprintf(val, e, "%s_%s=", prefix, opt->var); - if (len != 0) - print_option(v, len, opt->type, pl, p); - } - - return ep - env; -} |