Browse Source

Hello Shrine

v6
minexew 2 years ago
parent
commit
e45d1535db
  1. 2
      Adam/ADefine.HC
  2. 410
      Adam/HwSupp/PCNet.HC
  3. 119
      Adam/HwSupp/Pci.HC
  4. 2
      Adam/MakeAdam.HC
  5. 125
      Adam/Net/Arp.HC
  6. 275
      Adam/Net/Dhcp.HC
  7. 538
      Adam/Net/Dns.HC
  8. 55
      Adam/Net/Ethernet.HC
  9. 197
      Adam/Net/Http.HC
  10. 252
      Adam/Net/IPv4.HC
  11. 52
      Adam/Net/Icmp.HC
  12. 45
      Adam/Net/MakeSnailNet.HC
  13. 264
      Adam/Net/NativeSocket.HC
  14. 79
      Adam/Net/NetFifo.HC
  15. 38
      Adam/Net/NetHandlerTask.HC
  16. 139
      Adam/Net/Netcfg.HC
  17. 148
      Adam/Net/SnailLib.HC
  18. 29
      Adam/Net/Socket.HC
  19. 920
      Adam/Net/Tcp.HC
  20. 243
      Adam/Net/Udp.HC
  21. 91
      Adam/Net/Url.HC
  22. 73
      Adam/Net/UrlParse.HC
  23. 522
      Apps/Lsh.HC
  24. 98
      Apps/Mfa.HC
  25. 463
      Apps/Pkg.HC
  26. 25
      Apps/Wget.HC
  27. 49
      Demo/Network/TcpEchoClient.HC
  28. 54
      Demo/Network/TcpEchoServer.HC
  29. 50
      Demo/Network/UdpListen.HC
  30. 32
      Demo/Network/UdpSend.HC
  31. 15
      Demo/Network/tcp-echo-client.py
  32. 22
      Demo/Network/tcp-echo-server.py
  33. 14
      Demo/Network/udp-listen.py
  34. 11
      Demo/Network/udp-send.py
  35. 12
      Doc/Comm.HC
  36. 4
      Doc/Start.DD
  37. 2
      Kernel/KGlbls.HC
  38. 4
      Misc/DoDistro.HC
  39. 8
      Misc/PalConEmu.HC
  40. 8
      Misc/PalMonokai.HC
  41. 8
      Misc/PalUbuntu.HC
  42. 6
      Once.HC
  43. 134
      mfa.py
  44. 90
      snail.py

2
Adam/ADefine.HC

@ -4,7 +4,7 @@ U0 LoadDocDefines()
{
CBinFile *bfh=mem_boot_base-sizeof(CBinFile);
DefinePrint("DD_OS_NAME_VERSION","TempleOS V%0.2f",sys_os_version);
DefinePrint("DD_OS_NAME_VERSION","Shrine %0.2f",sys_os_version);
DefinePrint("DD_TEMPLEOS_AGE","%0.1f",
(Now-Str2Date("8/1/2003"))/ToF64(1<<32)/CDATE_YEAR_DAYS);

410
Adam/HwSupp/PCNet.HC

@ -0,0 +1,410 @@
// vim: set ft=c:
#include "::/Adam/HwSupp/Pci"
// Significantly based on http://wiki.osdev.org/AMD_PCNET
#define PCNET_DEVICE_ID 0x2000
#define PCNET_VENDOR_ID 0x1022
#define PCNET_COMMAND_IOEN (1<<0)
#define PCNET_COMMAND_MEMEN (1<<1)
#define PCNET_COMMAND_BMEN (1<<2)
#define PCNET_COMMAND_SCYCEN (1<<3)
#define PCNET_COMMAND_MWIEN (1<<4)
#define PCNET_COMMAND_VGASNOOP (1<<5)
#define PCNET_COMMAND_PERREN (1<<6)
#define PCNET_COMMAND_ADSTEP (1<<7)
#define PCNET_COMMAND_SERREN (1<<8)
#define PCNET_COMMAND_FBTBEN (1<<9)
#define PCNET_STATUS_FBTBC (1<<7)
#define PCNET_STATUS_DATAPERR (1<<8)
#define PCNET_STATUS_DEVSEL_BIT 9
#define PCNET_STATUS_STABORT (1<<11)
#define PCNET_STATUS_RTABORT (1<<12)
#define PCNET_STATUS_RMABORT (1<<13)
#define PCNET_STATUS_SERR (1<<14)
#define PCNET_STATUS_PERR (1<<15)
#define PCNET_WD_RESET 0x14
#define PCNET_DW_RDP 0x10
#define PCNET_DW_RAP 0x14
#define PCNET_DW_RESET 0x18
#define PCNET_CSR0_INIT (1<<0)
#define PCNET_CSR0_STRT (1<<1)
#define PCNET_CSR0_STOP (1<<2)
#define PCNET_CSR0_IENA (1<<6)
#define PCNET_CSR0_IDON (1<<8)
#define PCNET_CSR0_TINT (1<<9)
#define PCNET_CSR0_RINT (1<<10)
#define PCNET_CSR3_BSWP (1<<2)
#define PCNET_CSR3_IDONM (1<<8)
#define PCNET_CSR3_TINTM (1<<9)
#define PCNET_CSR3_RINTM (1<<10)
#define PCNET_CSR4_TXSTRT (1<<3)
#define PCNET_CSR4_ASTRP_RCV (1<<10)
#define PCNET_CSR4_APAD_XMT (1<<11)
#define PCNET_TXFIFO_FULL (-1)
// TODO: this should be configurable
#define PCNET_NUM_RX_LOG2 5
#define PCNET_NUM_TX_LOG2 3
#define rx_buffer_count (1<<PCNET_NUM_RX_LOG2)
#define tx_buffer_count (1<<PCNET_NUM_TX_LOG2)
// Including SrcAddr, DstAddr, EtherType
// Where does this even belong? NetFifo defines for now.
//#define ETHERNET_FRAME_SIZE 1548
#define PCNET_DE_SIZE 16
class CPCNetBufferSetup {
U16 mode;
U8 rlen;
U8 tlen;
U8 mac[6];
U16 reserved;
U8 ladr[8];
U32 rxbuf;
U32 txbuf;
};
// Card I/O base
I64 pcnet_iob = 0;
U8 my_mac[6];
// Current Rx/Tx buffer
I64 rx_buffer_ptr = 0;
I64 tx_buffer_ptr = 0;
// Rx/Tx descriptor ring buffers, PCNET_DE_SIZE each
// _phys are uncached
U8* rdes_phys;
U8* tdes_phys;
U8* rdes;
U8* tdes;
U32 rx_buffers; // physical address of actual receive buffers (< 4 GiB)
U32 tx_buffers; // physical address of actual transmit buffers (< 4 GiB)
static U0 writeRAP32(U32 val) {
OutU32(pcnet_iob + PCNET_DW_RAP, val);
}
static U32 readCSR32(U32 csr_no) {
writeRAP32(csr_no);
return InU32(pcnet_iob + PCNET_DW_RDP);
}
static U0 writeCSR32(U32 csr_no, U32 val) {
writeRAP32(csr_no);
OutU32(pcnet_iob + PCNET_DW_RDP, val);
}
static U0 PCNetReset() {
InU32(pcnet_iob + PCNET_DW_RESET);
InU16(pcnet_iob + PCNET_WD_RESET);
}
// does the driver own the particular buffer?
static I64 driverOwns(U8 *des, I64 idx)
{
return (des[PCNET_DE_SIZE * idx + 7] & 0x80) == 0;
}
static U0 PCNetReadEeprom(I64 offset, U8* buffer, I64 count) {
while (count) {
*buffer = InU32(pcnet_iob + offset);
offset++;
buffer++;
count--;
}
}
static I64 PCNetRxPacket(U8** buffer_out, U16* length_out) {
I64 index = rx_buffer_ptr;
// packet length is given by bytes 8 and 9 of the descriptor
// (no need to negate it unlike BCNT above)
U16* p16 = &rdes[index * PCNET_DE_SIZE + 8];
U16 length = *p16;
// increment rx_buffer_ptr;
rx_buffer_ptr = (rx_buffer_ptr + 1) & (rx_buffer_count - 1);
*buffer_out = rx_buffers + index * ETHERNET_FRAME_SIZE;
*length_out = length;
return index;
}
static I64 PCNetReleaseRxPacket(I64 index) {
rdes[index * PCNET_DE_SIZE + 7] = 0x80;
return 0;
}
static I64 PCNetAllocTxPacket(U8** buffer_out, I64 length, I64 flags) {
// FIXME: validate length
flags = flags;
if (!driverOwns(tdes, tx_buffer_ptr)) {
return PCNET_TXFIFO_FULL;
}
I64 index = tx_buffer_ptr;
// set the STP bit in the descriptor entry (signals this is the first
// frame in a split packet - we only support single frames)
tdes[index * PCNET_DE_SIZE + 7] |= 0x2;
// similarly, set the ENP bit to state this is also the end of a packet
tdes[index * PCNET_DE_SIZE + 7] |= 0x1;
// set the BCNT member to be 0xf000 OR'd with the first 12 bits of the
// two's complement of the length of the packet
U16 bcnt = (-length);
bcnt &= 0xfff;
bcnt |= 0xf000;
U16* p16 = &tdes[index * PCNET_DE_SIZE + 4];
*p16 = bcnt;
tx_buffer_ptr = (tx_buffer_ptr + 1) & (tx_buffer_count - 1);
*buffer_out = tx_buffers + index * ETHERNET_FRAME_SIZE;
return index;
}
static I64 PCNetFinishTxPacket(I64 index) {
// finally, flip the ownership bit back to the card
tdes[index * PCNET_DE_SIZE + 7] |= 0x80;
return 0;
}
static U0 PCNetInitDE(U32 buf_addr, U8 *des, I64 idx, I64 is_tx) {
MemSet(&des[idx * PCNET_DE_SIZE], PCNET_DE_SIZE, 0);
// first 4 bytes are the physical address of the actual buffer
U32* p32 = &des[idx * PCNET_DE_SIZE];
*p32 = buf_addr + idx * ETHERNET_FRAME_SIZE;
// next 2 bytes are 0xf000 OR'd with the first 12 bits of the 2s complement of the length
U16 bcnt = (-ETHERNET_FRAME_SIZE);
bcnt &= 0x0fff;
bcnt |= 0xf000;
U16* p16 = &des[idx * PCNET_DE_SIZE + 4];
*p16 = bcnt;
// finally, set ownership bit - transmit buffers are owned by us, receive buffers by the card
if (!is_tx)
des[idx * PCNET_DE_SIZE + 7] = 0x80;
}
static I64 PCNetAllocBuffers() {
I64 i;
I64 rdes_size = PCNET_DE_SIZE * rx_buffer_count;
I64 tdes_size = PCNET_DE_SIZE * tx_buffer_count;
rdes_phys = MAlloc(rdes_size, Fs->code_heap);
tdes_phys = MAlloc(tdes_size, Fs->code_heap);
if (rdes_phys + rdes_size > 0x100000000 || tdes_phys + tdes_size > 0x100000000) {
"$FG,4$PCNetAllocBuffers: rdes_phys=%08Xh tdes_phys=%08Xh\n$FG$", rdes_phys, tdes_phys;
return -1;
}
rdes = rdes_phys + dev.uncached_alias;
tdes = tdes_phys + dev.uncached_alias;
I64 rx_buffers_size = ETHERNET_FRAME_SIZE * rx_buffer_count;
I64 tx_buffers_size = ETHERNET_FRAME_SIZE * tx_buffer_count;
// TODO: shouldn't these be uncached as well?
rx_buffers = MAlloc(rx_buffers_size, Fs->code_heap);
tx_buffers = MAlloc(tx_buffers_size, Fs->code_heap);
if (rx_buffers + rx_buffers_size > 0x100000000 || tx_buffers + tx_buffers_size > 0x100000000) {
"$FG,4$PCNetAllocBuffers: rx_buffers=%08Xh tx_buffers=%08Xh\n$FG$", rx_buffers, tx_buffers;
return -1;
}
for (i = 0; i < rx_buffer_count; i++)
PCNetInitDE(rx_buffers, rdes, i, FALSE);
for (i = 0; i < tx_buffer_count; i++)
PCNetInitDE(tx_buffers, tdes, i, TRUE);
//"rdes: %08Xh\ttdes: %08X\n", rdes, tdes;
//"rbuf: %08Xh\ttbuf: %08X\n", rx_buffers, tx_buffers;
return 0;
}
interrupt U0 PCNetIrq() {
U32 csr0 = readCSR32(0);
while (driverOwns(rdes, rx_buffer_ptr)) {
//if (csr0 & PCNET_CSR0_RINT) {
//"Int reason %08X\n", csr0;
U8* buffer;
U16 length;
I64 index = PCNetRxPacket(&buffer, &length);
if (index >= 0) {
//"[%d] Rx %d B\n", index, length;
NetFifoPushCopy(buffer, length);
PCNetReleaseRxPacket(index);
}
writeCSR32(0, csr0 | PCNET_CSR0_RINT);
}
*(dev.uncached_alias + LAPIC_EOI)(U32*) = 0;
}
static U0 PCNetInit(I64 bus, I64 dev_, I64 fun) {
CPciDevInfo info;
PciGetDevInfo(&info, bus, dev_, fun);
//PciDumpInfo(&info);
if (info.vendor_id != PCNET_VENDOR_ID || info.device_id != PCNET_DEVICE_ID)
throw;
U16 config = PCNET_COMMAND_IOEN | PCNET_COMMAND_BMEN;
PCIWriteU16(bus, dev_, fun, PCI_REG_COMMAND, config);
config = PCIReadU16(bus, dev_, fun, PCI_REG_COMMAND);
pcnet_iob = (PCIReadU32(bus, dev_, fun, PCI_REG_BAR0) & ~(0x0000001f));
//U32 membase = (PCIReadU32(bus, dev_, fun, PCI_REG_BAR1) & ~(0x0000001f));
//"PCNet iobase: %016Xh\n", pcnet_iob;
//"PCNet membase: %08Xh\n", membase;
// Reset the card to get into defined state
PCNetReset();
Sleep(1);
// Enter 32-bit mode
OutU32(pcnet_iob + PCNET_DW_RDP, 0);
// SWSTYLE
U32 csr58 = readCSR32(58);
csr58 &= 0xfff0;
csr58 |= 2;
writeCSR32(58, csr58);
PCNetReadEeprom(0, my_mac, 6);
if (PCNetAllocBuffers() < 0)
return;
U8* setup = MAlloc(sizeof(CPCNetBufferSetup), Fs->code_heap);
CPCNetBufferSetup* u_setup = setup + dev.uncached_alias;
u_setup->mode = 0; // see CSR15 in spec
u_setup->rlen = (PCNET_NUM_RX_LOG2 << 4);
u_setup->tlen = (PCNET_NUM_TX_LOG2 << 4);
MemCpy(u_setup->mac, my_mac, 6);
"PCNet MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
u_setup->mac[0], u_setup->mac[1], u_setup->mac[2], u_setup->mac[3], u_setup->mac[4], u_setup->mac[5];
u_setup->reserved = 0;
MemSet(u_setup->ladr, 0, 8);
u_setup->rxbuf = rdes_phys;
u_setup->txbuf = tdes_phys;
U32 p_setup = setup;
writeCSR32(1, p_setup & 0xffff);
writeCSR32(2, p_setup >> 16);
U32 csr3 = readCSR32(3);
csr3 &= ~PCNET_CSR3_BSWP; // disable big-endian
csr3 &= ~PCNET_CSR3_RINTM; // enable Rx interruot
csr3 |= PCNET_CSR3_IDONM; // mask-out Init Done Interrupt
csr3 |= PCNET_CSR3_TINTM; // mask-out Tx interrupt
writeCSR32(3, csr3);
U32 csr4 = readCSR32(4);
csr4 |= PCNET_CSR4_APAD_XMT; // auto pad transmit
writeCSR32(4, csr4);
// Upload configuration
writeCSR32(0, readCSR32(0) | PCNET_CSR0_INIT | PCNET_CSR0_IENA);
while (!(readCSR32(0) & PCNET_CSR0_IDON)) {
Yield;
}
// Exit config mode
U32 csr0 = readCSR32(0);
csr0 &= ~(PCNET_CSR0_INIT | PCNET_CSR0_STOP);
csr0 |= PCNET_CSR0_STRT;
writeCSR32(0, csr0);
// Init interrupt
//IntEntrySet(info.interrupt_line, &PCNetIrq, IDTET_IRQ);
IntEntrySet(0x40, &PCNetIrq, IDTET_IRQ);
IntEntrySet(0x41, &PCNetIrq, IDTET_IRQ);
IntEntrySet(0x42, &PCNetIrq, IDTET_IRQ);
IntEntrySet(0x43, &PCNetIrq, IDTET_IRQ);
PciRerouteInterrupts(0x40);
Sleep(100);
Free(setup);
}
I64 EthernetFrameAlloc(U8** buffer_out, U8* src_addr, U8* dst_addr, U16 ethertype, I64 length, I64 flags) {
U8* frame;
// APAD_XMT doesn't seem to work in VirtualBox, so we have to pad the frame ourselves
if (length < 46)
length = 46;
I64 index = PCNetAllocTxPacket(&frame, 14 + length, flags);
if (index < 0)
return index;
MemCpy(frame + 0, dst_addr, 6);
MemCpy(frame + 6, src_addr, 6);
frame[12] = (ethertype >> 8);
frame[13] = (ethertype & 0xff);
*buffer_out = frame + 14;
return index;
}
I64 EthernetFrameFinish(I64 index) {
return PCNetFinishTxPacket(index);
}
U8* EthernetGetAddress() {
return my_mac;
}
I64 EthernetInit() {
I64 b, d, f;
if (pcnet_iob != 0)
return 0;
if (PciFindByID(PCNET_VENDOR_ID, PCNET_DEVICE_ID, &b, &d, &f)) {
//"PCNet @ %d:%d:%d\n", b, d, f;
PCNetInit(b, d, f);
return 0;
}
return -1;
}

119
Adam/HwSupp/Pci.HC

@ -0,0 +1,119 @@
// vim: set ft=c:
#define PCI_REG_VENDOR_ID 0x00
#define PCI_REG_DEVICE_ID 0x02
#define PCI_REG_COMMAND 0x04
#define PCI_REG_STATUS 0x06
#define PCI_REG_REVISION_ID 0x08
#define PCI_REG_PROG_IF 0x09
#define PCI_REG_SUBCLASS 0x0a
#define PCI_REG_CLASS 0x0b
#define PCI_REG_CACHE_LINE_SIZE 0x0c
#define PCI_REG_LATENCY_TIMER 0x0d
#define PCI_REG_HEADER_TYPE 0x0e
#define PCI_REG_BIST 0x0f
#define PCI_REG_BAR0 0x10
#define PCI_REG_BAR1 0x14
#define PCI_REG_BAR2 0x18
#define PCI_REG_BAR3 0x1c
#define PCI_REG_BAR4 0x20
#define PCI_REG_BAR5 0x24
#define PCI_REG_INTERRUPT_LINE 0x3C
class CPciDevInfo {
U16 vendor_id, device_id;
U16 command, status;
U8 class_, subclass, prog_if, revision_id;
U8 cache_line_size, latency_timer, header_type, bist;
U32 bar[6];
// ...a lot of trash comes here...
U8 interrupt_line;
};
U0 PciDumpInfo(CPciDevInfo* info) {
I64 i;
"vendor_id=%04Xh\tdevice_id=%04Xh\n", info->vendor_id, info->device_id;
"command=%04Xh\tstatus=%04Xh\n", info->command, info->status;
"revision_id=%02Xh\tprog_if=%02Xh\n", info->revision_id, info->prog_if;
"subclass=%02Xh\tclass_=%02Xh\n", info->subclass, info->class_;
"cache_line_size=%02Xh\tlatency_timer=%02Xh\n", info->cache_line_size, info->latency_timer;
"header_type=%02Xh\tbist=%02Xh\n", info->header_type, info->bist;
for (i = 0; i < 6; i++)
"BAR[%d]=%08X\n", i, info->bar[i];
"interrupt_line=%02Xh\n", info->interrupt_line;
}
Bool PciFindByID(U16 vendor_id, U16 device_id, I64* bus_out, I64* dev_out, I64* fun_out) {
I64 vendor, b, d, f, timeout = 32 * 8 * 2;
if (dev.pci_head.next != &dev.pci_head)
return FALSE;
for (b = 0; b < sys_pci_busses; b++) {
for (d = 0; d < 32; d++) {
for (f = 0; f < 8; f++) {
vendor = PCIReadU16(b, d, f, PCI_REG_VENDOR_ID);
if (vendor != 0xFFFF) {
if (vendor == vendor_id && PCIReadU16(b, d, f, PCI_REG_DEVICE_ID) == device_id) {
*bus_out = b;
*dev_out = d;
*fun_out = f;
return TRUE;
}
timeout = 32 * 8 * 2;
}
else if (sys_pci_busses == 256 && --timeout <= 0) {
break;
}
}
}
}
return FALSE;
}
U0 PciGetDevInfo(CPciDevInfo* info_out, I64 bus, I64 dev, I64 fun) {
// TODO: do a bunch of PCIReadU32 in a loop instead
info_out->vendor_id = PCIReadU16(bus, dev, fun, PCI_REG_VENDOR_ID);
info_out->device_id = PCIReadU16(bus, dev, fun, PCI_REG_DEVICE_ID);
info_out->command = PCIReadU16(bus, dev, fun, PCI_REG_COMMAND);
info_out->status = PCIReadU16(bus, dev, fun, PCI_REG_STATUS);
info_out->revision_id = PCIReadU8(bus, dev, fun, PCI_REG_REVISION_ID);
info_out->prog_if = PCIReadU8(bus, dev, fun, PCI_REG_PROG_IF);
info_out->subclass = PCIReadU8(bus, dev, fun, PCI_REG_SUBCLASS);
info_out->class_ = PCIReadU8(bus, dev, fun, PCI_REG_CLASS);
info_out->cache_line_size = PCIReadU8(bus, dev, fun, PCI_REG_CACHE_LINE_SIZE);
info_out->latency_timer = PCIReadU8(bus, dev, fun, PCI_REG_LATENCY_TIMER);
info_out->header_type = PCIReadU8(bus, dev, fun, PCI_REG_HEADER_TYPE);
info_out->bist = PCIReadU8(bus, dev, fun, PCI_REG_BIST);
info_out->bar[0] = PCIReadU32(bus, dev, fun, PCI_REG_BAR0);
info_out->bar[1] = PCIReadU32(bus, dev, fun, PCI_REG_BAR1);
info_out->bar[2] = PCIReadU32(bus, dev, fun, PCI_REG_BAR2);
info_out->bar[3] = PCIReadU32(bus, dev, fun, PCI_REG_BAR3);
info_out->bar[4] = PCIReadU32(bus, dev, fun, PCI_REG_BAR4);
info_out->bar[5] = PCIReadU32(bus, dev, fun, PCI_REG_BAR5);
info_out->interrupt_line = PCIReadU8(bus, dev, fun, PCI_REG_INTERRUPT_LINE);
}
#define INT_DEST_CPU 0
U0 PciRerouteInterrupts(I64 base) {
I64 i;
U8* da = dev.uncached_alias + IOAPIC_REG;
U32* _d = dev.uncached_alias + IOAPIC_DATA;
for (i = 0; i < 4; i++) {
*da = IOREDTAB + i * 2 + 1;
*_d = dev.mp_apic_ids[INT_DEST_CPU] << 24;
*da = IOREDTAB + i * 2;
*_d = 0x4000 + base + i;
}
}

2
Adam/MakeAdam.HC

@ -30,3 +30,5 @@ LBts(&sys_run_level,RLf_DOC);
#include "AMouse"
#include "Host"
Cd("..");;
#include "::/Adam/Net/MakeSnailNet"

125
Adam/Net/Arp.HC

@ -0,0 +1,125 @@
// vim: set ft=c:
// Not a Network Layer protocol, but it is encapsulated in L2 frames, which makes it L3 for our purposes
#define ARP_REQUEST 0x01
#define ARP_REPLY 0x02
class CArpHeader {
U16 htype;
U16 ptype;
U8 hlen;
U8 plen;
U16 oper;
U8 sha[6];
U32 spa;
U8 tha[6];
U32 tpa;
};
class CArpCacheEntry {
CArpCacheEntry* next;
U32 ip;
U8 mac[6];
};
// Stored in network order
static U32 arp_my_ipv4_n = 0;
// TODO: use a Hash table
static CArpCacheEntry* arp_cache = NULL;
// IPs are in network order
I64 ArpSend(U16 oper, U8* dest_mac, U8* sender_mac, U32 sender_ip_n, U8* target_mac, U32 target_ip_n) {
U8* frame;
I64 index = EthernetFrameAlloc(&frame, sender_mac, dest_mac,
ETHERTYPE_ARP, sizeof(CArpHeader), 0);
if (index < 0)
return index;
CArpHeader* hdr = frame;
hdr->htype = htons(1);
hdr->ptype = htons(ETHERTYPE_IPV4);
hdr->hlen = 6;
hdr->plen = 4;
hdr->oper = htons(oper);
MemCpy(hdr->sha, sender_mac, 6);
hdr->spa = sender_ip_n;
MemCpy(hdr->tha, target_mac, 6);
hdr->tpa = target_ip_n;
return EthernetFrameFinish(index);
}
U0 ArpSetIPv4Address(U32 addr) {
arp_my_ipv4_n = htonl(addr);
// Broadcast our new address
ArpSend(ARP_REPLY, eth_broadcast, EthernetGetAddress(), arp_my_ipv4_n, eth_null, arp_my_ipv4_n);
}
CArpCacheEntry* ArpCacheFindByIP(U32 ip) {
CArpCacheEntry* e = arp_cache;
while (e) {
if (e->ip == ip)
return e;
e = e->next;
}
return e;
}
CArpCacheEntry* ArpCachePut(U32 ip, U8* mac) {
CArpCacheEntry* e = ArpCacheFindByIP(ip);
if (!e) {
//"ARP: add entry for %08X\n", ip;
e = MAlloc(sizeof(CArpCacheEntry));
e->next = arp_cache;
e->ip = ip;
MemCpy(e->mac, mac, 6);
arp_cache = e;
}
// FIXME: else replace!
return e;
}
I64 ArpHandler(CEthFrame* eth_frame) {
if (eth_frame->ethertype != ETHERTYPE_ARP)
return -1;
if (eth_frame->length < sizeof(CArpHeader))
return -1;
CArpHeader* hdr = eth_frame->data;
U16 oper = ntohs(hdr->oper);
//"ARP: htype %d, ptype %d, hlen %d, plen %d, oper %d\n",
// ntohs(hdr->htype), ntohs(hdr->ptype), hdr->hlen, hdr->plen, oper;
//" spa %08X, tpa %08X\n", ntohl(hdr->spa), ntohl(hdr->tpa);
if (ntohs(hdr->htype) != 1 || ntohs(hdr->ptype) != ETHERTYPE_IPV4
|| hdr->hlen != 6 || hdr->plen != 4)
return -1;
if (oper == ARP_REQUEST) {
// Not too sure about this line, but it seems necessary in WiFi networks,
// because the wireless device won't hear our Ethernet broadcast when we Request
//ArpCachePut(ntohl(hdr->spa), hdr->sha);
if (hdr->tpa == arp_my_ipv4_n) {
ArpSend(ARP_REPLY, hdr->sha, EthernetGetAddress(), arp_my_ipv4_n, hdr->sha, hdr->spa);
}
}
else if (oper == ARP_REPLY) {
ArpCachePut(ntohl(hdr->spa), hdr->sha);
}
return 0;
}
RegisterL3Protocol(ETHERTYPE_ARP, &ArpHandler);

275
Adam/Net/Dhcp.HC

@ -0,0 +1,275 @@
// 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;
}

538
Adam/Net/Dns.HC

@ -0,0 +1,538 @@
// vim: set ft=c:
#define DNS_RCODE_NO_ERROR 0
#define DNS_RCODE_FORMAT_ERROR 1
#define DNS_RCODE_SERVER_FAILURE 2
#define DNS_RCODE_NAME_ERROR 3
#define DNS_RCODE_NOT_IMPLEMENTED 5
#define DNS_RCODE_REFUSED 6
#define DNS_FLAG_RA 0x0080
#define DNS_FLAG_RD 0x0100
#define DNS_FLAG_TC 0x0200
#define DNS_FLAG_AA 0x0400
#define DNS_OP_QUERY 0
#define DNS_OP_IQUERY 1
#define DNS_OP_STATUS 2
#define DNS_FLAG_QR 0x8000
// http://www.freesoft.org/CIE/RFC/1035/14.htm
#define DNS_TYPE_A 1
#define DNS_TYPE_NS 2
#define DNS_TYPE_CNAME 5
#define DNS_TYPE_PTR 12
#define DNS_TYPE_MX 15
#define DNS_TYPE_TXT 16
// http://www.freesoft.org/CIE/RFC/1035/16.htm
#define DNS_CLASS_IN 1
#define DNS_TIMEOUT 5000
#define DNS_MAX_RETRIES 3
class CDnsCacheEntry {
CDnsCacheEntry* next;
U8* hostname;
addrinfo info;
// TODO: honor TTL
};
class CDnsHeader {
U16 id;
U16 flags;
U16 qdcount;
U16 ancount;
U16 nscount;
U16 arcount;
};
class CDnsDomainName {
U8** labels;
I64 num_labels;
}
class CDnsQuestion {
CDnsQuestion* next;
CDnsDomainName qname;
U16 qtype;
U16 qclass;
};
class CDnsRR {
CDnsRR* next;
CDnsDomainName name;
U16 type;
U16 class_;
U32 ttl;
U16 rdlength;
U8* rdata;
};
// TODO: use a Hash table
static CDnsCacheEntry* dns_cache = NULL;
static U32 dns_ip = 0;
static CDnsCacheEntry* DnsCacheFind(U8* hostname) {
CDnsCacheEntry* e = dns_cache;
while (e) {
if (!StrCmp(e->hostname, hostname))
return e;
e = e->next;
}
return e;
}
static CDnsCacheEntry* DnsCachePut(U8* hostname, addrinfo* info) {
CDnsCacheEntry* e = DnsCacheFind(hostname);
if (!e) {
e = MAlloc(sizeof(CDnsCacheEntry));
e->next = dns_cache;
e->hostname = StrNew(hostname);
AddrInfoCopy(&e->info, info);
dns_cache = e;
}
return e;
}
static I64 DnsCalcQuestionSize(CDnsQuestion* question) {
I64 size = 0;
I64 i;
for (i = 0; i < question->qname.num_labels; i++) {
size += 1 + StrLen(question->qname.labels[i]);
}
return size + 1 + 4;
}
static U0 DnsSerializeQuestion(U8* buf, CDnsQuestion* question) {
I64 i;
for (i = 0; i < question->qname.num_labels; i++) {
U8* label = question->qname.labels[i];
*(buf++) = StrLen(label);
while (*label)
*(buf++) = *(label++);
}
*(buf++) = 0;
*(buf++) = (question->qtype >> 8);
*(buf++) = (question->qtype & 0xff);
*(buf++) = (question->qclass >> 8);
*(buf++) = (question->qclass & 0xff);
}
static I64 DnsSendQuestion(U16 id, U16 local_port, CDnsQuestion* question) {
if (!dns_ip)
return -1;
U8* frame;
I64 index = UdpPacketAlloc(&frame, IPv4GetAddress(), local_port, dns_ip, 53,
sizeof(CDnsHeader) + DnsCalcQuestionSize(question));
if (index < 0)
return index;
U16 flags = (DNS_OP_QUERY << 11) | DNS_FLAG_RD;
CDnsHeader* hdr = frame;
hdr->id = htons(id);
hdr->flags = htons(flags);
hdr->qdcount = htons(1);
hdr->ancount = 0;
hdr->nscount = 0;
hdr->arcount = 0;
DnsSerializeQuestion(frame + sizeof(CDnsHeader), question);
return UdpPacketFinish(index);
}
static I64 DnsParseDomainName(U8* packet_data, I64 packet_length,
U8** data_inout, I64* length_inout, CDnsDomainName* name_out) {
U8* data = *data_inout;
I64 length = *length_inout;
Bool jump_taken = FALSE;
if (length < 1) {
//"DnsParseDomainName: EOF\n";
return -1;
}
name_out->labels = MAlloc(16 * sizeof(U8*));
name_out->num_labels = 0;
U8* name_buf = MAlloc(256);
name_out->labels[0] = name_buf;
while (length) {
I64 label_len = *(data++);
length--;
if (label_len == 0) {
break;
}
else if (label_len >= 192) {
label_len &= 0x3f;
if (!jump_taken) {
*data_inout = data + 1;
*length_inout = length - 1;
jump_taken = TRUE;
}
//"jmp %d\n", ((label_len << 8) | *data);
data = packet_data + ((label_len << 8) | *data);
length = packet_data + packet_length - data;
}
else {
if (length < label_len) return -1;
MemCpy(name_buf, data, label_len);
data += label_len;
length -= label_len;
name_buf[label_len] = 0;
//"%d bytes => %s\n", label_len, name_buf;
name_out->labels[name_out->num_labels++] = name_buf;
name_buf += label_len + 1;
}
}
if (!jump_taken) {
*data_inout = data;
*length_inout = length;
}
return 0;
}
static I64 DnsParseQuestion(U8* packet_data, I64 packet_length,
U8** data_inout, I64* length_inout, CDnsQuestion* question_out) {
I64 error = DnsParseDomainName(packet_data, packet_length,
data_inout, length_inout, &question_out->qname);
if (error < 0)
return error;
U8* data = *data_inout;
I64 length = *length_inout;
if (length < 4)
return -1;
question_out->next = NULL;
question_out->qtype = (data[1] << 8) | data[0];
question_out->qclass = (data[3] << 8) | data[2];
//"DnsParseQuestion: qtype %d, qclass %d\n", ntohs(question_out->qtype), ntohs(question_out->qclass);
*data_inout = data + 4;
*length_inout = length - 4;
return 0;
}
static I64 DnsParseRR(U8* packet_data, I64 packet_length,
U8** data_inout, I64* length_inout, CDnsRR* rr_out) {
I64 error = DnsParseDomainName(packet_data, packet_length,
data_inout, length_inout, &rr_out->name);
if (error < 0)
return error;
U8* data = *data_inout;
I64 length = *length_inout;
if (length < 10)
return -1;
rr_out->next = NULL;
MemCpy(&rr_out->type, data, 10);
I64 record_length = 10 + ntohs(rr_out->rdlength);
if (length < record_length)
return -1;
rr_out->rdata = data + 10;
//"DnsParseRR: type %d, class %d\n, ttl %d, rdlength %d\n",
// ntohs(rr_out->type), ntohs(rr_out->class_), ntohl(rr_out->ttl), ntohs(rr_out->rdlength);
*data_inout = data + record_length;
*length_inout = length - record_length;
return 0;
}
static I64 DnsParseResponse(U16 id, U8* data, I64 length,
CDnsHeader** hdr_out, CDnsQuestion** questions_out,
CDnsRR** answers_out) {
U8* packet_data = data;
I64 packet_length = length;
if (length < sizeof(CDnsHeader)) {
//"DnsParseResponse: too short\n";
return -1;
}
CDnsHeader* hdr = data;
data += sizeof(CDnsHeader);
if (id != 0 && ntohs(hdr->id) != id) {
//"DnsParseResponse: id %04Xh != %04Xh\n", ntohs(hdr->id), id;
return -1;
}
I64 i;
for (i = 0; i < htons(hdr->qdcount); i++) {
CDnsQuestion* question = MAlloc(sizeof(CDnsQuestion));
if (DnsParseQuestion(packet_data, packet_length, &data, &length, question) < 0)
return -1;
question->next = *questions_out;
*questions_out = question;
}
for (i = 0; i < htons(hdr->ancount); i++) {
CDnsRR* answer = MAlloc(sizeof(CDnsRR));
if (DnsParseRR(packet_data, packet_length, &data, &length, answer) < 0)
return -1;
answer->next = *answers_out;
*answers_out = answer;
}
*hdr_out = hdr;
return 0;
}
static U0 DnsBuildQuestion(CDnsQuestion* question, U8* name) {
question->next = NULL;
question->qname.labels = MAlloc(16 * sizeof(U8*));
question->qname.labels[0] = 0;
question->qname.num_labels = 0;
question->qtype = DNS_TYPE_A;
question->qclass = DNS_CLASS_IN;
U8* copy = StrNew(name);
while (*copy) {
question->qname.labels[question->qname.num_labels++] = copy;
U8* dot = StrFirstOcc(copy, ".");
if (dot) {
*dot = 0;
copy = dot + 1;
}
else
break;
}
}
static U0 DnsFreeQuestion(CDnsQuestion* question) {
Free(question->qname.labels[0]);
}
static U0 DnsFreeRR(CDnsRR* rr) {
Free(rr->name.labels[0]);
}
static U0 DnsFreeQuestionChain(CDnsQuestion* questions) {
while (questions) {
CDnsQuestion* next = questions->next;
DnsFreeQuestion(questions);
Free(questions);
questions = next;
}
}
static U0 DnsFreeRRChain(CDnsRR* rrs) {
while (rrs) {
CDnsQuestion* next = rrs->next;
DnsFreeRR(rrs);
Free(rrs);
rrs = next;
}
}
static I64 DnsRunQuery(I64 sock, U8* name, U16 port, addrinfo** res_out) {
I64 retries = 0;
I64 timeout = DNS_TIMEOUT;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_MS, &timeout, sizeof(timeout)) < 0) {
"$FG,6$DnsRunQuery: setsockopt failed\n$FG$";
}
U16 local_port = RandU16();
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(local_port);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, &addr, sizeof(addr)) < 0) {
"$FG,4$DnsRunQuery: failed to bind\n$FG$";
return -1;
}
U8 buffer[2048];
I64 count;
sockaddr_in addr_in;
U16 id = RandU16();
I64 error = 0;
CDnsQuestion question;
DnsBuildQuestion(&question, name);
while (1) {
error = DnsSendQuestion(id, local_port, &question);
if (error < 0) return error;
count = recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
if (count > 0) {
//"Try parse response\n";
CDnsHeader* hdr = NULL;
CDnsQuestion* questions = NULL;
CDnsRR* answers = NULL;
error = DnsParseResponse(id, buffer, count, &hdr, &questions, &answers);
if (error >= 0) {
Bool have = FALSE;
// Look for a suitable A-record in the answer
CDnsRR* answer = answers;
while (answer) {
// TODO: if there are multiple acceptable answers,
// we should pick one at random -- not just the first one
if (htons(answer->type) == DNS_TYPE_A
&& htons(answer->class_) == DNS_CLASS_IN
&& htons(answer->rdlength) == 4) {
addrinfo* res = MAlloc(sizeof(addrinfo));
res->ai_flags = 0;
res->ai_family = AF_INET;
res->ai_socktype = 0;
res->ai_protocol = 0;
res->ai_addrlen = sizeof(sockaddr_in);
res->ai_addr = MAlloc(sizeof(sockaddr_in));
res->ai_canonname = NULL;
res->ai_next = NULL;
sockaddr_in* sa = res->ai_addr;
sa->sin_family = AF_INET;
sa->sin_port = port;
MemCpy(&sa->sin_addr.s_addr, answers->rdata, 4);
DnsCachePut(name, res);
*res_out = res;
have = TRUE;
break;
}
answer = answer->next;
}
DnsFreeQuestionChain(questions);
DnsFreeRRChain(answers);
if (have)
break;
// At this point we could try iterative resolution,
// but all end-user DNS servers would have tried that already
"$FG,6$DnsParseResponse: no suitable answer in reply\n$FG$";
error = -1;
}
else {
"$FG,6$DnsParseResponse: error %d\n$FG$", error;
}
}
if (++retries == DNS_MAX_RETRIES) {
"$FG,4$DnsRunQuery: max retries reached\n$FG$";
error = -1;
break;
}
}
DnsFreeQuestion(&question);
return error;
}
I64 DnsGetaddrinfo(U8* node, U8* service, addrinfo* hints, addrinfo** res) {
no_warn service;
no_warn hints;
CDnsCacheEntry* cached = DnsCacheFind(node);
if (cached) {
*res = MAlloc(sizeof(addrinfo));
AddrInfoCopy(*res, &cached->info);
(*res)->ai_flags |= AI_CACHED;
return 0;
}
I64 sock = socket(AF_INET, SOCK_DGRAM);
I64 error = 0;
if (sock >= 0) {
// TODO: service should be parsed as int, specifying port number
error = DnsRunQuery(sock, node, 0, res);
close(sock);
}
else
error = -1;
return error;
}
U0 DnsSetResolverIPv4(U32 ip) {
dns_ip = ip;
}
public U0 Host(U8* hostname) {
addrinfo* res = NULL;
I64 error = getaddrinfo(hostname, NULL, NULL, &res);
if (error < 0) {
"$FG,4$getaddrinfo: error %d\n", error;
}
else {
addrinfo* curr = res;
while (curr) {
"flags %04Xh, family %d, socktype %d, proto %d, addrlen %d, addr %s\n",
curr->ai_flags, curr->ai_family, curr->ai_socktype, curr->ai_protocol, curr->ai_addrlen,
inet_ntoa((curr->ai_addr(sockaddr_in*))->sin_addr);
curr = curr->ai_next;
}
}
freeaddrinfo(res);
}
U0 DnsInit() {
static CAddrResolver dns_addr_resolver;
dns_addr_resolver.getaddrinfo = &DnsGetaddrinfo;
socket_addr_resolver = &dns_addr_resolver;
}
DnsInit;

55
Adam/Net/Ethernet.HC

@ -0,0 +1,55 @@
// vim: set ft=c:
class CEthFrame {
U8 source_addr[6];
U8 padding[2];
U8 dest_addr[6];
U16 ethertype;
U8* data;
I64 length;
};
class CL3Protocol {
CL3Protocol* next;
U16 ethertype;
U8 padding[6];
I64 (*handler)(CEthFrame* frame);
};
static CL3Protocol* l3_protocols = NULL;
U8 eth_null[6] = {0, 0, 0, 0, 0, 0};
U8 eth_broadcast[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
I64 EthernetFrameParse(CEthFrame* frame_out, U8* frame, U16 length) {
// FIXME: check length
// TODO: MemCpy has high overhead, get rid of it
MemCpy(frame_out->dest_addr, frame, 6);
MemCpy(frame_out->source_addr, frame + 6, 6);
frame_out->ethertype = frame[13] | (frame[12] << 8);
/*"Rx dst: %02X:%02X:%02X:%02X:%02X:%02X\n",
frame_out->dest_addr[0], frame_out->dest_addr[1], frame_out->dest_addr[2], frame_out->dest_addr[3], frame_out->dest_addr[4], frame_out->dest_addr[5];
"Rx src: %02X:%02X:%02X:%02X:%02X:%02X\n",
frame_out->source_addr[0], frame_out->source_addr[1], frame_out->source_addr[2], frame_out->source_addr[3], frame_out->source_addr[4], frame_out->source_addr[5];
"Rx ethertype: %02X\n", frame_out->ethertype;*/
frame_out->data = frame + 14;
frame_out->length = length - 14 - 4; // ??
return 0;
}
U0 RegisterL3Protocol(U16 ethertype, I64 (*handler)(CEthFrame* frame)) {
CL3Protocol* p = MAlloc(sizeof(CL3Protocol));
p->next = l3_protocols;
p->ethertype = ethertype;
p->handler = handler;
l3_protocols = p;
}

197
Adam/Net/Http.HC

@ -0,0 +1,197 @@
// vim: set ft=cpp:
#include "::/Adam/Net/Socket"
#include "::/Adam/Net/UrlParse"
#define HTTP_ECONNECT (-101)
#define HTTP_EPROTOCOL (-102)
#define HTTP_EREQUEST (-103)
#define HTTP_EREDIRECT (-104)
#define HTTP_EEOF (-105)
#define HTTP_ECONTENTLENGTH (-106)
#define HTTP_MAX_REDIRECTS 5
#define HTTP_USER_AGENT "SnailNet ($TX+CX,"TempleOS",D="DD_OS_NAME_VERSION"$)"
/**
* @param len_out (required) requires the content length, or -1 if unspecified
* @return socket (>= 0) on success, error code on failure
*/
I64 HttpOpenGet(U8* host, U16 port = 0, U8* path, I64* len_out,
I64 allowed_redirects = HTTP_MAX_REDIRECTS) {
U8 line[256];
I64 error = 0;
if (!port)
port = 80;
// Should this be done here though?
if (*path == 0)
path = "/";
//"Connect(%s:%d)\n", host, port;
I64 sock = create_connection(host, port);
//"create_connection: %d\n", sock;
if (sock >= 0) {
StrPrint(line, "GET %s HTTP/1.0\r\n", path);
sendString(sock, line, 0);
StrPrint(line, "Host: %s\r\n", host);
sendString(sock, line, 0);
sendString(sock, "User-Agent: " HTTP_USER_AGENT "\r\n", 0);
sendString(sock, "\r\n", 0);
Bool haveHTTP = FALSE;
U8* location = NULL;
I64 code = -1;
*len_out = -1;
while (1) {
error = recvLine(sock, line, sizeof(line), 0);
if (error < 0) {
break;
}
else if (error == 0) {
if (!haveHTTP)
error = HTTP_EPROTOCOL;
break;
}
U8* delim;
//"%s\n", line;
if (!haveHTTP) {
delim = StrFirstOcc(line, " ");
if (delim && StrNCmp(line, "HTTP/", 5) == 0) {
code = Str2I64(delim + 1);
if (code >= 200 && code <= 399) {
haveHTTP = TRUE;
}
else {
error = HTTP_EREQUEST;
break;
}
}
else {
error = HTTP_EREQUEST;
break;
}
}
else {
delim = StrFirstOcc(line, ":");
if (!delim) {
error = HTTP_EPROTOCOL;
break;
}
*delim = 0;
do { delim++; }
while (*delim == ' ');
//"%s=%s\n", line, delim;
if (!StrCmp(line, "Content-Length")) {
StrScan(delim, "%d", len_out);
}
else if (!StrCmp(line, "Location")) {
// This will leak on malformed response
location = StrNew(delim);
}
}
}
// HTTP Code 3xx -- Redirection
if (!error && code >= 300 && code <= 399) {
if (allowed_redirects > 0) {
CUrl curl;
UrlInit(&curl);
if (UrlParse(location, &curl)) {
if (!StrCmp(curl.protocol, "http")) {
close(sock);
sock = HttpOpenGet(curl.host, curl.port, curl.path, len_out, allowed_redirects - 1);
if (sock < 0)
error = sock;
}
else
error = HTTP_EPROTOCOL;
}
else
error = HTTP_EREDIRECT;
UrlFree(&curl);
}
else {
error = HTTP_EREDIRECT;
}
}
Free(location);
}
else
error = HTTP_ECONNECT;
if (error) {
close(sock);
return error;
}
else {
return sock;
}
}
I64 HttpGet(U8* host, U16 port = 0, U8* path, U8** data_out = NULL, I64* len_out = NULL) {
I64 error = 0;
I64 len = 0;
I64 sock = HttpOpenGet(host, port, path, &len);
if (sock > 0) {
if (len >= 0) {