mirror of https://github.com/minexew/Shrine.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
275 lines
6.6 KiB
275 lines
6.6 KiB
// vim: set ft=c: |
|
|
|
#include "::/Adam/Net/Socket" |
|
|
|
#define BOOTREQUEST 0x01 |
|
#define BOOTREPLY 0x02 |
|
|
|
#define HTYPE_ETHERNET 0x01 |
|
|
|
#define HLEN_ETHERNET 6 |
|
|
|
#define DHCP_OPTION_SUBNET_MASK 1 |
|
#define DHCP_OPTION_ROUTER 3 |
|
#define DHCP_OPTION_DNS 6 |
|
#define DHCP_OPTION_DOMAIN_NAME 15 |
|
#define DHCP_OPTION_REQUESTED_IP 50 |
|
#define DHCP_OPTION_MSGTYPE 53 |
|
#define DHCP_OPTION_SERVER_ID 54 |
|
#define DHCP_OPTION_PARAMLIST 55 |
|
|
|
#define DHCP_COOKIE 0x63825363 |
|
#define DHCP_MSGTYPE_DISCOVER 0x01 |
|
#define DHCP_MSGTYPE_OFFER 0x02 |
|
#define DHCP_MSGTYPE_REQUEST 0x03 |
|
#define DHCP_MSGTYPE_ACK 0x05 |
|
|
|
class CDhcpHeader { |
|
U8 op; |
|
U8 htype; |
|
U8 hlen; |
|
U8 hops; |
|
U32 xid; |
|
U16 secs; |
|
U16 flags; |
|
U32 ciaddr; |
|
U32 yiaddr; |
|
U32 siaddr; |
|
U32 giaddr; |
|
U8 chaddr[16]; |
|
U8 sname[64]; |
|
U8 file[128]; |
|
}; |
|
|
|
class CDhcpDiscoverOptions { |
|
U32 cookie; |
|
// DHCP Message Type |
|
U8 dmt_type; |
|
U8 dmt_length; |
|
U8 dmt; |
|
// DHCP Parameter Request List |
|
U8 prl_type; |
|
U8 prl_length; |
|
U8 prl[4]; |
|
|
|
U8 end; |
|
}; |
|
|
|
class CDhcpRequestOptions { |
|
U32 cookie; |
|
// DHCP Message Type |
|
U8 dmt_type; |
|
U8 dmt_length; |
|
U8 dmt; |
|
// DHCP Requested IP |
|
U8 requested_ip_type; |
|
U8 requested_ip_length; |
|
U32 requested_ip; |
|
// DHCP Server Identifier |
|
U8 server_id_type; |
|
U8 server_id_length; |
|
U32 server_id; |
|
|
|
U8 end; |
|
}; |
|
|
|
U32 DhcpBeginTransaction() { |
|
return RandU32(); |
|
} |
|
|
|
I64 DhcpSendDiscover(U32 xid) { |
|
U8* frame; |
|
I64 index = UdpPacketAlloc(&frame, 0x00000000, 68, 0xffffffff, 67, |
|
sizeof(CDhcpHeader) + sizeof(CDhcpDiscoverOptions)); |
|
|
|
if (index < 0) |
|
return index; |
|
|
|
CDhcpHeader* dhcp = frame; |
|
MemSet(dhcp, 0, sizeof(CDhcpHeader)); |
|
dhcp->op = BOOTREQUEST; |
|
dhcp->htype = HTYPE_ETHERNET; |
|
dhcp->hlen = HLEN_ETHERNET; |
|
dhcp->hops = 0; |
|
dhcp->xid = htonl(xid); |
|
dhcp->secs = 0; |
|
dhcp->flags = htons(0x8000); |
|
dhcp->ciaddr = 0; |
|
dhcp->yiaddr = 0; |
|
dhcp->siaddr = 0; |
|
dhcp->giaddr = 0; |
|
MemCpy(dhcp->chaddr, EthernetGetAddress(), 6); |
|
|
|
CDhcpDiscoverOptions* opts = frame + sizeof(CDhcpHeader); |
|
opts->cookie = htonl(DHCP_COOKIE); |
|
opts->dmt_type = DHCP_OPTION_MSGTYPE; |
|
opts->dmt_length = 1; |
|
opts->dmt = DHCP_MSGTYPE_DISCOVER; |
|
opts->prl_type = DHCP_OPTION_PARAMLIST; |
|
opts->prl_length = 4; |
|
opts->prl[0] = DHCP_OPTION_SUBNET_MASK; |
|
opts->prl[1] = DHCP_OPTION_ROUTER; |
|
opts->prl[2] = DHCP_OPTION_DNS; |
|
opts->prl[3] = DHCP_OPTION_DOMAIN_NAME; |
|
opts->end = 0xff; |
|
|
|
return UdpPacketFinish(index); |
|
} |
|
|
|
I64 DhcpSendRequest(U32 xid, U32 requested_ip, U32 siaddr) { |
|
U8* frame; |
|
I64 index = UdpPacketAlloc(&frame, 0x00000000, 68, 0xffffffff, 67, |
|
sizeof(CDhcpHeader) + sizeof(CDhcpRequestOptions)); |
|
|
|
if (index < 0) |
|
return index; |
|
|
|
CDhcpHeader* dhcp = frame; |
|
MemSet(dhcp, 0, sizeof(CDhcpHeader)); |
|
dhcp->op = BOOTREQUEST; |
|
dhcp->htype = HTYPE_ETHERNET; |
|
dhcp->hlen = HLEN_ETHERNET; |
|
dhcp->hops = 0; |
|
dhcp->xid = htonl(xid); |
|
dhcp->secs = 0; |
|
dhcp->flags = htons(0x0000); |
|
dhcp->ciaddr = 0; |
|
dhcp->yiaddr = 0; |
|
dhcp->siaddr = htonl(siaddr); |
|
dhcp->giaddr = 0; |
|
MemCpy(dhcp->chaddr, EthernetGetAddress(), 6); |
|
|
|
CDhcpRequestOptions* opts = frame + sizeof(CDhcpHeader); |
|
opts->cookie = htonl(DHCP_COOKIE); |
|
opts->dmt_type = DHCP_OPTION_MSGTYPE; |
|
opts->dmt_length = 1; |
|
opts->dmt = DHCP_MSGTYPE_REQUEST; |
|
opts->requested_ip_type = DHCP_OPTION_REQUESTED_IP; |
|
opts->requested_ip_length = 4; |
|
opts->requested_ip = htonl(requested_ip); |
|
opts->server_id_type = DHCP_OPTION_SERVER_ID; |
|
opts->server_id_length = 4; |
|
opts->server_id = htonl(siaddr); |
|
opts->end = 0xff; |
|
|
|
return UdpPacketFinish(index); |
|
} |
|
|
|
I64 DhcpParseBegin(U8** data_inout, I64* length_inout, CDhcpHeader** hdr_out) { |
|
U8* data = *data_inout; |
|
I64 length = *length_inout; |
|
|
|
if (length < sizeof(CDhcpHeader) + 4) { |
|
//"DhcpParseBegin: too short\n"; |
|
return -1; |
|
} |
|
|
|
U32* p_cookie = data + sizeof(CDhcpHeader); |
|
|
|
if (ntohl(*p_cookie) != DHCP_COOKIE) { |
|
//"DhcpParseBegin: cookie %08Xh != %08Xh\n", ntohl(*p_cookie), DHCP_COOKIE; |
|
return -1; |
|
} |
|
|
|
*hdr_out = data; |
|
*data_inout = data + (sizeof(CDhcpHeader) + 4); |
|
*length_inout = length - (sizeof(CDhcpHeader) + 4); |
|
return 0; |
|
} |
|
|
|
I64 DhcpParseOption(U8** data_inout, I64* length_inout, U8* type_out, U8* value_length_out, U8** value_out) { |
|
U8* data = *data_inout; |
|
I64 length = *length_inout; |
|
|
|
if (length < 2 || length < 2 + data[1]) { |
|
//"DhcpParseOption: too short\n"; |
|
return -1; |
|
} |
|
|
|
if (data[0] == 0xff) |
|
return 0; |
|
|
|
*type_out = data[0]; |
|
*value_length_out = data[1]; |
|
*value_out = data + 2; |
|
|
|
*data_inout = data + (2 + *value_length_out); |
|
*length_inout = length - (2 + *value_length_out); |
|
return data[0]; |
|
} |
|
|
|
I64 DhcpParseOffer(U32 xid, U8* data, I64 length, U32* yiaddr_out, |
|
U32* dns_ip_out, U32* router_ip_out, U32* subnet_mask_out) { |
|
CDhcpHeader* hdr; |
|
I64 error = DhcpParseBegin(&data, &length, &hdr); |
|
if (error < 0) return error; |
|
|
|
if (ntohl(hdr->xid) != xid) |
|
return -1; |
|
|
|
Bool have_type = FALSE; |
|
Bool have_dns = FALSE; |
|
Bool have_router = FALSE; |
|
Bool have_subnet = FALSE; |
|
|
|
while (length) { |
|
U8 type, value_length; |
|
U8* value; |
|
|
|
error = DhcpParseOption(&data, &length, &type, &value_length, &value); |
|
//"%d, %02Xh, %d, %02Xh...\n", error, type, value_length, value[0]; |
|
if (error < 0) return error; |
|
if (error == 0) break; |
|
|
|
if (type == DHCP_OPTION_MSGTYPE && value_length == 1 && value[0] == DHCP_MSGTYPE_OFFER) |
|
have_type = TRUE; |
|
|
|
if (type == DHCP_OPTION_DNS && value_length == 4) { |
|
*dns_ip_out = ntohl(*(value(U32*))); |
|
have_dns = TRUE; |
|
} |
|
|
|
if (type == DHCP_OPTION_ROUTER && value_length == 4) { |
|
*router_ip_out = ntohl(*(value(U32*))); |
|
have_router = TRUE; |
|
} |
|
|
|
if (type == DHCP_OPTION_SUBNET_MASK && value_length == 4) { |
|
*subnet_mask_out = ntohl(*(value(U32*))); |
|
have_subnet = TRUE; |
|
} |
|
} |
|
|
|
//"DhcpParseOffer: end %d %d %d %d\n", have_type, have_dns, have_subnet, have_router; |
|
|
|
if (have_type && have_dns && have_subnet && have_router) { |
|
*yiaddr_out = ntohl(hdr->yiaddr); |
|
return 0; |
|
} |
|
else |
|
return -1; |
|
} |
|
|
|
I64 DhcpParseAck(U32 xid, U8* data, I64 length) { |
|
CDhcpHeader* hdr; |
|
I64 error = DhcpParseBegin(&data, &length, &hdr); |
|
if (error < 0) return error; |
|
|
|
if (ntohl(hdr->xid) != xid) |
|
return -1; |
|
|
|
while (length) { |
|
U8 type, value_length; |
|
U8* value; |
|
|
|
error = DhcpParseOption(&data, &length, &type, &value_length, &value); |
|
//"%d, %02Xh, %d, %02Xh...\n", error, type, value_length, value[0]; |
|
if (error < 0) return error; |
|
if (error == 0) break; |
|
|
|
if (type == DHCP_OPTION_MSGTYPE && value_length == 1 && value[0] == DHCP_MSGTYPE_ACK) |
|
return 0; |
|
} |
|
|
|
return -1; |
|
}
|
|
|