1/*
2 * Copyright 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * dhcpcd_test.cpp - unit tests for dhcpcd
17 */
18
19#include <stdint.h>
20#include <stdlib.h>
21#include <string>
22
23#include <gtest/gtest.h>
24
25// For convenience.
26#define ARRAYSIZE(x) sizeof((x)) / sizeof((x)[0])
27
28// Regrettably, copy these defines and the dhcp_message structure in from
29// dhcp.h.  This header file is not easily included, since subsequent
30// includes use C++ reserved keywords (like "new") as structure member names.
31extern "C" {
32
33#define DHO_PAD                 0
34#define DHO_DNSDOMAIN           15
35
36/* Max MTU - defines dhcp option length */
37#define MTU_MAX             1500
38
39/* Sizes for DHCP options */
40#define DHCP_CHADDR_LEN         16
41#define SERVERNAME_LEN          64
42#define BOOTFILE_LEN            128
43#define DHCP_UDP_LEN            (14 + 20 + 8)
44#define DHCP_FIXED_LEN          (DHCP_UDP_LEN + 226)
45#define DHCP_OPTION_LEN         (MTU_MAX - DHCP_FIXED_LEN)
46
47/* Some crappy DHCP servers require the BOOTP minimum length */
48#define BOOTP_MESSAGE_LENTH_MIN 300
49
50struct dhcp_message {
51        uint8_t op;           /* message type */
52        uint8_t hwtype;       /* hardware address type */
53        uint8_t hwlen;        /* hardware address length */
54        uint8_t hwopcount;    /* should be zero in client message */
55        uint32_t xid;            /* transaction id */
56        uint16_t secs;           /* elapsed time in sec. from boot */
57        uint16_t flags;
58        uint32_t ciaddr;         /* (previously allocated) client IP */
59        uint32_t yiaddr;         /* 'your' client IP address */
60        uint32_t siaddr;         /* should be zero in client's messages */
61        uint32_t giaddr;         /* should be zero in client's messages */
62        uint8_t chaddr[DHCP_CHADDR_LEN];  /* client's hardware address */
63        uint8_t servername[SERVERNAME_LEN];    /* server host name */
64        uint8_t bootfile[BOOTFILE_LEN];    /* boot file name */
65        uint32_t cookie;
66        uint8_t options[DHCP_OPTION_LEN]; /* message options - cookie */
67} _packed;
68
69char * get_option_string(const struct dhcp_message *dhcp, uint8_t option);
70
71}
72
73
74static const char kOptionString[] = "hostname";
75
76class DhcpcdGetOptionTest : public ::testing::Test {
77 protected:
78  virtual void SetUp() {
79    memset(dhcpmsgs, 0, ARRAYSIZE(dhcpmsgs) * sizeof(struct dhcp_message));
80    // Technically redundant.
81    memset(&(dhcpmsgs[0].options), DHO_PAD, sizeof(dhcpmsgs[0].options));
82
83    type_index = 0;
84    length_index = 0;
85    value_index = 0;
86  }
87
88  void PopulateTLV() {
89    // May very well write off the end of the first struct dhcp_message,
90    // by design.
91    length_index = type_index + 1;
92    value_index = length_index + 1;
93    dhcpmsgs[0].options[type_index] = DHO_DNSDOMAIN;
94    dhcpmsgs[0].options[length_index] = strlen(kOptionString);
95    memcpy(&(dhcpmsgs[0].options[value_index]),
96           kOptionString, strlen(kOptionString));
97  }
98
99  struct dhcp_message dhcpmsgs[2];
100  size_t type_index;
101  size_t length_index;
102  size_t value_index;
103};
104
105TEST_F(DhcpcdGetOptionTest, OptionNotPresent) {
106  // An entire option block of padding (all zeros).
107  EXPECT_EQ(NULL, ::get_option_string(&(dhcpmsgs[0]), DHO_DNSDOMAIN));
108}
109
110TEST_F(DhcpcdGetOptionTest, TypeIsOffTheEnd) {
111  type_index = sizeof(dhcpmsgs[0].options);
112  PopulateTLV();
113  EXPECT_EQ(NULL, ::get_option_string(&(dhcpmsgs[0]), DHO_DNSDOMAIN));
114}
115
116TEST_F(DhcpcdGetOptionTest, LengthIsOffTheEnd) {
117  type_index = sizeof(dhcpmsgs[0].options) - 1;
118  PopulateTLV();
119  EXPECT_EQ(NULL, ::get_option_string(&(dhcpmsgs[0]), DHO_DNSDOMAIN));
120}
121
122TEST_F(DhcpcdGetOptionTest, ValueIsOffTheEnd) {
123  type_index = sizeof(dhcpmsgs[0].options) - 2;
124  PopulateTLV();
125  EXPECT_EQ(NULL, ::get_option_string(&(dhcpmsgs[0]), DHO_DNSDOMAIN));
126}
127
128TEST_F(DhcpcdGetOptionTest, InsufficientSpaceForValue) {
129  type_index = sizeof(dhcpmsgs[0].options) - 6;
130  PopulateTLV();
131  char* value = ::get_option_string(&(dhcpmsgs[0]), DHO_DNSDOMAIN);
132  EXPECT_TRUE(NULL != value);
133  EXPECT_EQ("host", ::std::string(value));
134  free(value);
135}
136
137TEST_F(DhcpcdGetOptionTest, InsufficientSpaceForContinuedValue) {
138  type_index = sizeof(dhcpmsgs[0].options) - 16;
139  PopulateTLV();
140  type_index = sizeof(dhcpmsgs[0].options) - 6;
141  PopulateTLV();
142  char* value = ::get_option_string(&(dhcpmsgs[0]), DHO_DNSDOMAIN);
143  EXPECT_TRUE(NULL != value);
144  EXPECT_EQ("hostnamehost", ::std::string(value));
145  free(value);
146}
147