001 // vim: set ft=c: 002 003 #include "::/Adam/Net/Socket" 004 005 #define BOOTREQUEST 0x01 006 #define BOOTREPLY 0x02 007 008 #define HTYPE_ETHERNET 0x01 009 010 #define HLEN_ETHERNET 6 011 012 #define DHCP_OPTION_SUBNET_MASK 1 013 #define DHCP_OPTION_ROUTER 3 014 #define DHCP_OPTION_DNS 6 015 #define DHCP_OPTION_DOMAIN_NAME 15 016 #define DHCP_OPTION_REQUESTED_IP 50 017 #define DHCP_OPTION_MSGTYPE 53 018 #define DHCP_OPTION_SERVER_ID 54 019 #define DHCP_OPTION_PARAMLIST 55 020 021 #define DHCP_COOKIE 0x63825363 022 #define DHCP_MSGTYPE_DISCOVER 0x01 023 #define DHCP_MSGTYPE_OFFER 0x02 024 #define DHCP_MSGTYPE_REQUEST 0x03 025 #define DHCP_MSGTYPE_ACK 0x05 026 027 class CDhcpHeader { 028 U8 op; 029 U8 htype; 030 U8 hlen; 031 U8 hops; 032 U32 xid; 033 U16 secs; 034 U16 flags; 035 U32 ciaddr; 036 U32 yiaddr; 037 U32 siaddr; 038 U32 giaddr; 039 U8 chaddr[16]; 040 U8 sname[64]; 041 U8 file[128]; 042 }; 043 044 class CDhcpDiscoverOptions { 045 U32 cookie; 046 // DHCP Message Type 047 U8 dmt_type; 048 U8 dmt_length; 049 U8 dmt; 050 // DHCP Parameter Request List 051 U8 prl_type; 052 U8 prl_length; 053 U8 prl[4]; 054 055 U8 end; 056 }; 057 058 class CDhcpRequestOptions { 059 U32 cookie; 060 // DHCP Message Type 061 U8 dmt_type; 062 U8 dmt_length; 063 U8 dmt; 064 // DHCP Requested IP 065 U8 requested_ip_type; 066 U8 requested_ip_length; 067 U32 requested_ip; 068 // DHCP Server Identifier 069 U8 server_id_type; 070 U8 server_id_length; 071 U32 server_id; 072 073 U8 end; 074 }; 075 076 U32 DhcpBeginTransaction() { 077 return RandU32(); 078 } 079 080 I64 DhcpSendDiscover(U32 xid) { 081 U8* frame; 082 I64 index = UdpPacketAlloc(&frame, 0x00000000, 68, 0xffffffff, 67, 083 sizeof(CDhcpHeader) + sizeof(CDhcpDiscoverOptions)); 084 085 if (index < 0) 086 return index; 087 088 CDhcpHeader* dhcp = frame; 089 MemSet(dhcp, 0, sizeof(CDhcpHeader)); 090 dhcp->op = BOOTREQUEST; 091 dhcp->htype = HTYPE_ETHERNET; 092 dhcp->hlen = HLEN_ETHERNET; 093 dhcp->hops = 0; 094 dhcp->xid = htonl(xid); 095 dhcp->secs = 0; 096 dhcp->flags = htons(0x8000); 097 dhcp->ciaddr = 0; 098 dhcp->yiaddr = 0; 099 dhcp->siaddr = 0; 100 dhcp->giaddr = 0; 101 MemCpy(dhcp->chaddr, EthernetGetAddress(), 6); 102 103 CDhcpDiscoverOptions* opts = frame + sizeof(CDhcpHeader); 104 opts->cookie = htonl(DHCP_COOKIE); 105 opts->dmt_type = DHCP_OPTION_MSGTYPE; 106 opts->dmt_length = 1; 107 opts->dmt = DHCP_MSGTYPE_DISCOVER; 108 opts->prl_type = DHCP_OPTION_PARAMLIST; 109 opts->prl_length = 4; 110 opts->prl[0] = DHCP_OPTION_SUBNET_MASK; 111 opts->prl[1] = DHCP_OPTION_ROUTER; 112 opts->prl[2] = DHCP_OPTION_DNS; 113 opts->prl[3] = DHCP_OPTION_DOMAIN_NAME; 114 opts->end = 0xff; 115 116 return UdpPacketFinish(index); 117 } 118 119 I64 DhcpSendRequest(U32 xid, U32 requested_ip, U32 siaddr) { 120 U8* frame; 121 I64 index = UdpPacketAlloc(&frame, 0x00000000, 68, 0xffffffff, 67, 122 sizeof(CDhcpHeader) + sizeof(CDhcpRequestOptions)); 123 124 if (index < 0) 125 return index; 126 127 CDhcpHeader* dhcp = frame; 128 MemSet(dhcp, 0, sizeof(CDhcpHeader)); 129 dhcp->op = BOOTREQUEST; 130 dhcp->htype = HTYPE_ETHERNET; 131 dhcp->hlen = HLEN_ETHERNET; 132 dhcp->hops = 0; 133 dhcp->xid = htonl(xid); 134 dhcp->secs = 0; 135 dhcp->flags = htons(0x0000); 136 dhcp->ciaddr = 0; 137 dhcp->yiaddr = 0; 138 dhcp->siaddr = htonl(siaddr); 139 dhcp->giaddr = 0; 140 MemCpy(dhcp->chaddr, EthernetGetAddress(), 6); 141 142 CDhcpRequestOptions* opts = frame + sizeof(CDhcpHeader); 143 opts->cookie = htonl(DHCP_COOKIE); 144 opts->dmt_type = DHCP_OPTION_MSGTYPE; 145 opts->dmt_length = 1; 146 opts->dmt = DHCP_MSGTYPE_REQUEST; 147 opts->requested_ip_type = DHCP_OPTION_REQUESTED_IP; 148 opts->requested_ip_length = 4; 149 opts->requested_ip = htonl(requested_ip); 150 opts->server_id_type = DHCP_OPTION_SERVER_ID; 151 opts->server_id_length = 4; 152 opts->server_id = htonl(siaddr); 153 opts->end = 0xff; 154 155 return UdpPacketFinish(index); 156 } 157 158 I64 DhcpParseBegin(U8** data_inout, I64* length_inout, CDhcpHeader** hdr_out) { 159 U8* data = *data_inout; 160 I64 length = *length_inout; 161 162 if (length < sizeof(CDhcpHeader) + 4) { 163 //"DhcpParseBegin: too short\n"; 164 return -1; 165 } 166 167 U32* p_cookie = data + sizeof(CDhcpHeader); 168 169 if (ntohl(*p_cookie) != DHCP_COOKIE) { 170 //"DhcpParseBegin: cookie %08Xh != %08Xh\n", ntohl(*p_cookie), DHCP_COOKIE; 171 return -1; 172 } 173 174 *hdr_out = data; 175 *data_inout = data + (sizeof(CDhcpHeader) + 4); 176 *length_inout = length - (sizeof(CDhcpHeader) + 4); 177 return 0; 178 } 179 180 I64 DhcpParseOption(U8** data_inout, I64* length_inout, U8* type_out, U8* value_length_out, U8** value_out) { 181 U8* data = *data_inout; 182 I64 length = *length_inout; 183 184 if (length < 2 || length < 2 + data[1]) { 185 //"DhcpParseOption: too short\n"; 186 return -1; 187 } 188 189 if (data[0] == 0xff) 190 return 0; 191 192 *type_out = data[0]; 193 *value_length_out = data[1]; 194 *value_out = data + 2; 195 196 *data_inout = data + (2 + *value_length_out); 197 *length_inout = length - (2 + *value_length_out); 198 return data[0]; 199 } 200 201 I64 DhcpParseOffer(U32 xid, U8* data, I64 length, U32* yiaddr_out, 202 U32* dns_ip_out, U32* router_ip_out, U32* subnet_mask_out) { 203 CDhcpHeader* hdr; 204 I64 error = DhcpParseBegin(&data, &length, &hdr); 205 if (error < 0) return error; 206 207 if (ntohl(hdr->xid) != xid) 208 return -1; 209 210 Bool have_type = FALSE; 211 Bool have_dns = FALSE; 212 Bool have_router = FALSE; 213 Bool have_subnet = FALSE; 214 215 while (length) { 216 U8 type, value_length; 217 U8* value; 218 219 error = DhcpParseOption(&data, &length, &type, &value_length, &value); 220 //"%d, %02Xh, %d, %02Xh...\n", error, type, value_length, value[0]; 221 if (error < 0) return error; 222 if (error == 0) break; 223 224 if (type == DHCP_OPTION_MSGTYPE && value_length == 1 && value[0] == DHCP_MSGTYPE_OFFER) 225 have_type = TRUE; 226 227 if (type == DHCP_OPTION_DNS && value_length == 4) { 228 *dns_ip_out = ntohl(*(value(U32*))); 229 have_dns = TRUE; 230 } 231 232 if (type == DHCP_OPTION_ROUTER && value_length == 4) { 233 *router_ip_out = ntohl(*(value(U32*))); 234 have_router = TRUE; 235 } 236 237 if (type == DHCP_OPTION_SUBNET_MASK && value_length == 4) { 238 *subnet_mask_out = ntohl(*(value(U32*))); 239 have_subnet = TRUE; 240 } 241 } 242 243 //"DhcpParseOffer: end %d %d %d %d\n", have_type, have_dns, have_subnet, have_router; 244 245 if (have_type && have_dns && have_subnet && have_router) { 246 *yiaddr_out = ntohl(hdr->yiaddr); 247 return 0; 248 } 249 else 250 return -1; 251 } 252 253 I64 DhcpParseAck(U32 xid, U8* data, I64 length) { 254 CDhcpHeader* hdr; 255 I64 error = DhcpParseBegin(&data, &length, &hdr); 256 if (error < 0) return error; 257 258 if (ntohl(hdr->xid) != xid) 259 return -1; 260 261 while (length) { 262 U8 type, value_length; 263 U8* value; 264 265 error = DhcpParseOption(&data, &length, &type, &value_length, &value); 266 //"%d, %02Xh, %d, %02Xh...\n", error, type, value_length, value[0]; 267 if (error < 0) return error; 268 if (error == 0) break; 269 270 if (type == DHCP_OPTION_MSGTYPE && value_length == 1 && value[0] == DHCP_MSGTYPE_ACK) 271 return 0; 272 } 273 274 return -1; 275 }