Home | History | Annotate | Download | only in lib
      1 #define _GNU_SOURCE		/* For strnlen() */
      2 #include <stdlib.h>
      3 #include <errno.h>
      4 #include <string.h>
      5 // #include <arpa/inet.h>
      6 #include <netinet/in.h>
      7 
      8 // #include "dhcp.h"
      9 #include <dhcp.h>
     10 
     11 /*
     12  * Unpack DHCP options from a field.  Assumes opt is pre-initalized
     13  * (to all zero in the common case.)
     14  */
     15 int dhcp_unpack_field(const void *field, size_t len,
     16 		      struct dhcp_option opt[256])
     17 {
     18 	const uint8_t *p = field;
     19 	int err = 0;
     20 
     21 	while (len > 1) {
     22 		uint8_t op;
     23 		size_t xlen;
     24 
     25 		op = *p++; len--;
     26 		if (op == 0)
     27 			continue;
     28 		else if (op == 255)
     29 			break;
     30 
     31 		xlen = *p++; len--;
     32 		if (xlen > len)
     33 			break;
     34 		if (opt[op].len < 0)
     35 			opt[op].len = 0;
     36 		if (xlen) {
     37 			opt[op].data = realloc(opt[op].data,
     38 					       opt[op].len + xlen + 1);
     39 			if (!opt[op].data) {
     40 				err = ENOMEM;
     41 				continue;
     42 			}
     43 			memcpy((char *)opt[op].data + opt[op].len, p, xlen);
     44 			opt[op].len += xlen;
     45 			/* Null-terminate as a courtesy to users */
     46 			*((char *)opt[op].data + opt[op].len) = 0;
     47 			p += xlen;
     48 			len -= xlen;
     49 		}
     50 	}
     51 
     52 	return err;
     53 }
     54 
     55 /*
     56  * Unpack a DHCP packet, with overload support.  Do not use this
     57  * to unpack an encapsulated option set.
     58  */
     59 int dhcp_unpack_packet(const void *packet, size_t len,
     60 		       struct dhcp_option opt[256])
     61 {
     62 	const struct dhcp_packet *pkt = packet;
     63 	int err;
     64 	uint8_t overload;
     65 	int i;
     66 
     67 	if (len < 240 || pkt->magic != htonl(DHCP_VENDOR_MAGIC))
     68 		return EINVAL;	/* Bogus packet */
     69 
     70 	for (i = 0; i < 256; i++) {
     71 		opt[i].len = -1; /* Option not present */
     72 		opt[i].data = NULL;
     73 	}
     74 
     75 	err = dhcp_unpack_field(pkt->options, len-240, opt);
     76 
     77 	overload = 0;
     78 	if (opt[52].len == 1) {
     79 		overload = *(uint8_t *)opt[52].data;
     80 		free(opt[52].data);
     81 		opt[52].len = -1;
     82 		opt[52].data = NULL;
     83 	}
     84 
     85 	if (overload & 1) {
     86 		err |= dhcp_unpack_field(pkt->file, 128, opt);
     87 	} else {
     88 		opt[67].len  = strnlen((const char *)pkt->file, 128);
     89 		if (opt[67].len) {
     90 			opt[67].data = malloc(opt[67].len + 1);
     91 			if (opt[67].data) {
     92 				memcpy(opt[67].data, pkt->file, opt[67].len);
     93 				*((char *)opt[67].data + opt[67].len) = 0;
     94 			} else {
     95 				err |= ENOMEM;
     96 			}
     97 		}
     98 	}
     99 
    100 	if (overload & 2) {
    101 		err |= dhcp_unpack_field(pkt->sname, 64, opt);
    102 	} else {
    103 		opt[66].len  = strnlen((const char *)pkt->sname, 64);
    104 		if (opt[66].len) {
    105 			opt[66].data = malloc(opt[66].len + 1);
    106 			if (opt[66].data) {
    107 				memcpy(opt[66].data, pkt->file, opt[66].len);
    108 				*((char *)opt[66].data + opt[66].len) = 0;
    109 			} else {
    110 				err |= ENOMEM;
    111 			}
    112 		}
    113 	}
    114 
    115 	return err;
    116 }
    117