A TempleOS distro for heretics
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.

276 lines
6.6 KiB

2 years ago
// 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;
}