Home | History | Annotate | Download | only in dhcpcd
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <errno.h>
      4 #include <fcntl.h>
      5 
      6 #include "dhcp.h"
      7 #include "config.h"
      8 
      9 #define REQUEST	(1 << 0)
     10 #define UINT8	(1 << 1)
     11 #define UINT16	(1 << 2)
     12 #define SINT16	(1 << 3)
     13 #define UINT32	(1 << 4)
     14 #define SINT32	(1 << 5)
     15 #define IPV4	(1 << 6)
     16 #define STRING	(1 << 7)
     17 #define PAIR	(1 << 8)
     18 #define ARRAY	(1 << 9)
     19 #define RFC3361	(1 << 10)
     20 #define RFC3397	(1 << 11)
     21 #define RFC3442 (1 << 12)
     22 
     23 struct dhcp_opt {
     24 	uint8_t option;
     25 	int type;
     26 	const char *var;
     27 };
     28 
     29 static const struct dhcp_opt const dhcp_opts[] = {
     30 	{ 1,	IPV4 | REQUEST,	"subnet_mask" },
     31 	{ 2,	UINT32,		"time_offset" },
     32 	{ 3,	IPV4 | ARRAY | REQUEST,	"routers" },
     33 	{ 4,	IPV4 | ARRAY,	"time_servers" },
     34 	{ 5,	IPV4 | ARRAY,	"ien116_name_servers" },
     35 	{ 6,	IPV4 | ARRAY,	"domain_name_servers" },
     36 	{ 7,	IPV4 | ARRAY,	"log_servers" },
     37 	{ 8,	IPV4 | ARRAY,	"cookie_servers" },
     38 	{ 9, 	IPV4 | ARRAY,	"lpr_servers" },
     39 	{ 10,	IPV4 | ARRAY,	"impress_servers" },
     40 	{ 11,	IPV4 | ARRAY,	"resource_location_servers" },
     41 	{ 12,	STRING,		"host_name" },
     42 	{ 13,	UINT16,		"boot_size" },
     43 	{ 14,	STRING,		"merit_dump" },
     44 	{ 15,	STRING,		"domain_name" },
     45 	{ 16,	IPV4,		"swap_server" },
     46 	{ 17,	STRING,		"root_path" },
     47 	{ 18,	STRING,		"extensions_path" },
     48 	{ 19,	UINT8,		"ip_forwarding" },
     49 	{ 20,	UINT8,		"non_local_source_routing" },
     50 	{ 21,	IPV4 | ARRAY,	"policy_filter" },
     51 	{ 22,	SINT16,		"max_dgram_reassembly" },
     52 	{ 23,	UINT16,		"default_ip_ttl" },
     53 	{ 24,	UINT32,		"path_mtu_aging_timeout" },
     54 	{ 25,	UINT16 | ARRAY,	"path_mtu_plateau_table" },
     55 	{ 26,	UINT16,		"interface_mtu" },
     56 	{ 27,	UINT8,		"all_subnets_local" },
     57 	{ 28,	IPV4 | REQUEST,	"broadcast_address" },
     58 	{ 29,	UINT8,		"perform_mask_discovery" },
     59 	{ 30,	UINT8,		"mask_supplier" },
     60 	{ 31,	UINT8,		"router_discovery" },
     61 	{ 32,	IPV4,		"router_solicitation_address" },
     62 	{ 33,	IPV4 | ARRAY | REQUEST,	"static_routes" },
     63 	{ 34,	UINT8,		"trailer_encapsulation" },
     64 	{ 35, 	UINT32,		"arp_cache_timeout" },
     65 	{ 36,	UINT16,		"ieee802_3_encapsulation" },
     66 	{ 37,	UINT8,		"default_tcp_ttl" },
     67 	{ 38,	UINT32,		"tcp_keepalive_interval" },
     68 	{ 39,	UINT8,		"tcp_keepalive_garbage" },
     69 	{ 40,	STRING,		"nis_domain" },
     70 	{ 41,	IPV4 | ARRAY,	"nis_servers" },
     71 	{ 42,	IPV4 | ARRAY,	"ntp_servers" },
     72 	{ 43,	STRING,		"vendor_encapsulated_options" },
     73 	{ 44,	IPV4 | ARRAY,	"netbios_name_servers" },
     74 	{ 45,	IPV4,		"netbios_dd_server" },
     75 	{ 46,	UINT8,		"netbios_node_type" },
     76 	{ 47,	STRING,		"netbios_scope" },
     77 	{ 48,	IPV4 | ARRAY,	"font_servers" },
     78 	{ 49,	IPV4 | ARRAY,	"x_display_manager" },
     79 	{ 50, 	IPV4,		"dhcp_requested_address" },
     80 	{ 51,	UINT32 | REQUEST,	"dhcp_lease_time" },
     81 	{ 52,	UINT8,		"dhcp_option_overload" },
     82 	{ 53,	UINT8,		"dhcp_message_type" },
     83 	{ 54,	IPV4,		"dhcp_server_identifier" },
     84 	{ 55,	UINT8 | ARRAY,	"dhcp_parameter_request_list" },
     85 	{ 56,	STRING,		"dhcp_message" },
     86 	{ 57,	UINT16,		"dhcp_max_message_size" },
     87 	{ 58,	UINT32 | REQUEST,	"dhcp_renewal_time" },
     88 	{ 59,	UINT32 | REQUEST,	"dhcp_rebinding_time" },
     89 	{ 64,	STRING,		"nisplus_domain" },
     90 	{ 65,	IPV4 | ARRAY,	"nisplus_servers" },
     91 	{ 66,	STRING,		"tftp_server_name" },
     92 	{ 67,	STRING,		"bootfile_name" },
     93 	{ 68,	IPV4 | ARRAY,	"mobile_ip_home_agent" },
     94 	{ 69,	IPV4 | ARRAY,	"smtp_server" },
     95 	{ 70,	IPV4 | ARRAY,	"pop_server" },
     96 	{ 71,	IPV4 | ARRAY,	"nntp_server" },
     97 	{ 72,	IPV4 | ARRAY,	"www_server" },
     98 	{ 73,	IPV4 | ARRAY,	"finger_server" },
     99 	{ 74,	IPV4 | ARRAY,	"irc_server" },
    100 	{ 75,	IPV4 | ARRAY,	"streettalk_server" },
    101 	{ 76,	IPV4 | ARRAY,	"streettalk_directory_assistance_server" },
    102 	{ 77,	STRING,		"user_class" },
    103 	{ 85,	IPV4 | ARRAY,	"nds_servers" },
    104 	{ 86,	STRING,		"nds_tree_name" },
    105 	{ 87,	STRING,		"nds_context" },
    106 	{ 88,	STRING | RFC3397,	"bcms_controller_names" },
    107 	{ 89,	IPV4 | ARRAY,	"bcms_controller_address" },
    108 	{ 91,	UINT32,		"client_last_transaction_time" },
    109 	{ 92,	IPV4 | ARRAY,	"associated_ip" },
    110 	{ 98,	STRING,		"uap_servers" },
    111 	{ 112,	IPV4 | ARRAY,	"netinfo_server_address" },
    112 	{ 113,	STRING,		"netinfo_server_tag" },
    113 	{ 114,	STRING,		"default_url" },
    114 	{ 118,	IPV4,		"subnet_selection" },
    115 	{ 119,	STRING | RFC3397,	"domain_search" },
    116 	{ 121,  RFC3442 | REQUEST,	"classless_static_routes" },
    117 	{ 249,  RFC3442,	"ms-classless_static_routes" },
    118 	{ 0, 0, NULL }
    119 };
    120 
    121 struct dhcp_message *
    122 get_lease(const char *leasefile)
    123 {
    124 	int fd;
    125 	struct dhcp_message *dhcp;
    126 	ssize_t bytes;
    127 
    128 	fd = open(leasefile, O_RDONLY);
    129 	if (fd == -1)
    130 		return NULL;
    131 	dhcp = malloc(sizeof(*dhcp));
    132 	memset(dhcp, 0, sizeof(*dhcp));
    133 	bytes = read(fd, dhcp, sizeof(*dhcp));
    134 	close(fd);
    135 	if (bytes < 0) {
    136 		free(dhcp);
    137 		dhcp = NULL;
    138 	}
    139 	return dhcp;
    140 }
    141 
    142 static uint8_t *dhcp_opt_buffer = NULL;
    143 
    144 static int
    145 valid_length(uint8_t option, int dl, int *type)
    146 {
    147 	const struct dhcp_opt *opt;
    148 	ssize_t sz;
    149 
    150 	if (dl == 0)
    151 		return -1;
    152 
    153 	for (opt = dhcp_opts; opt->option; opt++) {
    154 		if (opt->option != option)
    155 			continue;
    156 
    157 		if (type)
    158 			*type = opt->type;
    159 
    160 		if (opt->type == 0 || opt->type & STRING || opt->type & RFC3442)
    161 			return 0;
    162 
    163 		sz = 0;
    164 		if (opt->type & UINT32 || opt->type & IPV4)
    165 			sz = sizeof(uint32_t);
    166 		if (opt->type & UINT16)
    167 			sz = sizeof(uint16_t);
    168 		if (opt->type & UINT8)
    169 			sz = sizeof(uint8_t);
    170 		if (opt->type & IPV4 || opt->type & ARRAY)
    171 			return dl % sz;
    172 		return (dl == sz ? 0 : -1);
    173 	}
    174 
    175 	/* unknown option, so let it pass */
    176 	return 0;
    177 }
    178 
    179 static void
    180 free_option_buffer(void)
    181 {
    182 	free(dhcp_opt_buffer);
    183 }
    184 
    185 
    186 #define get_option_raw(dhcp, opt) get_option(dhcp, opt, NULL, NULL)
    187 static const uint8_t *
    188 get_option(const struct dhcp_message *dhcp, uint8_t opt, int *len, int *type)
    189 {
    190 	const uint8_t *p = dhcp->options;
    191 	const uint8_t *e = p + sizeof(dhcp->options);
    192 	uint8_t l, ol = 0;
    193 	uint8_t o = 0;
    194 	uint8_t overl = 0;
    195 	uint8_t *bp = NULL;
    196 	const uint8_t *op = NULL;
    197 	int bl = 0;
    198 
    199 	while (p < e) {
    200 		o = *p++;
    201 		if (o == opt) {
    202 			if (op) {
    203 				if (!dhcp_opt_buffer) {
    204 					dhcp_opt_buffer = malloc(sizeof(struct dhcp_message));
    205 					atexit(free_option_buffer);
    206 				}
    207 				if (!bp)
    208 					bp = dhcp_opt_buffer;
    209 				memcpy(bp, op, ol);
    210 				bp += ol;
    211 			}
    212 			ol = *p;
    213 			op = p + 1;
    214 			bl += ol;
    215 		}
    216 		switch (o) {
    217 		case DHO_PAD:
    218 			continue;
    219 		case DHO_END:
    220 			if (overl & 1) {
    221 				/* bit 1 set means parse boot file */
    222 				overl &= ~1;
    223 				p = dhcp->bootfile;
    224 				e = p + sizeof(dhcp->bootfile);
    225 			} else if (overl & 2) {
    226 				/* bit 2 set means parse server name */
    227 				overl &= ~2;
    228 				p = dhcp->servername;
    229 				e = p + sizeof(dhcp->servername);
    230 			} else
    231 				goto exit;
    232 			break;
    233 		case DHO_OPTIONSOVERLOADED:
    234 			/* Ensure we only get this option once */
    235 			if (!overl)
    236 				overl = p[1];
    237 			break;
    238 		}
    239 		l = *p++;
    240 		p += l;
    241 	}
    242 
    243 exit:
    244 	if (valid_length(o, bl, type) == -1) {
    245 		errno = EINVAL;
    246 		return NULL;
    247 	}
    248 	if (len)
    249 		*len = bl;
    250 	if (bp) {
    251 		memcpy(bp, op, ol);
    252 		return (const uint8_t *)&dhcp_opt_buffer;
    253 	}
    254 	if (op)
    255 		return op;
    256 	errno = ENOENT;
    257 	return NULL;
    258 }
    259 
    260 int
    261 get_option_addr32(uint32_t *a, const struct dhcp_message *dhcp, uint8_t option)
    262 {
    263 	const uint8_t *p = get_option_raw(dhcp, option);
    264 
    265 	if (!p)
    266 		return -1;
    267 	memcpy(a, p, sizeof(*a));
    268 	return 0;
    269 }
    270 
    271 int
    272 get_option_uint32(uint32_t *i, const struct dhcp_message *dhcp, uint8_t option)
    273 {
    274 	uint32_t a;
    275 
    276 	if (get_option_addr32(&a, dhcp, option) == -1)
    277 		return -1;
    278 
    279 	*i = ntohl(a);
    280 	return 0;
    281 }
    282 
    283 uint32_t
    284 get_netmask(uint32_t addr)
    285 {
    286 	uint32_t dst;
    287 
    288 	if (addr == 0)
    289 		return 0;
    290 
    291 	dst = htonl(addr);
    292 	if (IN_CLASSA(dst))
    293 		return ntohl(IN_CLASSA_NET);
    294 	if (IN_CLASSB (dst))
    295 		return ntohl(IN_CLASSB_NET);
    296 	if (IN_CLASSC (dst))
    297 		return ntohl(IN_CLASSC_NET);
    298 
    299 	return 0;
    300 }
    301 
    302 void showlease(struct dhcp_lease *lease)
    303 {
    304     printf("addr:      %s\n", inet_ntoa(lease->addr));
    305     printf("net:       %s\n", inet_ntoa(lease->net));
    306     printf("leasetime: %d\n", lease->leasetime);
    307     printf("renew:     %d\n", lease->renewaltime);
    308     printf("rebind:    %d\n", lease->rebindtime);
    309     printf("server:    %s\n", inet_ntoa(lease->server));
    310 }
    311 #define MAX_LEASETIME 2147460
    312 
    313 int
    314 main(int argc, char *argv[])
    315 {
    316     struct dhcp_message *dhcp;
    317     struct dhcp_lease *lease;
    318     char leasefile[PATH_MAX];
    319 
    320     if (argc < 2) {
    321         fprintf(stderr, "Usage: %s <interface>\n", argv[0]);
    322         exit(1);
    323     }
    324     snprintf(leasefile, PATH_MAX, LEASEFILE, argv[1]);
    325     if ((dhcp = get_lease(leasefile)) == NULL) {
    326         fprintf(stderr, "Couldn't read lease file: %s\n", strerror(errno));
    327         exit(1);
    328     }
    329     lease = malloc(sizeof(*lease));
    330     lease->frominfo = 0;
    331     lease->addr.s_addr = dhcp->yiaddr;
    332 
    333     if (get_option_addr32(&lease->net.s_addr, dhcp, DHO_SUBNETMASK) == -1)
    334         lease->net.s_addr = get_netmask(dhcp->yiaddr);
    335     if (get_option_uint32(&lease->leasetime, dhcp, DHO_LEASETIME) != 0)
    336         lease->leasetime = DEFAULT_LEASETIME;
    337     get_option_addr32(&lease->server.s_addr, dhcp, DHO_SERVERID);
    338     /* Dm: limit lease time value to avoid negative numbers when
    339        converting to milliseconds */
    340     if ((lease->leasetime != ~0U) && (lease->leasetime > MAX_LEASETIME))
    341         lease->leasetime = MAX_LEASETIME;
    342     if (get_option_uint32(&lease->renewaltime, dhcp, DHO_RENEWALTIME) != 0)
    343         lease->renewaltime = 0;
    344     if (get_option_uint32(&lease->rebindtime, dhcp, DHO_REBINDTIME) != 0)
    345         lease->rebindtime = 0;
    346     showlease(lease);
    347     free(lease);
    348     return 0;
    349 }
    350