Hello Shrine

This commit is contained in:
minexew
2020-02-28 18:51:14 +01:00
parent 81e37f4fe4
commit e45d1535db
44 changed files with 5722 additions and 5 deletions
+1 -1
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -30,3 +30,5 @@ LBts(&sys_run_level,RLf_DOC);
#include "AMouse"
#include "Host"
Cd("..");;
#include "::/Adam/Net/MakeSnailNet"
+125
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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) {
U8* data = MAlloc(1 + len);
I64 total = 0;
while (total < len) {
I64 step = len - total;
if (step > 1024)
step = 1024;
I64 got = recv(sock, data + total, step, 0);
if (got <= 0) {
error = HTTP_EEOF;
break;
}
total += got;
}
if (error) {
Free(data);
}
else {
if (data_out) {
data[total] = 0;
*data_out = data;
}
if (len_out)
*len_out = len;
}
}
else
// We currently don't handle dynamic-length HTTP responses
error = HTTP_ECONTENTLENGTH;
close(sock);
}
else
error = sock;
return error;
}
+252
View File
@@ -0,0 +1,252 @@
// vim: set ft=c:
#define IP_PROTO_ICMP 0x01
#define IP_PROTO_TCP 0x06
#define IP_PROTO_UDP 0x11
#define IPV4_EADDR_INVALID (-200001)
#define IPV4_EHOST_UNREACHABLE (-200002)
#define IPV4_TTL 64
class CIPv4Packet {
CEthFrame* l2_frame;
U32 source_ip;
U32 dest_ip;
U8 proto;
U8 padding[7];
U8* data;
I64 length;
};
class CIPv4Header {
U8 version_ihl;
U8 dscp_ecn;
U16 total_length;
U16 ident;
U16 flags_fragoff;
U8 ttl;
U8 proto;
U16 header_checksum;
U32 source_ip;
U32 dest_ip;
};
class CL4Protocol {
CL4Protocol* next;
U8 proto;
U8 padding[7];
U0 (*handler)(CIPv4Packet* packet);
};
// *_n = stored in network order
static U32 my_ip = 0;
static U32 my_ip_n = 0;
static U32 ipv4_router_addr = 0;
static U32 ipv4_subnet_mask = 0;
static CL4Protocol* l4_protocols = NULL;
// http://stackoverflow.com/q/26774761/2524350
static U16 IPv4Checksum(U8* header, I64 length) {
I64 nleft = length;
U16* w = header;
I64 sum = 0;
while (nleft > 1) {
sum += *(w++);
nleft -= 2;
}
// mop up an odd byte, if necessary
if (nleft == 1) {
sum += ((*w) & 0x00ff);
}
// add back carry outs from top 16 bits to low 16 bits
sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16
sum += (sum >> 16); // add carry
return (~sum) & 0xffff;
}
static I64 GetEthernetAddressForIP(U32 ip, U8** mac_out) {
// invalid
if (ip == 0) {
return IPV4_EADDR_INVALID;
}
// broadcast
else if (ip == 0xffffffff) {
*mac_out = eth_broadcast;
return 0;
}
// outside this subnet; needs routing
else if ((ip & ipv4_subnet_mask) != (my_ip & ipv4_subnet_mask)) {
// FIXME: infinite loop if mis-configured
return GetEthernetAddressForIP(ipv4_router_addr, mac_out);
}
// local network
else {
// FIXME: this can stall NetHandlerTask, we might need a flag to bail early
CArpCacheEntry* e = ArpCacheFindByIP(ip);
if (e) {
*mac_out = e->mac;
return 0;
}
//"Not in cache, requesting\n";
// Up to 4 retries, 500 ms each
I64 retries = 4;
while (retries) {
ArpSend(ARP_REQUEST, eth_broadcast, EthernetGetAddress(), my_ip_n, eth_null, htonl(ip));
I64 try_ = 0;
for (try_ = 0; try_ < 50; try_++) {
Sleep(10);
e = ArpCacheFindByIP(ip);
if (e) break;
}
if (e) {
*mac_out = e->mac;
return 0;
}
retries--;
}
in_addr in;
in.s_addr = htonl(ip);
"$FG,6$IPv4: Failed to resolve address %s\n$FG$", inet_ntoa(in);
return IPV4_EHOST_UNREACHABLE;
}
}
I64 IPv4PacketAlloc(U8** frame_out, U8 proto, U32 source_ip, U32 dest_ip, I64 length) {
U8* frame;
U8* dest_mac;
I64 error = GetEthernetAddressForIP(dest_ip, &dest_mac);
if (error < 0)
return error;
I64 index = EthernetFrameAlloc(&frame, EthernetGetAddress(), dest_mac,
ETHERTYPE_IPV4, sizeof(CIPv4Header) + length, 0);
if (index < 0)
return index;
I64 internet_header_length = 5;
CIPv4Header* hdr = frame;
hdr->version_ihl = internet_header_length | (4 << 4);
hdr->dscp_ecn = 0;
hdr->total_length = htons(internet_header_length * 4 + length);
hdr->ident = 0;
hdr->flags_fragoff = 0;
hdr->ttl = IPV4_TTL;
hdr->proto = proto;
hdr->header_checksum = 0;
hdr->source_ip = htonl(source_ip);
hdr->dest_ip = htonl(dest_ip);
hdr->header_checksum = IPv4Checksum(hdr, internet_header_length * 4);
*frame_out = frame + sizeof(CIPv4Header);
return index;
}
I64 IPv4PacketFinish(I64 index) {
return EthernetFrameFinish(index);
}
U32 IPv4GetAddress() {
return my_ip;
}
U0 IPv4SetAddress(U32 addr) {
my_ip = addr;
my_ip_n = htonl(addr);
ArpSetIPv4Address(addr);
}
U0 IPv4SetSubnet(U32 router_addr, U32 subnet_mask) {
ipv4_router_addr = router_addr;
ipv4_subnet_mask = subnet_mask;
}
I64 IPv4ParsePacket(CIPv4Packet* packet_out, CEthFrame* eth_frame) {
if (eth_frame->ethertype != ETHERTYPE_IPV4)
return -1;
// FIXME: check eth_frame->length etc.
CIPv4Header* hdr = eth_frame->data;
I64 header_length = (hdr->version_ihl & 0x0f) * 4;
//"IPv4: hdr %d, proto %02X, source %08X, dest %08X, len %d\n",
// header_length, hdr->proto, ntohl(hdr->source_ip), ntohl(hdr->dest_ip),
// eth_frame->length - header_length;
U16 total_length = ntohs(hdr->total_length);
packet_out->l2_frame = eth_frame;
packet_out->source_ip = ntohl(hdr->source_ip);
packet_out->dest_ip = ntohl(hdr->dest_ip);
packet_out->proto = hdr->proto;
packet_out->data = eth_frame->data + header_length;
packet_out->length = total_length - header_length;
return 0;
}
U0 RegisterL4Protocol(U8 proto, I64 (*handler)(CIPv4Packet* frame)) {
CL4Protocol* p = MAlloc(sizeof(CL4Protocol));
p->next = l4_protocols;
p->proto = proto;
p->handler = handler;
l4_protocols = p;
}
I64 IPv4Handler(CEthFrame* eth_frame) {
CIPv4Packet packet;
I64 error = IPv4ParsePacket(&packet, eth_frame);
if (error < 0)
return error;
// This seems necessary to receive connections under VBox NAT,
// but is also pretty slow, so should be optimized to use a better
// struct than linked list.
ArpCachePut(packet.source_ip, eth_frame->source_addr);
CL4Protocol* l4 = l4_protocols;
while (l4) {
if (l4->proto == packet.proto) {
l4->handler(&packet);
break;
}
l4 = l4->next;
}
return error;
}
RegisterL3Protocol(ETHERTYPE_IPV4, &IPv4Handler);
+52
View File
@@ -0,0 +1,52 @@
// vim: set ft=c:
#define ICMP_TYPE_ECHO_REPLY 0
#define ICMP_TYPE_ECHO_REQUEST 8
class CIcmpHeader {
U8 type;
U8 code;
U16 checksum;
U16 identifier;
U16 seq_number;
};
I64 IcmpSendReply(U32 dest_ip, U16 identifier, U16 seq_number, U16 request_checksum, U8* payload, I64 length) {
U8* frame;
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_ICMP, IPv4GetAddress(), dest_ip, sizeof(CIcmpHeader) + length);
if (index < 0)
return index;
CIcmpHeader* hdr = frame;
hdr->type = ICMP_TYPE_ECHO_REPLY;
hdr->code = 0;
hdr->checksum = htons(ntohs(request_checksum) + 0x0800); // hack alert!
hdr->identifier = identifier;
hdr->seq_number = seq_number;
MemCpy(frame + sizeof(CIcmpHeader), payload, length);
return IPv4PacketFinish(index);
}
I64 IcmpHandler(CIPv4Packet* packet) {
if (packet->proto != IP_PROTO_ICMP)
return -1;
if (packet->length < sizeof(CIcmpHeader))
return -1;
CIcmpHeader* hdr = packet->data;
if (hdr->type == ICMP_TYPE_ECHO_REQUEST && hdr->code == 0) {
// This also makes sure that we don't stall NetHandlerTask
ArpCachePut(packet->source_ip, packet->l2_frame->source_addr);
IcmpSendReply(packet->source_ip, hdr->identifier, hdr->seq_number, hdr->checksum,
packet->data + sizeof(CIcmpHeader), packet->length - sizeof(CIcmpHeader));
}
return 0;
}
RegisterL4Protocol(IP_PROTO_ICMP, &IcmpHandler);
+45
View File
@@ -0,0 +1,45 @@
// vim: set ft=c:
#exe {
#include "::/Adam/HwSupp/Pci"
U8* native_driver = NULL;
I64 b, d, f;
if (PciFindByID(0x1022, 0x2000, &b, &d, &f))
native_driver = "PCNet";
// If we're using the native stack, load it system-wide
if (native_driver != NULL) {
StreamPrint("U8* SNAILNET_NATIVE_DRIVER = \"%s\";\n", native_driver);
// Hardware support
StreamPrint("#include \"::/Adam/Net/NetFifo\"");
StreamPrint("#include \"::/Adam/HwSupp/%s\"", native_driver);
// Contains a lot of common definitions, probably should be cleaned up
StreamPrint("#include \"::/Adam/Net/NativeSocket\"");
// L2
StreamPrint("#include \"::/Adam/Net/Ethernet\"");
// L3
StreamPrint("#include \"::/Adam/Net/Arp\"");
StreamPrint("#include \"::/Adam/Net/IPv4\"");
// L4
StreamPrint("#include \"::/Adam/Net/Icmp\"");
StreamPrint("#include \"::/Adam/Net/Tcp\"");
StreamPrint("#include \"::/Adam/Net/Udp\"");
// L7
StreamPrint("#include \"::/Adam/Net/Dns\"");
// Handler Task
StreamPrint("#include \"::/Adam/Net/NetHandlerTask\"");
StreamPrint("#include \"::/Adam/Net/Netcfg\"");
}
else
StreamPrint("U8* SNAILNET_NATIVE_DRIVER = NULL;\n", native_driver);
}
+264
View File
@@ -0,0 +1,264 @@
// vim: set ft=c:
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define SOCK_RAW 3
#define AF_UNSPEC 0
#define AF_INET 2
#define AF_INET6 10
#define INADDR_ANY 0
#define SOL_SOCKET 1
// optval = I64*
#define SO_RCVTIMEO_MS 1
#define AI_CACHED 0x8000
class in_addr {
U32 s_addr;
};
class sockaddr {
U16 sa_family;
U8 sa_data[14];
};
class sockaddr_in {
I16 sin_family;
U16 sin_port;
in_addr sin_addr;
U8 sin_zero[8];
};
class addrinfo {
I32 ai_flags;
I32 ai_family;
I32 ai_socktype;
I32 ai_protocol;
I64 ai_addrlen;
sockaddr* ai_addr;
U8* ai_canonname;
addrinfo* ai_next;
};
I64 inet_aton(U8* cp, in_addr* inp) {
// FIXME: error handling
I64 a, b, c, d;
StrScan(cp, "%d.%d.%d.%d", &a, &b, &c, &d);
inp->s_addr = (a | (b << 8) | (c << 16) | (d << 24));
return 0;
}
U8* inet_ntoa(in_addr in) {
static U8 buffer[16];
StrPrint(buffer, "%d.%d.%d.%d", in.s_addr & 0xff, (in.s_addr >> 8) & 0xff,
(in.s_addr >> 16) & 0xff, (in.s_addr >> 24) & 0xff);
return buffer;
}
class CSocket {
I64 (*accept)(CSocket* s, sockaddr* src_addr, I64 addrlen);
I64 (*bind)(CSocket* s, sockaddr* addr, I64 addrlen);
I64 (*close)(CSocket* s);
I64 (*connect)(CSocket* s, sockaddr* addr, I64 addrlen);
I64 (*listen)(CSocket* s, I64 backlog);
I64 (*recvfrom)(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen);
I64 (*sendto)(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr* dest_addr, I64 addrlen);
I64 (*setsockopt)(CSocket* s, I64 level, I64 optname, U8* optval, I64 optlen);
};
class CSocketClass {
CSocketClass* next;
U16 domain;
U16 type;
U8 padding[4];
CSocket* (*socket)(U16 domain, U16 type);
};
class CAddrResolver {
// TODO: allow different resolvers for different socket domains
I64 (*getaddrinfo)(U8* node, U8* service, addrinfo* hints, addrinfo** res);
};
static CSocketClass* socket_classes = NULL;
static CAddrResolver* socket_addr_resolver = NULL;
static CSocketClass* FindSocketClass(U16 domain, U16 type) {
CSocketClass* cls = socket_classes;
while (cls) {
if (cls->domain == domain && cls->type == type)
return cls;
cls = cls->next;
}
return NULL;
}
I64 SocketInit() {
return 0;
}
I64 socket(I64 domain, I64 type) {
CSocketClass* cls = FindSocketClass(domain, type);
if (cls) return cls->socket(domain, type)(I64);
else return -1;
}
I64 accept(I64 sockfd, sockaddr* addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->accept(sock, addr, addrlen);
else return -1;
}
I64 close(I64 sockfd) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->close(sock);
else return -1;
}
I64 bind(I64 sockfd, sockaddr* addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->bind(sock, addr, addrlen);
else return -1;
}
I64 connect(I64 sockfd, sockaddr* addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->connect(sock, addr, addrlen);
else return -1;
}
I64 listen(I64 sockfd, I64 backlog) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->listen(sock, backlog);
else return -1;
}
I64 recv(I64 sockfd, U8* buf, I64 len, I64 flags) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->recvfrom(sock, buf, len, flags, NULL, 0);
else return -1;
}
I64 recvfrom(I64 sockfd, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->recvfrom(sock, buf, len, flags, src_addr, addrlen);
else return -1;
}
I64 send(I64 sockfd, U8* buf, I64 len, I64 flags) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->sendto(sock, buf, len, flags, NULL, 0);
else return -1;
}
I64 sendto(I64 sockfd, U8* buf, I64 len, I64 flags, sockaddr* dest_addr, I64 addrlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->sendto(sock, buf, len, flags, dest_addr, addrlen);
else return -1;
}
I64 setsockopt(I64 sockfd, I64 level, I64 optname, U8* optval, I64 optlen) {
CSocket* sock = sockfd(CSocket*);
if (sockfd > 0) return sock->setsockopt(sock, level, optname, optval, optlen);
else return -1;
}
I64 getaddrinfo(U8* node, U8* service, addrinfo* hints, addrinfo** res) {
if (socket_addr_resolver) return socket_addr_resolver->getaddrinfo(node, service, hints, res);
else return -1;
}
U0 freeaddrinfo(addrinfo* res) {
while (res) {
addrinfo* next = res->ai_next;
Free(res->ai_addr);
Free(res->ai_canonname);
Free(res);
res = next;
}
}
U0 AddrInfoCopy(addrinfo* ai_out, addrinfo* ai_in) {
MemCpy(ai_out, ai_in, sizeof(addrinfo));
if (ai_in->ai_addr) {
ai_out->ai_addr = MAlloc(ai_in->ai_addrlen);
MemCpy(ai_out->ai_addr, ai_in->ai_addr, ai_in->ai_addrlen);
}
if (ai_in->ai_canonname) {
ai_out->ai_canonname = StrNew(ai_in->ai_canonname);
}
}
U8* gai_strerror(I64 errcode) {
no_warn errcode;
return "Unspecified error";
}
// Inspired by https://docs.python.org/3.7/library/socket.html#socket.create_connection
I64 create_connection(U8* hostname, U16 port) {
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = 0;
addrinfo* res;
I64 error = getaddrinfo(hostname, NULL, NULL, &res);
if (error < 0) {
"$FG,4$getaddrinfo: error %d\n$FG$", error;
}
else {
addrinfo* curr = res;
while (curr) {
if (curr->ai_family == AF_INET && (curr->ai_socktype == 0 || curr->ai_socktype == SOCK_STREAM)) {
addr.sin_addr.s_addr = (curr->ai_addr(sockaddr_in*))->sin_addr.s_addr;
freeaddrinfo(res);
I64 sockfd = socket(AF_INET, SOCK_STREAM);
if (sockfd < 0)
return sockfd;
error = connect(sockfd, &addr, sizeof(addr));
if (error < 0) {
close(sockfd);
return error;
}
return sockfd;
}
curr = curr->ai_next;
}
"$FG,4$create_connection: no suitable address\n$FG$";
}
freeaddrinfo(res);
return -1;
}
U0 RegisterSocketClass(U16 domain, U16 type, CSocket* (*socket)(U16 domain, U16 type)) {
CSocketClass* cls = MAlloc(sizeof(CSocketClass));
cls->next = socket_classes;
cls->domain = domain;
cls->type = type;
cls->socket = socket;
socket_classes = cls;
}
+79
View File
@@ -0,0 +1,79 @@
// vim: set ft=c:
// Warning: terrible code ahead. this still needs a lot of work
// In the future we'll probably have 2 FIFOs (pending frames & empty buffers)
// TODO: check if FIFO implementation is suitable for high throughput
#define NET_FIFO_DEPTH 1024
#define ETHERNET_FRAME_SIZE 1548
#define ETHERTYPE_IPV4 0x0800
#define ETHERTYPE_ARP 0x0806
class CNetFifoEntry {
I64 length;
U8 frame[ETHERNET_FRAME_SIZE];
};
static CFifoI64* netfifo;
static CNetFifoEntry* entries;
static I64 next_entry = 0;
CTask* netfifo_handler_task = NULL;
// TODO: asm optimization? or perhaps use EndianU*?
// These don't belong here in the first place,
// but it's convenient for Ethernet drivers
// We'll probably split it off along with ETHERTYPE_* constants
U16 htons(U16 h) {
return ((h >> 8) | (h << 8)) & 0xffff;
}
U16 ntohs(U16 h) {
return ((h >> 8) | (h << 8)) & 0xffff;
}
U32 htonl(U32 h) {
return ((h >> 24) | ((h & 0x00ff0000) >> 8) | ((h & 0x0000ff00) << 8) | (h << 24)) & 0xffffffff;
}
U32 ntohl(U32 h) {
return ((h >> 24) | ((h & 0x00ff0000) >> 8) | ((h & 0x0000ff00) << 8) | (h << 24)) & 0xffffffff;
}
CNetFifoEntry* NetFifoPull() {
CNetFifoEntry* entry;
if (FifoI64Rem(netfifo, &entry))
return entry;
else
return NULL;
}
I64 NetFifoPushCopy(U8* data, I64 length) {
CNetFifoEntry* entry = &entries[next_entry];
next_entry = (next_entry + 1) & (NET_FIFO_DEPTH - 1);
entry->length = length;
MemCpy(entry->frame, data, length);
if (!FifoI64Ins(netfifo, entry))
return -1;
// Wake up Handler Task
if (netfifo_handler_task)
LBtr(&netfifo_handler_task->task_flags, TASKf_IDLE);
return 0;
}
U0 NetFifoInit() {
netfifo = FifoI64New(NET_FIFO_DEPTH);
entries = MAlloc(NET_FIFO_DEPTH * sizeof(CNetFifoEntry));
}
NetFifoInit;
+38
View File
@@ -0,0 +1,38 @@
// vim: set ft=c:
U0 HandleNetFifoEntry(CNetFifoEntry* e) {
CEthFrame l2_frame;
if (EthernetFrameParse(&l2_frame, e->frame, e->length) < 0)
return;
//"NetFifoEntry %04X\n", l2_frame.ethertype;
CL3Protocol* l3 = l3_protocols;
while (l3) {
if (l3->ethertype == l2_frame.ethertype) {
l3->handler(&l2_frame);
break;
}
l3 = l3->next;
}
}
U0 NetHandlerTask(I64) {
EthernetInit();
while (1) {
CNetFifoEntry* e = NetFifoPull();
if (e) {
HandleNetFifoEntry(e);
}
else {
LBts(&Fs->task_flags, TASKf_IDLE);
Yield;
}
}
}
netfifo_handler_task = Spawn(&NetHandlerTask, NULL, "NetHandler");
+139
View File
@@ -0,0 +1,139 @@
// vim: set ft=c:
#include "::/Adam/Net/Dhcp"
#define CLIENT_START 0
#define CLIENT_DISCOVER 1
#define CLIENT_REQUEST 2
#define CLIENT_REQUEST_ACCEPTED 3
#define DHCP_TIMEOUT 3000
#define MAX_RETRIES 3
I64 DhcpConfigureInner(I64 sock, U32* yiaddr_out, U32* dns_ip_out, U32* router_ip_out, U32* subnet_mask_out) {
I64 state = CLIENT_START;
I64 retries = 0;
I64 timeout = DHCP_TIMEOUT;
if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO_MS, &timeout, sizeof(timeout)) < 0) {
"$FG,6$DhcpConfigure: setsockopt failed\n$FG$";
}
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(68);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, &addr, sizeof(addr)) < 0) {
"$FG,4$DhcpConfigure: failed to bind\n$FG$";
return -1;
}
U32 xid = DhcpBeginTransaction();
I64 error = 0;
U32 dhcp_addr;
U8 buffer[2048];
I64 count;
sockaddr_in addr_in;
while (state != CLIENT_REQUEST_ACCEPTED) {
if (state == CLIENT_START) {
state = CLIENT_DISCOVER;
retries = 0;
}
else if (state == CLIENT_DISCOVER) {
error = DhcpSendDiscover(xid);
if (error < 0) return error;
count = recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
if (count > 0) {
//"Try parse Offer\n";
error = DhcpParseOffer(xid, buffer, count, yiaddr_out, dns_ip_out, router_ip_out, subnet_mask_out);
if (error < 0) {
"$FG,6$DhcpParseOffer: error %d\n$FG$", error;
}
}
if (count > 0 && error >= 0) {
dhcp_addr = ntohl(addr_in.sin_addr.s_addr);
//"DHCP Offer from %08X: YIAddr %08X,\n\tDNS %08X, Router %08X, Subnet %08X\n",
// dhcp_addr, *yiaddr_out, dns_ip, router_ip, subnet_mask;
state = CLIENT_REQUEST;
retries = 0;
}
else if (++retries == MAX_RETRIES) {
"$FG,4$DhcpConfigure: max retries for DISCOVER\n$FG$";
return -1;
}
}
else if (state == CLIENT_REQUEST) {
error = DhcpSendRequest(xid, *yiaddr_out, dhcp_addr);
if (error < 0) return error;
count = recvfrom(sock, buffer, sizeof(buffer), 0, &addr_in, sizeof(addr_in));
if (count > 0) {
//"Try parse Ack\n";
error = DhcpParseAck(xid, buffer, count);
if (error < 0) {
"$FG,6$DhcpParseOffer: error %d\n$FG$", error;
}
}
if (count > 0 && error >= 0) {
dhcp_addr = ntohl(addr_in.sin_addr.s_addr);
//"DHCP Ack from %08X\n", dhcp_addr;
state = CLIENT_REQUEST_ACCEPTED;
}
else if (++retries == MAX_RETRIES) {
"$FG,4$DhcpConfigure: max retries for REQUEST\n$FG$";
return -1;
}
}
}
return state;
}
I64 DhcpConfigure() {
I64 sock = socket(AF_INET, SOCK_DGRAM);
if (sock < 0)
return -1;
U32 yiaddr, dns_ip, router_ip, subnet_mask;
I64 state = DhcpConfigureInner(sock, &yiaddr, &dns_ip, &router_ip, &subnet_mask);
close(sock);
if (state == CLIENT_REQUEST_ACCEPTED) {
in_addr in;
in.s_addr = htonl(yiaddr);
"$FG,2$Obtained IP address %s\n$FG$", inet_ntoa(in);
IPv4SetAddress(yiaddr);
IPv4SetSubnet(router_ip, subnet_mask);
DnsSetResolverIPv4(dns_ip);
return 0;
}
else
return -1;
}
U0 Netcfg() {
SocketInit();
"$FG,7$Netcfg: Configuring network...\n$FG$";
I64 error = DhcpConfigure();
if (error < 0)
"$FG,4$DhcpConfigure: error %d\n$FG$", error;
}
+148
View File
@@ -0,0 +1,148 @@
// vim: set ft=cpp:
#include "::/Doc/Comm"
#define CMD_SOCKET 1
#define CMD_CLOSE 2
#define CMD_CONNECT_TCP 3
#define CMD_SEND 4
#define CMD_RECV 5
#define CMD_HELLO 0xAA
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
#define SOCK_RAW 3
#define AF_UNSPEC 0
#define AF_INET 2
#define AF_INET6 10
#define SNAIL_COM 3
#define SNAIL_TIMEOUT 500
#define SNAIL_FRAME_SIZE 112
static CComm* snail_comm;
static U8 ReadByte() {
U8 chr;
while (1) {
if (FifoU8Rem(snail_comm->RX_fifo, &chr))
return chr;
else
Yield;
}
}
static I8 ReadI8() {
I8 chr;
while (1) {
if (FifoU8Rem(snail_comm->RX_fifo, &chr))
return chr;
else
Yield;
}
}
static U0 ReadBlock(U8* buf, I64 count) {
while (count) {
if (FifoU8Rem(snail_comm->RX_fifo, buf)) {
buf++;
count--;
}
else Yield;
}
}
I64 SocketInit() {
U8 chr;
snail_comm = CommInit8n1(SNAIL_COM, 115200);
while (FifoU8Rem(snail_comm->RX_fifo, &chr)) {}
CommPutChar(SNAIL_COM, CMD_HELLO);
I64 max_time = cnts.jiffies + SNAIL_TIMEOUT * JIFFY_FREQ / 1000;
do {
if (FifoU8Rem(snail_comm->RX_fifo, &chr)) {
if (chr == CMD_HELLO) {
return 0;
}
else {
"$FG,6$Failed to initialize Snail -- wrong hello 0x%02X\n", chr;
"Are you using the right version of snail.py?\n$FG$";
throw;
}
return chr;
}
else
Yield;
}
while (cnts.jiffies < max_time);
"$FG,6$Failed to initialize Snail -- make sure COM%d "
"is properly configured & snail.py is running!\n$FG$", SNAIL_COM;
throw;
}
I64 socket(I64 domain, I64 type) {
CommPutChar(SNAIL_COM, CMD_SOCKET);
CommPutChar(SNAIL_COM, domain);
CommPutChar(SNAIL_COM, type);
return ReadI8();
}
I64 close(I64 sockfd) {
CommPutChar(SNAIL_COM, CMD_CLOSE);
CommPutChar(SNAIL_COM, sockfd);
return ReadI8();
}
I64 create_connection(U8* addr, U16 port) {
I64 sockfd = socket(AF_INET, SOCK_STREAM);
if (sockfd < 0)
return sockfd;
CommPutChar(SNAIL_COM, CMD_CONNECT_TCP);
CommPutChar(SNAIL_COM, sockfd);
CommPutChar(SNAIL_COM, StrLen(addr));
CommPutS(SNAIL_COM, addr);
CommPutChar(SNAIL_COM, port & 0xff);
CommPutChar(SNAIL_COM, port >> 8);
I64 error = ReadI8();
if (error < 0) {
close(sockfd);
return error;
}
return sockfd;
}
I64 recv(I64 sockfd, U8* buf, I64 len, I64 flags) {
// This will be problematic for UDP
if (len > SNAIL_FRAME_SIZE)
len = SNAIL_FRAME_SIZE;
CommPutChar(SNAIL_COM, CMD_RECV);
CommPutChar(SNAIL_COM, sockfd);
CommPutChar(SNAIL_COM, len);
CommPutChar(SNAIL_COM, flags);
I64 got = ReadI8();
if (got > 0)
ReadBlock(buf, got);
return got;
}
I64 send(I64 sockfd, U8* buf, I64 len, I64 flags) {
// FIXME: use frames
CommPutChar(SNAIL_COM, CMD_SEND);
CommPutChar(SNAIL_COM, sockfd);
CommPutChar(SNAIL_COM, len);
CommPutChar(SNAIL_COM, flags);
CommPutBlk(SNAIL_COM, buf, len);
return ReadI8();
}
+29
View File
@@ -0,0 +1,29 @@
// vim: set ft=c:
#exe {
if (SNAILNET_NATIVE_DRIVER == NULL) {
StreamPrint("#include \"::/Adam/Net/SnailLib\"");
}
}
// Higher-level, utility functions
I64 recvLine(I64 sock, U8* buffer, I64 size, I64 flags) {
I64 got = 0;
while (got + 1 < size) {
if (!recv(sock, buffer + got, 1, flags))
return -1;
if (buffer[got] == '\n')
break;
else if (buffer[got] != '\r')
got++;
}
// FIXME: safe but incorrect behavior on overflow
buffer[got] = 0;
return got;
}
I64 sendString(I64 sockfd, U8* str, I64 flags) {
return send(sockfd, str, StrLen(str), flags);
}
+920
View File
@@ -0,0 +1,920 @@
// vim: set ft=c:
// https://tools.ietf.org/html/rfc793
// See https://en.wikipedia.org/wiki/File:Tcp_state_diagram_fixed_new.svg
#define TCP_STATE_CLOSED 0
#define TCP_STATE_LISTEN 1
#define TCP_STATE_SYN_SENT 2
#define TCP_STATE_SYN_RECEIVED 3
#define TCP_STATE_ESTABLISHED 4
#define TCP_STATE_FIN_WAIT_1 5
#define TCP_STATE_FIN_WAIT_2 6
#define TCP_STATE_CLOSE_WAIT 7
#define TCP_STATE_CLOSING 8
#define TCP_STATE_LAST_ACK 9
#define TCP_STATE_TIME_WAIT 10
#define TCP_CONNECT_TIMEOUT 10000
#define TCP_DEFAULT_MSS 536
#define TCP_WINDOW_SIZE 8192
#define TCP_FLAG_FIN 0x01
#define TCP_FLAG_SYN 0x02
#define TCP_FLAG_RST 0x04
#define TCP_FLAG_PSH 0x08
#define TCP_FLAG_ACK 0x10
#define TCP_FLAG_URG 0x20
#define TCP_SRTT_ALPHA 0.9
#define TCP_RTO_MIN 0.2
#define TCP_RTO_MAX 10
#define TCP_RTO_BETA 2
class CTcpHeader {
U16 source_port;
U16 dest_port;
U32 seq;
U32 ack;
U8 data_offset;
U8 flags;
U16 window_size;
U16 checksum;
U16 urgent_pointer;
};
class CTcpSendBufHeader {
CTcpSendBufHeader* next;
F64 time_sent;
U32 length;
U32 retries;
U32 seq_start;
U32 seq_end;
};
class CTcpSocket {
CSocket sock;
I64 state;
U32 local_addr;
U16 local_port;
U32 remote_addr;
U32 remote_port;
U32 snd_una; // seq number of first unacknowledged octet
U32 snd_nxt; // seq number of next octet to send
U32 snd_wnd; // allowed number of unacknowledged outgoing octets
U32 mss; // maximum segment size
U32 rcv_nxt; // seq number of next octet to receive
U32 rcv_wnd; // allowed number of unacknowledged incoming octets
F64 conntime;
F64 srtt;
I64 recv_buf_size;
U8* recv_buf;
I64 recv_buf_read_pos;
I64 recv_buf_write_pos;
CTcpSocket* backlog_next;
CTcpSocket* backlog_first;
CTcpSocket* backlog_last;
I64 backlog_remaining;
CTcpSendBufHeader* send_buf_first;
CTcpSendBufHeader* send_buf_last;
//I64 rcvtimeo_ms;
//I64 recv_maxtime;
};
class CTcpPseudoHeader {
U32 source_addr;
U32 dest_addr;
U8 zeros;
U8 protocol;
U16 tcp_length;
};
// TODO: this takes up half a meg, change it to a binary tree or something
static CTcpSocket** tcp_bound_sockets;
static U16 tcp_next_source_port = RandU16();
static Bool TcpIsSynchronizedState(I64 state) {
return state == TCP_STATE_ESTABLISHED || state == TCP_STATE_FIN_WAIT_1
|| state == TCP_STATE_FIN_WAIT_2 || state == TCP_STATE_CLOSE_WAIT
|| state == TCP_STATE_CLOSING || state == TCP_STATE_LAST_ACK
|| state == TCP_STATE_TIME_WAIT;
}
static U16 TcpPartialChecksum(U32 sum, U8* header, I64 length) {
I64 nleft = length;
U16* w = header;
while (nleft > 1) {
sum += *(w++);
nleft -= 2;
}
return sum;
}
static U16 TcpFinalChecksum(U32 sum, U8* header, I64 length) {
I64 nleft = length;
U16* w = header;
while (nleft > 1) {
sum += *(w++);
nleft -= 2;
}
// mop up an odd byte, if necessary
if (nleft == 1) {
sum += ((*w) & 0x00ff);
}
// add back carry outs from top 16 bits to low 16 bits
sum = (sum >> 16) + (sum & 0xffff); // add hi 16 to low 16
sum += (sum >> 16); // add carry
return (~sum) & 0xffff;
}
I64 TcpPacketAlloc(U8** frame_out, U32 source_ip, U16 source_port, U32 dest_ip, U16 dest_port,
U32 seq, U32 ack, U8 flags, I64 length) {
U8* frame;
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_TCP, source_ip, dest_ip,
sizeof(CTcpHeader) + length);
if (index < 0)
return index;
CTcpHeader* hdr = frame;
hdr->source_port = htons(source_port);
hdr->dest_port = htons(dest_port);
hdr->seq = htonl(seq);
hdr->ack = htonl(ack);
hdr->data_offset = (sizeof(CTcpHeader) / 4) << 4;
hdr->flags = flags;
hdr->window_size = htons(TCP_WINDOW_SIZE / 2); // FIXME
hdr->checksum = 0;
hdr->urgent_pointer = 0;
*frame_out = frame + sizeof(CTcpHeader);
return index;
}
I64 TcpPacketFinish(I64 index, U32 source_ip, U32 dest_ip, U8* frame, I64 length,
CTcpSendBufHeader** send_buf_out) {
CTcpHeader* hdr = frame - sizeof(CTcpHeader);
CTcpPseudoHeader pseudo;
pseudo.source_addr = htonl(source_ip);
pseudo.dest_addr = htonl(dest_ip);
pseudo.zeros = 0;
pseudo.protocol = IP_PROTO_TCP;
pseudo.tcp_length = htons(sizeof(CTcpHeader) + length);
U32 sum = TcpPartialChecksum(0, &pseudo, sizeof(CTcpPseudoHeader));
hdr->checksum = TcpFinalChecksum(sum, hdr, sizeof(CTcpHeader) + length);
if (send_buf_out) {
CTcpSendBufHeader* sb = MAlloc(sizeof(CTcpSendBufHeader) + sizeof(CTcpHeader) + length);
sb->next = NULL;
sb->time_sent = tS;
sb->length = sizeof(CTcpHeader) + length;
sb->retries = 0;
sb->seq_start = ntohl(hdr->seq);
sb->seq_end = 0; // NEEDS TO BE SET UPSTREAM
MemCpy((sb(U8*)) + sizeof(CTcpSendBufHeader), frame, sizeof(CTcpHeader) + length);
*send_buf_out = sb;
}
return IPv4PacketFinish(index);
}
I64 TcpSend(U32 local_addr, U16 local_port, U32 remote_addr, U16 remote_port, U32 seq, U32 ack, U8 flags) {
U8* frame;
I64 index = TcpPacketAlloc(&frame,
local_addr, local_port, remote_addr, remote_port,
seq, ack, flags, 0);
if (index < 0)
return index;
return TcpPacketFinish(index, local_addr, remote_addr, frame, 0, NULL);
}
I64 TcpSend2(CTcpSocket* s, U8 flags) {
U8* frame;
I64 index = TcpPacketAlloc(&frame,
s->local_addr, s->local_port, s->remote_addr, s->remote_port,
s->snd_nxt, s->rcv_nxt, flags, 0);
if (index < 0)
return index;
if (flags & TCP_FLAG_SYN)
s->snd_nxt++;
if (flags & TCP_FLAG_FIN)
s->snd_nxt++;
//"Sent #%d, to %08X, err = %d\n", s->seq, s->remote_addr, error;
// FIXME: If the packet is SYN or FIN, we also need to queue for retransmit!
return TcpPacketFinish(index, s->local_addr, s->remote_addr, frame, 0, NULL);
}
I64 TcpSendData2(CTcpSocket* s, U8 flags, U8* data, I64 length) {
U8* frame;
I64 index = TcpPacketAlloc(&frame,
s->local_addr, s->local_port, s->remote_addr, s->remote_port,
s->snd_nxt, s->rcv_nxt, flags, length);
if (index < 0)
return index;
if (length)
MemCpy(frame, data, length);
if (flags & TCP_FLAG_SYN)
s->snd_nxt++;
s->snd_nxt += length;
if (flags & TCP_FLAG_FIN)
s->snd_nxt++;
//"Sent #%d, to %08X, err = %d\n", s->seq, s->remote_addr, error;
CTcpSendBufHeader* sb;
TcpPacketFinish(index, s->local_addr, s->remote_addr, frame, length, &sb);
sb->seq_end = s->snd_nxt;
// Append to SendBuf chain
if (s->send_buf_first)
s->send_buf_last->next = sb;
else
s->send_buf_first = sb;
s->send_buf_last = sb;
}
I64 TcpParsePacket(CTcpHeader** header_out, U8** data_out, I64* length_out, CIPv4Packet* packet) {
if (packet->proto != IP_PROTO_TCP)
return -1;
// FIXME: validate packet->length
// FIXME: checksum
CTcpHeader* hdr = packet->data;
I64 header_length = (hdr->data_offset >> 4) * 4;
//"TCP: in hdr %d, flags %02Xh, seq %d, ack %d, len %d, chksum %d\n",
// header_length, hdr->flags, ntohl(hdr->seq), ntohl(hdr->ack),
// packet->length - header_length, ntohs(hdr->checksum);
*header_out = hdr;
*data_out = packet->data + header_length;
*length_out = packet->length - header_length;
return 0;
}
/*
class CTcpSendBufHeader {
CTcpSendBufHeader* next;
F64 time_sent;
U32 length;
U32 retries;
U32 seq_start;
U32 seq_end;
};
*/
static U0 TcpSocketAckSendBufs(CTcpSocket* s, U32 seg_ack) {
F64 time = tS;
while (s->send_buf_first) {
CTcpSendBufHeader* sb = s->send_buf_first;
// There's no notion of smaller/greater than in modular arithemtic,
// we can only check if a number lies within some range.
// Here we check that
// sb->seq_end <= seg_ack <= s->snd_nxt
// because that will work for all meaningful ACKs.
I64 seg_ack_rel = (seg_ack - sb->seq_end) & 0xffffffff;
I64 snd_nxt_rel = (s->snd_nxt - sb->seq_end) & 0xffffffff;
if (seg_ack_rel <= snd_nxt_rel) {
// Update smoothed RTT
F64 rtt = time - sb->time_sent;
s->srtt = (s->srtt * TCP_SRTT_ALPHA) + ((1.0 - TCP_SRTT_ALPHA) * rtt);
//"ACK'd %d->%d (RTT %f ms)", sb->seq_start, sb->seq_end, rtt * 1000;
// Remove SendBuf from chain
s->send_buf_first = sb->next;
if (s->send_buf_first == NULL)
s->send_buf_last = NULL;
Free(sb);
}
else
break;
}
}
static U0 TcpSocketCheckSendBufs(CTcpSocket* s) {
F64 time = tS;
F64 rto = TCP_RTO_BETA * s->srtt;
if (rto < TCP_RTO_MIN) rto = TCP_RTO_MIN;
if (rto > TCP_RTO_MAX) rto = TCP_RTO_MAX;
while (s->send_buf_first) {
CTcpSendBufHeader* sb = s->send_buf_first;
if (time > sb->time_sent + rto) {
// Retransmit
"Retransmit %d->%d (%f ms)!\n", sb->seq_start, sb->seq_end, (time - sb->time_sent) * 1000;
U8* frame;
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_TCP, s->local_addr, s->remote_addr, sb->length);
if (index < 0)
return; // retry later I guess
MemCpy(frame, (sb(U8*)) + sizeof(CTcpSendBufHeader), sb->length);
IPv4PacketFinish(index);
sb->time_sent = tS;
// Move to the end of the chain
s->send_buf_first = sb->next;
sb->next = NULL;
if (s->send_buf_first)
s->send_buf_last->next = sb;
else
s->send_buf_first = sb;
s->send_buf_last = sb;
}
else
break;
}
}
I64 TcpSocketAccept(CTcpSocket* s, sockaddr* addr, I64 addrlen) {
if (s->state != TCP_STATE_LISTEN)
return -1;
while (1) {
// TODO: Thread safe?
if (s->backlog_first) {
CTcpSocket* new_socket = s->backlog_first;
"Retr %p\n", new_socket;
s->backlog_first = s->backlog_first->backlog_next;
if (!s->backlog_first)
s->backlog_last = NULL;
s->backlog_remaining++;
// TODO: this should be done in a way that doesn't block on accept()
I64 maxtime = cnts.jiffies + TCP_CONNECT_TIMEOUT * JIFFY_FREQ / 1000;
while (cnts.jiffies < maxtime) {
if (new_socket->state == TCP_STATE_ESTABLISHED || new_socket->state == TCP_STATE_CLOSED)
break;
else
Yield;
}
if (new_socket->state != TCP_STATE_ESTABLISHED) {
close(new_socket);
return -1;
}
return new_socket;
}
else
Yield;
}
no_warn addr; // FIXME
no_warn addrlen;
return -1;
}
I64 TcpSocketBind(CTcpSocket* s, sockaddr* addr, I64 addrlen) {
if (addrlen < sizeof(sockaddr_in))
return -1;
if (s->state != TCP_STATE_CLOSED)
return -1;
sockaddr_in* addr_in = addr;
U16 local_port = ntohs(addr_in->sin_port);
// TODO: address & stuff
if (tcp_bound_sockets[local_port] != NULL)
return -1;
tcp_bound_sockets[local_port] = s;
s->local_addr = IPv4GetAddress();
s->local_port = local_port;
return 0;
}
I64 TcpSocketClose(CTcpSocket* s) {
if (TcpIsSynchronizedState(s->state)) {
TcpSend2(s, TCP_FLAG_RST);
}
// Free backlog
CTcpSocket* backlog = s->backlog_first;
CTcpSocket* backlog2;
while (backlog) {
backlog2 = backlog->backlog_next;
close(backlog);
backlog = backlog2;
}
if (s->local_port)
tcp_bound_sockets[s->local_port] = NULL;
Free(s->recv_buf);
Free(s);
return 0;
}
I64 TcpSocketConnect(CTcpSocket* s, sockaddr* addr, I64 addrlen) {
if (addrlen < sizeof(sockaddr_in))
return -1;
if (s->state != TCP_STATE_CLOSED)
return -1;
sockaddr_in* addr_in = addr;
U16 local_port = 0x8000 + (tcp_next_source_port & 0x7fff);
tcp_next_source_port++;
// TODO: address & stuff
if (tcp_bound_sockets[local_port] != NULL)
return -1;
tcp_bound_sockets[local_port] = s;
s->local_addr = IPv4GetAddress();
s->local_port = local_port;
s->remote_addr = ntohl(addr_in->sin_addr.s_addr);
s->remote_port = ntohs(addr_in->sin_port);
s->snd_una = 0;
s->snd_nxt = 0;
s->snd_wnd = 0;
s->mss = TCP_DEFAULT_MSS;
s->rcv_nxt = 0;
s->rcv_wnd = TCP_WINDOW_SIZE;
s->conntime = tS;
TcpSend2(s, TCP_FLAG_SYN);
s->state = TCP_STATE_SYN_SENT;
// TODO: TcpSetTimeout
I64 maxtime = cnts.jiffies + TCP_CONNECT_TIMEOUT * JIFFY_FREQ / 1000;
while (cnts.jiffies < maxtime) {
if (s->state == TCP_STATE_ESTABLISHED || s->state == TCP_STATE_CLOSED)
break;
else
Yield;
}
if (s->state != TCP_STATE_ESTABLISHED)
return -1;
return 0;
}
I64 TcpSocketListen(CTcpSocket* s, I64 backlog) {
if (s->state != TCP_STATE_CLOSED)
return -1;
// Enter listen state. If a SYN packet arrives, it will be processed by TcpHandler,
// which opens the connection and puts the new socket into the listening socket's accept backlog.
s->state = TCP_STATE_LISTEN;
s->backlog_remaining = backlog;
return 0;
}
I64 TcpSocketRecvfrom(CTcpSocket* s, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen) {
no_warn flags;
no_warn src_addr; // FIXME
no_warn addrlen;
//"TcpSocketRecvfrom\n";
while (s->state == TCP_STATE_ESTABLISHED && s->recv_buf_read_pos == s->recv_buf_write_pos) {
TcpSocketCheckSendBufs(s);
Yield;
}
// TODO: this works for now, but we should be still able to receive data
// in connection-closing states
if ((s->state != TCP_STATE_ESTABLISHED && s->recv_buf_read_pos == s->recv_buf_write_pos)
|| len == 0)
return 0;
I64 read_pos = s->recv_buf_read_pos;
I64 write_pos = s->recv_buf_write_pos;
//I64 avail = (write_pos - read_pos) & (s->recv_buf_size);
I64 read_total = 0;
I64 step;
if (write_pos < read_pos) {
// We can read up to the end of the buffer
step = s->recv_buf_size - read_pos;
if (step > len)
step = len;
//"Read %d from %d..end\n", step, read_pos;
MemCpy(buf, s->recv_buf + read_pos, step);
buf += step;
len -= step;
read_pos = (read_pos + step) & (s->recv_buf_size - 1);
read_total += step;
// at this point, (len == 0 || read_pos == 0) must be true
}
if (len) {
step = write_pos - read_pos;
if (step > len)
step = len;
//"Read %d from start+%d..\n", step, read_pos;
MemCpy(buf, s->recv_buf + read_pos, step);
buf += step;
len -= step;
read_pos += step;
read_total += step;
}
s->recv_buf_read_pos = read_pos;
return read_total;
}
I64 TcpSocketSendto(CTcpSocket* s, U8* buf, I64 len, I64 flags, sockaddr_in* dest_addr, I64 addrlen) {
no_warn dest_addr;
no_warn addrlen;
no_warn flags;
I64 sent_total = 0;
while (s->state == TCP_STATE_ESTABLISHED && len) {
I64 can_send = (s->snd_una + s->snd_wnd - s->snd_nxt) & 0xffffffff;
// TODO: Keep trying
// Must be tied to a timeout; see RFC793/Managing-the-Window
//if (s->snd_wnd == 0)
// can_send = 1;
if (can_send == 0) {
if (sent_total > 0)
break;
else {
TcpSocketCheckSendBufs(s);
Yield;
}
}
else {
if (can_send > len)
can_send = len;
if (can_send > s->mss)
can_send = s->mss;
TcpSendData2(s, TCP_FLAG_ACK, buf, can_send);
buf += can_send;
len -= can_send;
}
}
return sent_total;
}
I64 TcpSocketSetsockopt(CTcpSocket* s, I64 level, I64 optname, U8* optval, I64 optlen) {
/*if (level == SOL_SOCKET && optname == SO_RCVTIMEO_MS && optlen == 8) {
s->rcvtimeo_ms = *(optval(I64*));
return 0;
}*/
no_warn s;
no_warn level;
no_warn optname;
no_warn optval;
no_warn optlen;
return -1;
}
CTcpSocket* TcpSocket(U16 domain, U16 type) {
if (domain != AF_INET || type != SOCK_STREAM)
return NULL;
CTcpSocket* s = MAlloc(sizeof(CTcpSocket));
s->sock.accept = &TcpSocketAccept;
s->sock.bind = &TcpSocketBind;
s->sock.close = &TcpSocketClose;
s->sock.connect = &TcpSocketConnect;
s->sock.listen = &TcpSocketListen;
s->sock.recvfrom = &TcpSocketRecvfrom;
s->sock.sendto = &TcpSocketSendto;
s->sock.setsockopt = &TcpSocketSetsockopt;
s->state = TCP_STATE_CLOSED;
s->send_buf_first = NULL;
s->send_buf_last = NULL;
s->recv_buf_size = TCP_WINDOW_SIZE;
s->recv_buf = MAlloc(s->recv_buf_size);
s->recv_buf_read_pos = 0;
s->recv_buf_write_pos = 0;
s->backlog_next = NULL;
s->backlog_first = NULL;
s->backlog_last = NULL;
s->backlog_remaining = 0;
/*s->rcvtimeo_ms = 0;
s->recv_maxtime = 0;
s->recv_buf = NULL;
s->recv_len = 0;
s->recv_addr.sin_family = AF_INET;
s->bound_to = 0;*/
return s;
}
U0 TcpSocketHandle(CTcpSocket* s, CIPv4Packet* packet, CTcpHeader* hdr, U8* data, I64 length) {
U32 seg_len = length;
if (hdr->flags & TCP_FLAG_FIN) seg_len++;
if (hdr->flags & TCP_FLAG_SYN) seg_len++;
U32 seg_seq = ntohl(hdr->seq);
if (s->state == TCP_STATE_LISTEN) {
// A new connection is being opened.
if ((hdr->flags & TCP_FLAG_SYN) && s->backlog_remaining > 0) {
//"SYN in from %08X:%d => %08X:%d.\n", packet->source_ip, ntohs(hdr->source_port),
// packet->dest_ip, ntohs(hdr->dest_port);
CTcpSocket* new_socket = TcpSocket(AF_INET, SOCK_STREAM);
new_socket->local_addr = IPv4GetAddress();
new_socket->local_port = s->local_port;
new_socket->remote_addr = packet->source_ip;
new_socket->remote_port = ntohs(hdr->source_port);
new_socket->snd_una = 0;
new_socket->snd_nxt = 0;
new_socket->snd_wnd = 0;
new_socket->mss = TCP_DEFAULT_MSS;
new_socket->rcv_nxt = ++seg_seq;
new_socket->rcv_wnd= TCP_WINDOW_SIZE;
new_socket->conntime = tS;
TcpSend2(new_socket, TCP_FLAG_SYN | TCP_FLAG_ACK);
new_socket->state = TCP_STATE_SYN_RECEIVED;
// FIXME FIXME FIXME FIXME
tcp_bound_sockets[new_socket->local_port] = new_socket;
if (s->backlog_last)
s->backlog_last->backlog_next = new_socket;
else
s->backlog_first = new_socket;
s->backlog_last = new_socket;
s->backlog_remaining--;
}
else {
//"REJ %08X:%d (as %08X:%d)\n", packet->source_ip, ntohs(hdr->source_port),
// packet->dest_ip, ntohs(hdr->dest_port);
TcpSend(packet->dest_ip, ntohs(hdr->dest_port), packet->source_ip, ntohs(hdr->source_port),
seg_seq + 1, seg_seq + 1, TCP_FLAG_ACK | TCP_FLAG_RST);
}
return;
}
if (s->state == TCP_STATE_CLOSED)
return;
Bool must_ack = FALSE;
// Process SYN
if (hdr->flags & TCP_FLAG_SYN) {
s->rcv_nxt = ++seg_seq;
//"Reset ACK to %d\n", s->ack;
must_ack = TRUE;
}
// Validate SEQ
Bool valid_seq;
if (seg_len == 0 && s->rcv_wnd == 0) {
valid_seq = (seg_seq == s->rcv_nxt);
}
else {
// At least one of these must be true:
// RCV.NXT =< SEG.SEQ < RCV.NXT+RCV.WND
// RCV.NXT =< SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND
I64 rel_seq = ((seg_seq - s->rcv_nxt) & 0xffffffff);
I64 rel_seq_end = ((seg_seq + seg_len - 1 - s->rcv_nxt) & 0xffffffff);
if (rel_seq < s->rcv_wnd || rel_seq_end < s->rcv_wnd)
valid_seq = TRUE;
else
valid_seq = FALSE;
}
if (!valid_seq)
"SEQ error: seg_seq %d, seg_len %d, rcv_nxt %d, rcv_wnd %d\n", seg_seq, seg_len, s->rcv_nxt, s->rcv_wnd;
// Process ACK
if (hdr->flags & TCP_FLAG_ACK) {
U32 seg_ack = ntohl(hdr->ack);
// ACK is acceptable iff SND.UNA < SEG.ACK =< SND.NXT
I64 rel_ack = ((seg_ack - s->snd_una) & 0xffffffff);
I64 rel_nxt = ((s->snd_nxt - s->snd_una) & 0xffffffff);
// RFC 793 is poorly worded in this regard, unacceptable ACK
// is not the opposite of an acceptible (= new) ACK!
// TODO: Instead of zero, we should compare rel_ack to some NEGATIVE_CONSTANT,
// so that we don't unnecessarily try to correct every slightly delayed ACK
if (/*0 < rel_ack &&*/ rel_ack <= rel_nxt) {
TcpSocketAckSendBufs(s, seg_ack);
// Accept ACK
s->snd_una = seg_ack;
if (s->state == TCP_STATE_SYN_SENT && (hdr->flags & TCP_FLAG_SYN)) {
s->state = TCP_STATE_ESTABLISHED;
s->srtt = tS - s->conntime;
//"Initial RTT: %f ms", s->srtt * 1000;
}
else if (s->state == TCP_STATE_SYN_RECEIVED) {
//"Connection established.\n";
s->state = TCP_STATE_ESTABLISHED;
s->srtt = tS - s->conntime;
//"Initial RTT: %f ms", s->srtt * 1000;
}
}
else {
// Unacceptable ACK
"Bad ACK; state %d, seg_ack %d, snd_nxt %d\n", s->state, seg_ack, s->snd_nxt;
if (s->state == TCP_STATE_LISTEN || s->state == TCP_STATE_SYN_SENT
|| s->state == TCP_STATE_SYN_RECEIVED) {
// Reset
TcpSend(packet->dest_ip, ntohs(hdr->dest_port), packet->source_ip, ntohs(hdr->source_port),
seg_ack, seg_seq + seg_len, TCP_FLAG_ACK | TCP_FLAG_RST);
}
else if (TcpIsSynchronizedState(s->state)) {
// Send a 'corrective' ACK
must_ack = TRUE;
}
}
}
// Process RST
if (hdr->flags & TCP_FLAG_RST) {
if ((s->state == TCP_STATE_SYN_SENT)) {
// If acknowledged
if (s->snd_una == s->snd_nxt) {
"Connection refused\n";
s->state = TCP_STATE_CLOSED;
return;
}
}
else {
if (valid_seq) {
"Connection reset by peer\n";
s->state = TCP_STATE_CLOSED;
return;
}
}
"Spurious RST\n";
}
// FIXME check remote addr & port
// Process data
if (valid_seq) {
s->snd_wnd = hdr->window_size;
if (s->state == TCP_STATE_ESTABLISHED) {
I64 write_pos = s->recv_buf_write_pos;
//"%d in @ %d", length, write_pos;
// Skip retransmitted bytes
while (length && seg_seq != s->rcv_nxt) {
seg_seq = (seg_seq + 1) & 0xffffffff;
data++;
length--;
}
// ugh!
I64 i = 0;
for (i = 0; i < length; i++) {
I64 next_pos = (write_pos + 1) & (s->recv_buf_size - 1);
if (next_pos == s->recv_buf_read_pos)
break;
s->recv_buf[write_pos] = data[i];
write_pos = next_pos;
}
s->recv_buf_write_pos = write_pos;
s->rcv_nxt += i;
//"; %d saved\n", i;
if (i > 0)
must_ack = TRUE;
if (hdr->flags & TCP_FLAG_FIN) {
s->rcv_nxt++;
s->state = TCP_STATE_CLOSE_WAIT;
must_ack = TRUE;
}
}
}
if (must_ack) {
TcpSend2(s, TCP_FLAG_ACK);
}
}
I64 TcpHandler(CIPv4Packet* packet) {
CTcpHeader* hdr;
U8* data;
I64 length;
I64 error = TcpParsePacket(&hdr, &data, &length, packet);
if (error < 0)
return error;
U16 dest_port = ntohs(hdr->dest_port);
//"%u => %p\n", dest_port, tcp_bound_sockets[dest_port];
CTcpSocket* s = tcp_bound_sockets[dest_port];
// FIXME: should also check that bound address is INADDR_ANY,
// OR packet dest IP matches bound address
if (s != NULL) {
TcpSocketHandle(s, packet, hdr, data, length);
}
else {
// TODO: Send RST as per RFC793/Reset-Generation
}
return error;
}
U0 TcpInit() {
tcp_bound_sockets = MAlloc(65536 * sizeof(CTcpSocket*));
MemSet(tcp_bound_sockets, 0, 65536 * sizeof(CTcpSocket*));
}
TcpInit;
RegisterL4Protocol(IP_PROTO_TCP, &TcpHandler);
RegisterSocketClass(AF_INET, SOCK_STREAM, &TcpSocket);
+243
View File
@@ -0,0 +1,243 @@
// vim: set ft=c:
class CUdpHeader {
U16 source_port;
U16 dest_port;
U16 length;
U16 checksum;
};
class CUdpSocket {
CSocket sock;
I64 rcvtimeo_ms;
I64 recv_maxtime;
U8* recv_buf;
I64 recv_len;
sockaddr_in recv_addr;
U16 bound_to;
};
// TODO: this takes up half a meg, change it to a binary tree or something
static CUdpSocket** udp_bound_sockets;
I64 UdpPacketAlloc(U8** frame_out, U32 source_ip, U16 source_port, U32 dest_ip, U16 dest_port, I64 length) {
U8* frame;
I64 index = IPv4PacketAlloc(&frame, IP_PROTO_UDP, source_ip, dest_ip, sizeof(CUdpHeader) + length);
if (index < 0)
return index;
CUdpHeader* hdr = frame;
hdr->source_port = htons(source_port);
hdr->dest_port = htons(dest_port);
hdr->length = htons(sizeof(CUdpHeader) + length);
hdr->checksum = 0;
*frame_out = frame + sizeof(CUdpHeader);
return index;
}
I64 UdpPacketFinish(I64 index) {
return IPv4PacketFinish(index);
}
I64 UdpParsePacket(U16* source_port_out, U16* dest_port_out, U8** data_out, I64* length_out, CIPv4Packet* packet) {
if (packet->proto != IP_PROTO_UDP)
return -1;
CUdpHeader* hdr = packet->data;
//"UDP: from %d, to %d, len %d, chksum %d\n",
// ntohs(hdr->source_port), ntohs(hdr->dest_port), ntohs(hdr->length), ntohs(hdr->checksum);
// FIXME: validate packet->length
*source_port_out = ntohs(hdr->source_port);
*dest_port_out = ntohs(hdr->dest_port);
//ntohs(hdr->length)
//ntohs(hdr->checksum)
*data_out = packet->data + sizeof(CUdpHeader);
*length_out = packet->length - sizeof(CUdpHeader);
return 0;
}
I64 UdpSocketAccept(CUdpSocket* s, sockaddr* addr, I64 addrlen) {
no_warn s;
no_warn addr;
no_warn addrlen;
return -1;
}
I64 UdpSocketBind(CUdpSocket* s, sockaddr* addr, I64 addrlen) {
if (addrlen < sizeof(sockaddr_in))
return -1;
if (s->bound_to)
return -1;
sockaddr_in* addr_in = addr;
U16 port = ntohs(addr_in->sin_port);
// TODO: address & stuff
if (udp_bound_sockets[port] != NULL)
return -1;
udp_bound_sockets[port] = s;
s->bound_to = port;
return 0;
}
I64 UdpSocketClose(CUdpSocket* s) {
if (s->bound_to)
udp_bound_sockets[s->bound_to] = NULL;
Free(s);
return 0;
}
I64 UdpSocketConnect(CUdpSocket* s, sockaddr* addr, I64 addrlen) {
// FIXME: implement
no_warn s;
no_warn addr;
no_warn addrlen;
return -1;
}
I64 UdpSocketListen(CUdpSocket* s, I64 backlog) {
no_warn s;
no_warn backlog;
return -1;
}
I64 UdpSocketRecvfrom(CUdpSocket* s, U8* buf, I64 len, I64 flags, sockaddr* src_addr, I64 addrlen) {
no_warn flags;
s->recv_buf = buf;
s->recv_len = len;
if (s->rcvtimeo_ms != 0)
s->recv_maxtime = cnts.jiffies + s->rcvtimeo_ms * JIFFY_FREQ / 1000;
while (s->recv_buf != NULL) {
// Check for timeout
if (s->rcvtimeo_ms != 0 && cnts.jiffies > s->recv_maxtime) {
// TODO: seterror(EWOULDBLOCK)
s->recv_len = -1;
break;
}
Yield;
}
// TODO: addrlen
if (src_addr) {
// wtf? can't copy structs with '='?
MemCpy((src_addr(sockaddr_in*)), &s->recv_addr, addrlen);
}
return s->recv_len;
}
I64 UdpSocketSendto(CSocket* s, U8* buf, I64 len, I64 flags, sockaddr_in* dest_addr, I64 addrlen) {
no_warn s;
no_warn flags;
if (addrlen < sizeof(sockaddr_in))
return -1;
U8* frame;
I64 index = UdpPacketAlloc(&frame, IPv4GetAddress(), 0, ntohl(dest_addr->sin_addr.s_addr),
ntohs(dest_addr->sin_port), len);
if (index < 0)
return -1;
MemCpy(frame, buf, len);
return UdpPacketFinish(index);
}
I64 UdpSocketSetsockopt(CUdpSocket* s, I64 level, I64 optname, U8* optval, I64 optlen) {
if (level == SOL_SOCKET && optname == SO_RCVTIMEO_MS && optlen == 8) {
s->rcvtimeo_ms = *(optval(I64*));
return 0;
}
return -1;
}
CUdpSocket* UdpSocket(U16 domain, U16 type) {
if (domain != AF_INET || type != SOCK_DGRAM)
return NULL;
CUdpSocket* s = MAlloc(sizeof(CUdpSocket));
s->sock.accept = &UdpSocketAccept;
s->sock.bind = &UdpSocketBind;
s->sock.close = &UdpSocketClose;
s->sock.connect = &UdpSocketConnect;
s->sock.listen = &UdpSocketListen;
s->sock.recvfrom = &UdpSocketRecvfrom;
s->sock.sendto = &UdpSocketSendto;
s->sock.setsockopt = &UdpSocketSetsockopt;
s->rcvtimeo_ms = 0;
s->recv_maxtime = 0;
s->recv_buf = NULL;
s->recv_len = 0;
s->recv_addr.sin_family = AF_INET;
s->bound_to = 0;
return s;
}
I64 UdpHandler(CIPv4Packet* packet) {
U16 source_port;
U16 dest_port;
U8* data;
I64 length;
I64 error = UdpParsePacket(&source_port, &dest_port, &data, &length, packet);
if (error < 0)
return error;
//"%u => %p\n", dest_port, udp_bound_sockets[dest_port];
CUdpSocket* s = udp_bound_sockets[dest_port];
// FIXME: should also check that bound address is INADDR_ANY,
// OR packet dest IP matches bound address
if (s != NULL) {
if (s->recv_buf) {
I64 num_recv = s->recv_len;
if (num_recv > length)
num_recv = length;
MemCpy(s->recv_buf, data, num_recv);
// signal that we received something
s->recv_buf = NULL;
s->recv_len = num_recv;
// TODO: we keep converting n>h>n, fuck that
s->recv_addr.sin_port = htons(source_port);
s->recv_addr.sin_addr.s_addr = htonl(packet->source_ip);
}
}
return error;
}
U0 UdpInit() {
udp_bound_sockets = MAlloc(65536 * sizeof(CUdpSocket*));
MemSet(udp_bound_sockets, 0, 65536 * sizeof(CUdpSocket*));
}
UdpInit;
RegisterL4Protocol(IP_PROTO_UDP, &UdpHandler);
RegisterSocketClass(AF_INET, SOCK_DGRAM, &UdpSocket);
+91
View File
@@ -0,0 +1,91 @@
// vim: set ft=cpp:
#include "::/Adam/Net/Http"
I64 UrlGet(U8* url, U8** data_out = NULL, I64* size_out = NULL) {
CUrl curl;
UrlInit(&curl);
I64 error = 0;
if (UrlParse(url, &curl)) {
if (!StrCmp(curl.protocol, "http"))
error = HttpGet(curl.host, curl.port, curl.path, data_out, size_out);
else
error = URL_EPROTOCOL;
}
else
error = URL_EPARSE;
UrlFree(&curl);
return error;
}
I64 UrlGetWithProgress(U8* url, U8** data_out, I64* size_out) {
CUrl curl;
UrlInit(&curl);
I64 error = 0;
I64 size = 0;
if (UrlParse(url, &curl)) {
if (!StrCmp(curl.protocol, "http")) {
I64 sock = HttpOpenGet(curl.host, curl.port, curl.path, &size);
if (sock > 0) {
if (size >= 0) {
U8* data = MAlloc(1 + size);
I64 total = 0;
I64 progress = 0;
"[$FG,3$";
while (total < size) {
I64 step = size - total;
if (step > 1024)
step = 1024;
I64 got = recv(sock, data + total, step, 0);
if (got <= 0) {
error = HTTP_EEOF;
break;
}
total += got;
I64 new_progress = (20 * total + size - 1) / size;
while (progress < new_progress) {
'' 0xfe;
progress++;
}
}
}
else
error = HTTP_ECONTENTLENGTH;
close(sock);
if (error) {
"$FG,4$x\n$FG$";
Free(data);
}
else {
"$FG$]\n";
data[total] = 0;
*data_out = data;
*size_out = total;
}
}
else
error = sock;
}
else
error = URL_EPROTOCOL;
}
else
error = URL_EPARSE;
UrlFree(&curl);
return error;
}
+73
View File
@@ -0,0 +1,73 @@
// vim: set ft=cpp:
#define URL_EPARSE (-201)
#define URL_EPROTOCOL (-202)
class CUrl {
U8* protocol;
U8* host;
U16 port;
U8* path;
};
U0 UrlInit(CUrl* url) {
url->protocol = 0;
url->host = 0;
url->port = 0;
url->path = 0;
}
U0 UrlFree(CUrl* url) {
Free(url->protocol);
Free(url->host);
Free(url->path);
UrlInit(url);
}
Bool UrlParse(U8* url, CUrl* url_out) {
U8* colon = StrFirstOcc(url, ":");
U8* protosep = StrFind("//", url);
if (colon && colon < protosep) {
I64 len = colon - url;
url_out->protocol = MAlloc(len + 1);
MemCpy(url_out->protocol, url, len);
url_out->protocol[len] = 0;
url = colon + 1;
while (*url == '/')
url++;
}
else {
url_out->protocol = StrNew("http");
}
I64 pos = 0;
while (url[pos]) {
if (url[pos] == ':' || url[pos] == '/') {
url_out->host = MAlloc(pos + 1);
MemCpy(url_out->host, url, pos);
url_out->host[pos] = 0;
if (url[pos] == ':') {
I64 port = 0;
U8* end = 0;
port = Str2I64(url + pos + 1, 10, &end);
url_out->port = port;
url_out->path = StrNew(end);
}
else {
url_out->path = StrNew(url + pos);
}
return TRUE;
}
pos++;
}
url_out->host = StrNew(url);
return TRUE;
}
+522
View File
@@ -0,0 +1,522 @@
// vim: set ft=cpp:
#define LAMBDA_BITMAP 0x006336363C181B0E
#define LAMBDA "\xFB"
#define LAMBDA_CHARCODE 0xFB
#define AUTOCOMPLETE_MAX_RESULTS 15
// -------------------------------------
// History
class histent {
histent* next;
U8 text[0];
};
histent* history;
Bool LoadHistory() {
history = NULL;
return TRUE;
}
U0 HistoryPush(U8* line) {
if (!line[0])
return;
if (history && !StrCmp(history->text, line))
return;
I64 len = StrLen(line);
histent* ent = MAlloc(sizeof(histent) + len + 1);
StrCpy(ent->text, line);
ent->next = history;
history = ent;
}
// -------------------------------------
// Line input
U8* ReadLine(U8* prompt, I64 (*autocomplete)(U8* buffer) = NULL) {
static U8 buf[256];
I64 len = 0;
histent* hist = history;
buf[0] = 0;
show_prompt:
DocBottom;
"" prompt;
"" buf;
while (len < 255) {
I64 scan;
I64 ch = GetKey(&scan, FALSE, FALSE);
if (ch == CH_BACKSPACE) {
if (len > 0) {
'' CH_BACKSPACE;
--len;
}
}
else if (ch == CH_ESC) {
len = 0;
buf[len] = 0;
EdLineDel(DocPut);
goto show_prompt;
}
else if (ch == CH_SHIFT_ESC) {
return 0;
}
else if (ch == '\t' && autocomplete) {
buf[len] = 0;
// Completing path or last argument?
U8* start = StrLastOcc(buf, " ");
if (start)
start++;
else
start = buf;
// Find matching results
I64 results = autocomplete(start);
len = StrLen(buf);
// If multiple results were printed,
// we need to wait for the WinMgr (or whoever)
// to catch up. UGH!
if (results > 1)
Sleep(200);
EdLineDel(DocPut);
goto show_prompt;
}
else if (ch == 0 && scan.u8[0] == SC_CURSOR_UP) {
if (hist) {
StrCpy(buf, hist->text);
len = StrLen(buf);
hist = hist->next;
EdLineDel(DocPut);
goto show_prompt;
}
}
else if (ch) {
'' ch;
if (ch == '\n')
break;
buf[len++] = ch;
}
}
buf[len] = 0;
return buf;
}
// -------------------------------------
// Parse
I64 Tokenize(U8* str, U8** tokens) {
I64 count = 0;
Bool started = FALSE;
while (*str) {
if (*str == ' ') {
if (started) {
*str = 0;
started = FALSE;
}
}
else if (!started) {
tokens[count] = str;
count++;
started = TRUE;
}
str++;
}
return count;
}
U0 TransformCommand(U8* command, U8* exec_buf) {
Bool upperize = TRUE;
I64 pos = 0;
for (; *command; command++) {
if (*command == '-')
upperize = TRUE;
else if (upperize) {
exec_buf[pos++] = ToUpper(*command);
upperize = FALSE;
}
else
exec_buf[pos++] = *command;
}
exec_buf[pos] = 0;
}
// -------------------------------------
// Autocomplete
class CHashTableIter {
CHashTable* ht;
I64 i;
CHash* curr;
I64 recursive;
};
U0 HashTableIterBegin(CHashTableIter* it, CHashTable* ht, I64 recursive) {
it->ht = ht;
it->i = 0;
it->curr = NULL;
it->recursive = recursive;
}
CHash* HashTableIterNext(CHashTableIter* it) {
// End of current bucket?
while (!it->curr) {
// End of hash table?
while (it->i >= it->ht->mask) {
// If recursive search is enabled,
// jump to the next table in chain
if (it->recursive) {
if (!it->ht->next)
return NULL;
it->ht = it->ht->next;
it->i = 0;
}
else
return NULL;
}
it->curr = it->ht->body[it->i];
it->i++;
}
CHash* ret = it->curr;
it->curr = it->curr->next;
return ret;
}
class CAutocompleteIter {
U8* query;
I64 length;
CDirEntry* entries;
CDirEntry* de;
CHashTableIter hti;
};
class CAutocompleteResult {
// Exactly one of these will be set
CDirEntry* de;
CHashFun* fun;
};
U0 AutocompleteIterRewind(CAutocompleteIter* it) {
it->de = it->entries;
HashTableIterBegin(&it->hti, Fs->hash_table, TRUE);
}
U0 AutocompleteIterBegin(CAutocompleteIter* it, U8* query) {
it->query = query;
it->length = StrLen(query);
U8* mask = MStrPrint("%s*", query);
try {
it->entries = FilesFind(mask);
}
catch {
it->entries = NULL;
}
Free(mask);
AutocompleteIterRewind(it);
}
I64 AutocompleteIterNext(CAutocompleteIter* it, CAutocompleteResult* out) {
// Go through all file matches first
while (it->de) {
if (it->de->name[0] != '.') {
// Return the DE, iteration will resume at the next one
out->de = it->de;
out->fun = NULL;
it->de = it->de->next;
return TRUE;
}
it->de = it->de->next;
}
// Go through all hashtable matches
CHash* next;
while ((next = HashTableIterNext(&it->hti))) {
// Function?
if ((next->type & HTT_FUN) != 0
&& !StrNICmp(next->str, it->query, it->length)) {
out->de = NULL;
out->fun = next(CHashFun*);
return TRUE;
}
}
return FALSE;
}
U0 AutocompleteIterEnd(CAutocompleteIter* it) {
DirTreeDel(it->entries);
}
U0 AutocompleteSetResult(U8* buffer, U8* str, I64 length) {
// Completing path or last argument?
U8* start = StrLastOcc(buffer, "/");
if (start)
start++;
else
start = buffer;
MemCpy(start, str, length);
start[length] = 0;
}
I64 StrCommonSubset(U8* a, U8* b) {
I64 len = 0;
while (*a && *b == *a) {
a++;
b++;
len++;
}
return len;
}
// No matches -> return 0
// 1 match -> return 1, set *p_match to alloced
// multiple matches -> print matches, return count
I64 Autocomplete(U8* buffer) {
// This is somewhat complicated, because we want
// to avoid any unnecessary allocations.
CAutocompleteIter it;
CAutocompleteResult first, next;
AutocompleteIterBegin(&it, buffer);
if (!AutocompleteIterNext(&it, &first)) {
// No results.
return 0;
}
I64 count;
U8* str;
if (!AutocompleteIterNext(&it, &next)) {
// Single result.
if (first.de)
str = first.de->name;
else if (first.fun)
str = first.fun->str;
AutocompleteSetResult(buffer, str, StrLen(str));
count = 1;
}
else {
U8* common_base = NULL;
I64 common_length = 0;
AutocompleteIterRewind(&it);
count = 0;
"\n";
while (count < AUTOCOMPLETE_MAX_RESULTS
&& AutocompleteIterNext(&it, &next)) {
if (next.de) {
str = next.de->name;
"$FG,4$%s\n", str;
}
else if (next.fun) {
str = next.fun->str;
"$FG,3$%s\n", str;
}
if (!common_base) {
common_base = str;
common_length = StrLen(common_base);
}
else {
I64 new_common = StrCommonSubset(common_base, str);
if (common_length > new_common)
common_length = new_common;
}
count++;
}
if (AutocompleteIterNext(&it, &next))
"$FG,6$Too many results, display truncated\n$FG$";
else if (common_length > StrLen(buffer))
AutocompleteSetResult(buffer, common_base, common_length);
}
AutocompleteIterEnd(&it);
return count;
}
// -------------------------------------
// Shell
Bool skip_intro = FALSE;
U8* DirCurShort() {
U8* dir = DirCur();
// FIXME!!
if (!StrCmp(dir, "C:/Home")) {
Free(dir);
return StrNew("~");
}
else
return dir;
}
CHashFun* FindFunction(U8* name) {
CHash* result = HashFind(name, Fs->hash_table, HTT_FUN);
if (result && (result->type & HTT_FUN) != 0)
return result(CHashFun *);
else
return NULL;
}
Bool IsPath(U8* str) {
for (; *str; str++) {
if (*str == '/')
return TRUE;
}
return FALSE;
}
U8* Prompt() {
// TODO: Avoid malloc if we can rely on MAX_PATH
static U8 buf[200];
U8* dir = DirCurShort();
//StrPrint(buf, "$FG,5$" LAMBDA " $FG,8$%s $FG,0$", dir);
StrPrint(buf, "$FG,8$%s $FG,5$" LAMBDA " $FG,0$", dir);
Free(dir);
return buf;
}
U0 PatchFont() {
U64* font = sys_font_std;
font[LAMBDA_CHARCODE] = LAMBDA_BITMAP;
}
// -------------------------------------
// Main
U0 Intro() {
"\n"
"$FG,1$- #include files by absolute or relative path\n"
" $FG,7$" LAMBDA "$FG,0$ ./Lsh $FG,7$=> #include \"Lsh\"\n"
"\n"
"$FG,1$- Call functions\n"
" $FG,7$" LAMBDA "$FG,0$ cd .. $FG,7$=> Cd(\"..\");\n"
" $FG,7$" LAMBDA "$FG,0$ dir $FG,7$=> Dir;\n"
" $FG,7$" LAMBDA "$FG,0$ ed Program.HC $FG,7$=> Ed(\"Program.HC\");\n"
" $FG,7$" LAMBDA "$FG,0$ file-mgr $FG,7$=> FileMgr;\n"
"\n"
"$FG,1$- Execute code directly\n"
" $FG,7$" LAMBDA "$FG,0$ 'DskChg('B');\n"
"\n"
"$FG,1$- $FG,0$Esc$FG,1$ deletes line\n"
"$FG,1$- $FG,0$Tab$FG,1$ auto-completes paths\n"
"$FG,1$- $FG,0$Shift-Esc$FG,1$ quits\n"
"$FG,1$- $FG,0$Up Arrow$FG,1$ recalls previous commands\n"
"\n";
}
U0 ParseAndExecute(U8* line) {
if (line[0] == '#')
return;
if (line[0] == '\'') {
ExePutS(line + 1);
return;
}
U8* tokens[10];
I64 count = Tokenize(line, tokens);
if (count) {
if (IsPath(tokens[0])) {
"$FG$";
ExePrint("#include \"%s\";", tokens[0]);
}
else {
U8 exec_buf[200];
TransformCommand(tokens[0], exec_buf);
CHashFun* fun = FindFunction(exec_buf);
if (!fun) {
"%s: $FG,4$function not found$FG$\n", exec_buf;
return;
}
if (count > 1) {
CatPrint(exec_buf, "(");
I64 have;
for (have = 1; have < count; have++) {
if (have > 1)
CatPrint(exec_buf, ",");
CatPrint(exec_buf, "\"%s\"", tokens[have]);
}
CatPrint(exec_buf, ")");
}
CatPrint(exec_buf, ";");
"$FG,7$%s\n$FG$", exec_buf;
ExePutS(exec_buf);
}
}
}
U0 Lsh() {
PatchFont();
LoadHistory();
if (!skip_intro) {
"$FG,8$Welcome to $FG,5$Lambda Shell$FG,8$!\n";
"Type $FG,0$intro$FG,8$ for a quick introduction.\n\n";
}
while (1) {
U8* p = Prompt();
U8* line = ReadLine(p, &Autocomplete);
if (!line || !StrCmp(line, "exit"))
break;
HistoryPush(line);
try {
ParseAndExecute(line);
}
catch {
PutExcept();
}
}
"$FG$\n";
}
+98
View File
@@ -0,0 +1,98 @@
// vim: set ft=cpp:
#include "::/Doc/Comm"
#define MFA_COM 1
static CComm* comm;
static U8 in_buf[256];
U8* ReadStr() {
I64 len = 0;
while (1) {
if (FifoU8Rem(comm->RX_fifo, in_buf + len)) {
if (in_buf[len] == '\n')
break;
len++;
}
else Yield;
}
in_buf[len] = 0;
"%s\n", in_buf;
return in_buf;
}
U0 ReadBlk(U8* buf, I64 count) {
while (count) {
if (FifoU8Rem(comm->RX_fifo, buf)) {
buf++;
count--;
}
else Yield;
}
}
U0 Mfa() {
U8 command;
comm = CommInit8n1(MFA_COM, 115200);
while (FifoU8Rem(comm->RX_fifo, &command)) {}
"$FG,5$minimalist file access\n"
"\n"
"$FG,8$- configure your VM's COM1 as follows:\n"
"$FG,0$ TCP, server, port 7770\n"
"$FG,8$- use $FG,5$mfa.py$FG,8$ to send commands & files\n"
"\n"
"awaiting commands. press Esc to quit\n";
while (1) {
next:
I64 key;
if (ScanKey(&key) && (key == CH_ESC || key == CH_SHIFT_ESC || key == 'q'))
break;
if (!FifoU8Rem(comm->RX_fifo, &command)) {
Sleep(50);
goto next;
}
'' command;
U8* line = ReadStr();
I64 size;
if (command == 'L') {
U8* file = FileRead(line, &size);
CommPrint(MFA_COM, "S%d\n", size);
CommPutBlk(MFA_COM, file, size);
Free(file);
"Sent %d\n", size;
}
else if (command == 'P') {
U8 filename[255];
StrCpy(filename, line);
U8* next = ReadStr();
StrScan(next, "S%d", &size);
U8* file_buf = MAlloc(size);
ReadBlk(file_buf, size);
FileWrite(filename, file_buf, size);
Free(file_buf);
"Wrote %d\n", size;
}
else if (command == '\'') {
ExePutS(line);
}
else if (command == '?') {
CommPutS(MFA_COM, "!\n");
}
}
"$FG$";
}
Mfa;
+463
View File
@@ -0,0 +1,463 @@
// vim: set ft=cpp:
#include "::/Adam/Net/Url"
#define PKG_EURL (-20001)
#define PKG_EMOUNT (-20002)
#define PKG_EMANIFEST (-20003)
#define PKG_EVERSION (-20004)
#define PKG_EOSVERSION (-20005)
#define PKG_EUNSUITABLE (-20006)
#define PKG_VERSION 11
static U8* PKG_BASE_URL = "http://shrine.lanceward.net/packages";
static U8* PKG_LOCAL_REPO = "::/Misc/Packages";
static U8* PKG_TMP_DIR = "::/Tmp/PkgTmp";
class CPkgInfo {
U8* package_name;
I32 pkgmin;
I32 release;
I32 osmin;
I32 osmax;
I64 size;
U8* version;
U8* installdir;
U8* iso_c;
U8* post_install_doc;
};
// TODO: Is there a built-in for this?
static U8* StripDir(U8* file_path) {
U8* slash = StrLastOcc(file_path, "/");
if (slash)
return slash + 1;
else
return file_path;
}
U0 PkgInfoInit(CPkgInfo* pinf) {
pinf->package_name = 0;
pinf->pkgmin = 0x7fffffff;
pinf->release = 0;
pinf->osmin = 0;
pinf->osmax = 0x7fffffff;
pinf->size = 0;
pinf->version = 0;
pinf->installdir = 0;
pinf->iso_c = 0;
pinf->post_install_doc = 0;
}
U0 PkgInfoFree(CPkgInfo* pinf) {
Free(pinf->package_name);
Free(pinf->version);
Free(pinf->installdir);
Free(pinf->iso_c);
Free(pinf->post_install_doc);
PkgInfoInit(pinf);
}
// Returns 0 or error code
I64 PkgParseManifest(CPkgInfo* pinf, U8* manifest) {
U8* key = manifest;
while (*key) {
//"?%s", key;
U8* end = StrFirstOcc(key, "\n");
if (end) {
*end = 0;
end++;
}
else
end = key + StrLen(key);
U8* value = StrFirstOcc(key, "\t");
if (!value)
return PKG_EMANIFEST;
*value = 0;
value++;
//"%s=%s;\n", key, value;
if (0) {}
else if (!StrCmp(key, "name")) { Free(pinf->package_name); pinf->package_name = StrNew(value); }
else if (!StrCmp(key, "pkgmin")) { pinf->pkgmin = Str2I64(value); }
else if (!StrCmp(key, "release")) { pinf->release = Str2I64(value); }
else if (!StrCmp(key, "osmin")) { pinf->osmin = Str2I64(value); }
else if (!StrCmp(key, "osmax")) { pinf->osmax = Str2I64(value); }
else if (!StrCmp(key, "size")) { pinf->size = Str2I64(value); }
else if (!StrCmp(key, "version")) { Free(pinf->version); pinf->version = StrNew(value); }
else if (!StrCmp(key, "installdir")) { Free(pinf->installdir); pinf->installdir = StrNew(value); }
else if (!StrCmp(key, "iso.c")) { Free(pinf->iso_c); pinf->iso_c = StrNew(value); }
else if (!StrCmp(key, "post-install-doc")) { Free(pinf->post_install_doc); pinf->post_install_doc = StrNew(value); }
else { /* unrecognized keys are simply ignored */ }
key = end;
}
return 0;
}
I64 PkgWriteManifest(CPkgInfo* pinf, U8* path) {
// TODO: implement
no_warn pinf;
FileWrite(path, "", 0);
return 0;
}
// Downloads a package info from the repository.
// Returns 0 or error code
I64 PkgFetchManifest(CPkgInfo* pinf, U8* package_name) {
// Old packages didn't have to specify a name, so we'll keep this for now
pinf->package_name = StrNew(package_name);
U8* url = MStrPrint("%s/%s", PKG_BASE_URL, package_name);
U8* manifest = 0;
I64 size = 0;
I64 error = UrlGet(url, &manifest, &size);
if (error == 0)
error = PkgParseManifest(pinf, manifest);
Free(manifest);
Free(url);
return error;
}
// Get the URL of the package's ISO.C download.
// Returns NULL if N/A, otherwise must be Free()d.
U8* PkgAllocISOCUrl(CPkgInfo* pinf) {
if (!pinf->iso_c)
return NULL;
// A bit hacky, but will probably always work
if (StrFind("//", pinf->iso_c))
return StrNew(pinf->iso_c);
else
return MStrPrint("%s/%s", PKG_BASE_URL, pinf->iso_c);
}
// Check if the package metadata makes it viable for installation.
// You still need to do PkgCheckCompatibility, dependency resolution,
// and check for a suitable installable format.
Bool PkgIsInstallable(CPkgInfo* pinf) {
return pinf->package_name != NULL && pinf->version != NULL && pinf->installdir != NULL;
}
// Check if the package is compatible with this OS & Pkg version
I64 PkgCheckCompatibility(CPkgInfo* pinf) {
if (pinf->pkgmin > PKG_VERSION) {
"$FG,6$This package requires a more recent version of $FG,5$Pkg\n";
"$FG$Please update.\n";
return PKG_EVERSION;
}
I64 osver = ToI64(sys_os_version * 100);
if (osver < pinf->osmin) {
"$FG,6$This package requires a more recent system version.\n";
"$FG$Please update. (need %d, have %d)\n", pinf->osmin, osver;
return PKG_EOSVERSION;
}
if (osver > pinf->osmax) {
"$FG,6$This package is not compatible with your system version.\n";
"$FG$Last supported version is %d, you have %d.\n", pinf->osmax, osver;
return PKG_EOSVERSION;
}
return 0;
}
I64 PkgRegister(CPkgInfo* pinf) {
// TODO: this is very preliminary
if (pinf->package_name == NULL)
return PKG_EUNSUITABLE;
U8* path = MStrPrint("%s/%s", PKG_LOCAL_REPO, pinf->package_name);
PkgWriteManifest(pinf, path);
return 0;
}
// Install a package, using the provided ISO.C file.
// This will also register the package as installed.
I64 PkgInstallISOC(CPkgInfo* pinf, U8* iso_c) {
if (pinf->package_name == NULL || pinf->installdir == NULL)
return PKG_EUNSUITABLE;
I64 error = 0;
"Installing %s\n$FG,7$", pinf->package_name;
I64 letter = MountFile(iso_c);
if (letter) {
U8 src_path[8];
StrPrint(src_path, "%c:/", letter);
// StrLen check is a temporary hack to not complain about MkDir("::/");
if (StrLen(pinf->installdir) > 3)
DirMk(pinf->installdir);
CopyTree(src_path, pinf->installdir);
// Register package as installed
error = PkgRegister(pinf);
// Display post-install doc
if (pinf->post_install_doc) {
Ed(pinf->post_install_doc);
}
}
else
error = PKG_EMOUNT;
Unmount(letter);
"$FG$";
return error;
}
// Verify, download & install a single package
// All dependencies must have been installed at this point.
I64 PkgDownloadAndInstall(CPkgInfo* pinf) {
I64 error = PkgCheckCompatibility(pinf);
if (error) { return error; }
U8* iso_c_url = PkgAllocISOCUrl(pinf);
if (iso_c_url) {
U8* iso_data = 0;
I64 iso_size = 0;
"Downloading %s...\n", pinf->package_name;
error = UrlGetWithProgress(iso_c_url, &iso_data, &iso_size);
if (error == 0) {
U8* tmppath = "::/Tmp/Package.ISO.C";
FileWrite(tmppath, iso_data, iso_size);
error = PkgInstallISOC(pinf, tmppath);
}
Free(iso_data);
Free(iso_c_url);
}
else {
"$FG,6$No suitable download address. Package broken?\n";
error = PKG_EUNSUITABLE;
}
return error;
}
// Expected max length: 5 ("1023k")
static U8* FormatSize(I64 size) {
static U8 buf[16];
if (size > 0x40000000)
StrPrint(buf, "%dG", (size + 0x3fffffff) / 0x40000000);
else if (size > 0x100000)
StrPrint(buf, "%dM", (size + 0xfffff) / 0x100000);
else if (size > 0x400)
StrPrint(buf, "%dk", (size + 0x3ff) / 0x400);
else
StrPrint(buf, "%d", size);
return buf;
}
// Install a package using a local manifest file
public I64 PkgInstallFromFile(U8* manifest_path) {
DirMk(PKG_LOCAL_REPO);
CPkgInfo pinf;
PkgInfoInit(&pinf);
// Parse manifest
I64 manifest_size;
U8* manifest_file = FileRead(manifest_path, &manifest_size);
// This relies on FileRead returning a 0-terminated buffer.
// As of v502, this happens for all file systems
I64 error = PkgParseManifest(&pinf, manifest_file);
if (error == 0) {
error = PkgCheckCompatibility(&pinf);
if (!error) {
if (pinf.iso_c) {
PkgInstallISOC(&pinf, pinf.iso_c);
}
else {
"$FG,6$No suitable installable file. Package broken?\n";
error = PKG_EUNSUITABLE;
}
}
else {
"$FG,4$PkgCheckCompatibility error: %d\n$FG$", error;
}
}
else {
"$FG,4$PkgParseManifest error: %d\n$FG$", error;
}
PkgInfoFree(&pinf);
return error;
}
// Install a package from the repository
public I64 PkgInstall(U8* package_name) {
SocketInit();
DirMk(PKG_LOCAL_REPO);
CPkgInfo pinf;
PkgInfoInit(&pinf);
I64 error = PkgFetchManifest(&pinf, package_name);
if (error == 0) {
if (PkgIsInstallable(&pinf)) {
"$FG,8$ Package Ver \n"
"$FG,8$ Dir Size \n"
"$FG,8$============================\n"
"$FG,2$+ %-20s %-6s\n", package_name, pinf.version;
"$FG,2$ %-20s %-6s\n", pinf.installdir, FormatSize(pinf.size);
"\n"
"$FG$Is this ok? (y/n) ";
I64 ok = GetKey(NULL, TRUE);
"\n";
// TODO: verify all packages before we start downloading
if (ok == 'y') {
error = PkgDownloadAndInstall(&pinf);
if (error == 0) {
"$FG,2$Installed 1 package(s)\n";
}
else {
"$FG,4$PkgDownloadAndInstall error: %d\n$FG$", error;
}
}
}
else {
"$FG,4$PkgInstall: %s is not installable\n$FG$", package_name;
error = PKG_EUNSUITABLE;
}
}
else {
"$FG,4$PkgFetchManifest error: %d\n$FG$", error;
}
PkgInfoFree(&pinf);
return error;
}
// List packages available in the repository
public I64 PkgList() {
SocketInit();
U8* url = MStrPrint("%s/packages.list", PKG_BASE_URL);
U8* list = 0;
I64 size = 0;
I64 error = UrlGet(url, &list, &size);
if (error == 0) {
"$FG,2$%s\n", list;
/*U8* entry = list;
while (*entry) {
U8* end = StrFirstOcc(entry, "\n");
if (end) {
*end = 0;
end++;
}
else
end = value + StrLen(value);
"$FG,2$%s\n", entry;
entry = end;
}*/
}
else {
"$FG,4$UrlGet error: %d\n$FG$", error;
}
Free(list);
Free(url);
return error;
}
// Build a package from directory contents
public I64 PkgMakeFromDir(U8* manifest_path, U8* src_dir) {
CPkgInfo pinf;
PkgInfoInit(&pinf);
// Parse manifest
I64 manifest_size;
U8* manifest_file = FileRead(manifest_path, &manifest_size);
// This relies on FileRead returning a 0-terminated buffer.
// As of v502, this happens for all file systems
I64 error = PkgParseManifest(&pinf, manifest_file);
if (error == 0) {
// Build RedSea ISO
if (pinf.iso_c) {
U8* iso_path = pinf.iso_c;
// RedSeaISO doesn't return a proper error code
RedSeaISO(iso_path, src_dir);
// TODO: update & save manifest
/*CDirEntry* de;
if (FileFind(iso_path, &de)) {
pinf.size = de.size;
// Save updated manifest
PkgWriteManifest(&pinf, manifest_path);
Free(de->full_name);
}
else {
"$FG,6$Something went wrong, can't stat %s.\n", iso_path;
error = PKG_EMOUNT;
}*/
}
else {
"$FG,6$No output file defined.\n";
error = PKG_EUNSUITABLE;
}
}
else {
"$FG,4$PkgParseManifest error: %d\n$FG$", error;
}
PkgInfoFree(&pinf);
return error;
}
// Build a package using a single file
I64 PkgMakeFromFile(U8* manifest_path, U8* file_path) {
DelTree(PKG_TMP_DIR);
DirMk(PKG_TMP_DIR);
U8* tmppath = MStrPrint("%s/%s", PKG_TMP_DIR, StripDir(file_path));
Copy(file_path, tmppath);
I64 error = PkgMakeFromDir(manifest_path, PKG_TMP_DIR);
Free(tmppath);
return error;
}
+25
View File
@@ -0,0 +1,25 @@
// vim: set ft=cpp:
#include "::/Adam/Net/Url"
I64 Wget(U8* url, U8* saveas = NULL) {
SocketInit();
U8* data;
I64 len;
I64 error = UrlGet(url, &data, &len);
if (!error) {
if (saveas) {
FileWrite(saveas, data, len);
}
else {
"%s\n", data;
}
}
else {
"$FG,4$Wget: error %d\n$FG$", error;
}
return error;
}
+49
View File
@@ -0,0 +1,49 @@
// vim: set ft=c:
#include "::/Adam/Net/Socket"
#define PORT 8000
I64 TcpEchoClient(U8* dest_address) {
SocketInit();
I64 sock = socket(AF_INET, SOCK_STREAM);
if (sock < 0)
return -1;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = 0;
inet_aton(dest_address, &addr.sin_addr);
I64 error = connect(sock, &addr, sizeof(addr));
if (error < 0) {
"$FG,6$connect: error %d\n$FG$", error;
return -1;
}
U8* message = "hello, world!\n\n";
I64 count = send(sock, message, StrLen(message), 0);
if (count < 0) {
"$FG,6$send: error %d\n$FG$", count;
return -1;
}
U8 buffer[2048 + 1];
count = recv(sock, buffer, sizeof(buffer) - 1, 0);
if (count <= 0) {
"$FG,6$recv: error %d\n$FG$", count;
}
else {
buffer[count] = 0;
"$FG,8$Received %d bytes:\n$FG$%s\n", count, buffer;
}
close(sock);
return 0;
}
+54
View File
@@ -0,0 +1,54 @@
// vim: set ft=c:
#include "::/Adam/Net/Socket"
#define PORT 8000
I64 TcpEchoServer() {
SocketInit();
I64 sock = socket(AF_INET, SOCK_STREAM);
if (sock < 0)
return -1;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, &addr, sizeof(addr)) < 0) {
close(sock);
"$FG,4$TcpEchoServer: failed to bind to port %d\n$FG$", PORT;
return -1;
}
I64 error = listen(sock, 1);
if (error < 0) {
"$FG,6$listen: error %d\n$FG$", error;
return -1;
}
"$FG,2$Listening on port %d\n$FG$", PORT;
I64 client = accept(sock, 0, 0);
U8 buffer[2048 + 1];
I64 count = recv(client, buffer, sizeof(buffer) - 1, 0);
if (count <= 0) {
"$FG,6$recv: error %d\n$FG$", count;
}
else {
buffer[count] = 0;
"$FG,8$Received %d bytes:\n$FG$%s\n", count, buffer;
}
send(client, buffer, count, 0);
close(client);
close(sock);
return 0;
}
+50
View File
@@ -0,0 +1,50 @@
// vim: set ft=c:
// udp-send.py 192.168.1.10 15051 "Hello World!"
#include "::/Adam/Net/Socket"
#define PORT 15051
I64 UdpListen() {
SocketInit();
I64 sock = socket(AF_INET, SOCK_DGRAM);
if (sock < 0)
return -1;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, &addr, sizeof(addr)) < 0) {
close(sock);
"$FG,4$UdpListen: failed to bind to port %d\n$FG$", PORT;
return -1;
}
"$FG,2$Listening on port %d\n$FG$", PORT;
while (1) {
U8 buffer[2048 + 1];
I64 count = recvfrom(sock, buffer, sizeof(buffer) - 1, 0, &addr, sizeof(addr));
if (count == 0)
break;
if (count < 0) {
"$FG,6$recvfrom: error %d\n$FG$", count;
}
else {
buffer[count] = 0;
"$FG,8$Received %d bytes from %s:%d:\n$FG$%s\n", count,
inet_ntoa(addr.sin_addr), ntohs(addr.sin_port), buffer;
}
}
close(sock);
return 0;
}
+32
View File
@@ -0,0 +1,32 @@
// vim: set ft=c:
// udp-listen.py 15051
#include "::/Adam/Net/Socket"
#define PORT 15051
I64 UdpSend(U8* dest_address) {
SocketInit();
I64 sock = socket(AF_INET, SOCK_DGRAM);
if (sock < 0)
return -1;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = 0;
inet_aton(dest_address, &addr.sin_addr);
U8* message = "hello, world!";
I64 count = sendto(sock, message, StrLen(message), 0, &addr, sizeof(addr));
if (count < 0) {
"$FG,6$sendto: error %d\n$FG$", count;
}
close(sock);
return 0;
}
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env python3
import socket
import sys
HOST = sys.argv[1]
PORT = int(sys.argv[2])
MESSAGE = sys.argv[3]
with socket.create_connection((HOST, PORT)) as s:
s.sendall(MESSAGE.encode())
data = s.recv(1024)
if data:
print('Received', data.decode())
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env python3
import socket
import sys
HOST = "0.0.0.0"
PORT = int(sys.argv[1])
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind((HOST, PORT))
s.listen(1)
conn, addr = s.accept()
with conn:
print('Connected by', addr)
while True:
data = conn.recv(1024)
if not data:
break
conn.sendall(data.decode().upper().encode())
+14
View File
@@ -0,0 +1,14 @@
#!/usr/bin/env python3
import socket
import sys
HOST = "0.0.0.0"
PORT = int(sys.argv[1])
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((HOST, PORT))
while True:
data, addr = sock.recvfrom(2048)
print("received message:", data.decode())
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/env python3
import socket
import sys
HOST = sys.argv[1]
PORT = int(sys.argv[2])
MESSAGE = sys.argv[3]
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.sendto(MESSAGE.encode(), (HOST, PORT))
+12
View File
@@ -100,6 +100,7 @@ public CComm *CommInit8n1(I64 port,I64 baud)
return c;
}
/*
public U0 CommPutChar(I64 port,U8 b)
{
CComm *c=&comm_ports[port];
@@ -110,6 +111,17 @@ public U0 CommPutChar(I64 port,U8 b)
POPFD
Sleep(10); //!!! Remove this line!!! Linux echo_socket is too slow.
}
*/
public U0 CommPutChar(I64 port,U8 b)
{
I64 base=comm_ports[port].base;
while (!(InU8(base+UART_LSR) & 0x20))
Yield;
OutU8(base+UART_THR,b);
while (!(InU8(base+UART_LSR) & 0x20))
Yield;
}
U0 CommPutS(I64 port,U8 *st)
{
+3 -1
View File
@@ -1,4 +1,6 @@
$WW+H,1$$FG,5$$TX+CX,"TempleOS V5.03",D="DD_OS_NAME_VERSION"$$FG$
$WW+H,1$$FG,3$$TX+CX,"Shrine",D="DD_OS_NAME_VERSION"$$FG$
$WW+H,1$$FG,5$$TX+CX,"based on TempleOS by Terry A. Davis"$$FG$
$TX+CX,"Public Domain Operating System"$
+1 -1
View File
@@ -13,7 +13,7 @@ U8 *rev_bits_table, //Table with U8 bits revd
*set_bits_table; //Table with count of set bits in a U8
CDate local_time_offset;
F64 *pow10_I64,
sys_os_version=5.030;
sys_os_version=5.050;
CAutoCompleteDictGlbls acd;
CAutoCompleteGlbls ac;
+2 -2
View File
@@ -31,7 +31,7 @@ U0 MakeMyISO(U8 *_out_iso_filename)
CopyTree("/Misc", "/Distro/Misc");
//To save space, optionally delete dictionary.
//Del("/Distro/Adam/AutoComplete/ACDefs.DATA");
Del("/Distro/Adam/AutoComplete/ACDefs.DATA");
CopyTree("/Downloads","/Distro/Downloads"); //You can leave this out.
DirMk("/Distro/Tmp");
DirMk("/Distro/Tmp/ScrnShots");
@@ -44,6 +44,6 @@ U0 MakeMyISO(U8 *_out_iso_filename)
Free(out_iso_filename);
}
MakeMyISO("/Tmp/MyDistro.ISO.C");
MakeMyISO("/Tmp/ShrineDist.ISO.C");
// Study my account examples $LK,"Cfg Strs",A="FL:::/Demo/AcctExample/TOS/TOSCfg.HC,1"$, $LK,"Update Funs",A="FL:::/Demo/AcctExample/TOS/TOSDistro.HC,1"$.
+8
View File
@@ -0,0 +1,8 @@
CBGR48 gr_palette_conemu[COLORS_NUM]={
0x00002b2b3636,0x070736364242,0x000080808080,0x31318282a4a4,
0xcbcb4b4b1616,0x9c9c3636b6b6,0x858599990000,0xeeeee8e8d5d5,
0x9393a1a1a1a1,0x26268b8bd2d2,0x4f4fb6b63636,0x2a2aa1a19898,
0xdcdc32322f2f,0xd3d336368282,0xb5b589890000,0xfdfdf6f6e3e3};
GrPaletteSet(gr_palette_conemu);
Adam("DefineLoad(\"USER_PALETTE\", \"ConEmu\");");
+8
View File
@@ -0,0 +1,8 @@
CBGR48 gr_palette_monokai[COLORS_NUM]={
0x272728282222,0x010154549e9e,0x7474aaaa0404,0x1a1a8383a6a6,
0xa7a703033434,0x898956569c9c,0xb6b6b6b64949,0xcacacacacaca,
0x7c7c7c7c7c7c,0x03038383f5f5,0x8d8dd0d00606,0x5858c2c2e5e5,
0xf3f304044b4b,0xa8a87d7db8b8,0xcccccccc8181,0xffffffffffff};
GrPaletteSet(gr_palette_monokai);
Adam("DefineLoad(\"USER_PALETTE\", \"Monokai\");");
+8
View File
@@ -0,0 +1,8 @@
CBGR48 gr_palette_ubuntu[COLORS_NUM]={
0x2e2e34343636,0x34346565a4a4,0x4e4e9a9a0606,0x060698989a9a,
0xcccc00000000,0x757550507b7b,0xc4c4a0a00000,0xd3d3d7d7cfcf,
0x555557575353,0x72729f9fcfcf,0x8a8ae2e23434,0x3434e2e2e2e2,
0xefef29292929,0xadad7f7fa8a8,0xfcfce9e94f4f,0xeeeeeeeeecec};
GrPaletteSet(gr_palette_ubuntu);
Adam("DefineLoad(\"USER_PALETTE\", \"Ubuntu\");");
+6
View File
@@ -1,3 +1,7 @@
#include "::/Apps/Lsh";
#include "::/Apps/Pkg";
#include "::/Misc/PalUbuntu.HC"
//Place this file in /Home and change
//anything you want.
@@ -52,4 +56,6 @@ U0 Tmp()
}
}
#exe { if (SNAILNET_NATIVE_DRIVER != NULL) StreamPrint("Netcfg;\n"); }
Tmp;
Lsh;
Executable
+134
View File
@@ -0,0 +1,134 @@
#!/usr/bin/env python3
'''
mfa - minimalist file access for TempleOS
usage:
./mfa.py list <filename> [<local-filename>]
./mfa.py put <filename> [<local-filename>]
./mfa.py command <command>
for scripting:
./mfa.py <script
- use tabs to separate arguments
- Mfa.HC must be running in a loop
example script:
wait 5
test
put B:/AutoOSInstall.HC.Z AutoOSInstall.HC
test
command #include "B:/AutoOSInstall"
test
command OSInstall(FALSE);
test
'''
import socket
import sys
import time
TCP_IP = '127.0.0.1'
TCP_PORT = 7770
CHUNK_SIZE = 8
BAUD = 19200
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((TCP_IP, TCP_PORT))
def read_line():
s = b''
while True:
ch = sock.recv(1)
if ch == b'\n':
print('<', s.decode())
return s.decode()
else:
s += ch
def read_bytes(count):
s = b''
while len(s) < count:
s += sock.recv(1)
return s
def send(line):
print('>', line)
sock.send((line + "\n").encode())
def test():
send('?')
assert read_line() == '!'
def do_command(*argv):
cmd = argv[0]
if cmd == 'put':
filename = argv[1]
local_filename = argv[2] if len(argv) > 2 else filename
with open(local_filename, 'rb') as f:
data = f.read()
size = len(data)
test()
send('P' + filename)
send('S' + str(size))
while len(data):
sock.send(data[0:CHUNK_SIZE])
data = data[CHUNK_SIZE:]
time.sleep(float(CHUNK_SIZE) * 8 / BAUD)
print('Read', size, 'bytes from', local_filename)
elif cmd == 'list':
filename = argv[1]
local_filename = argv[2] if len(argv) > 2 else filename
send('L' + filename)
next_ = read_line()
assert next_[0] == 'S'
size = int(next_[1:])
size_remaining = size
with open(local_filename, 'wb') as f:
while size_remaining:
read_now = min(size_remaining, 0x1000)
data = read_bytes(read_now)
f.write(data)
size_remaining -= len(data)
print('Written', size, 'bytes to', local_filename)
elif cmd == 'command':
command = argv[1]
test()
send("'" + command)
#elif cmd == 'test':
# test()
elif cmd == 'wait':
secs = float(argv[1])
time.sleep(secs)
else:
raise Exception('Command error: ' + argv[0])
if len(sys.argv) > 1:
do_command(*sys.argv[1:])
else:
try:
while True:
if sys.version_info[0] < 3:
entry = raw_input()
else:
entry = input()
if len(entry) and entry[0] != '#':
items = entry.split('\t')
do_command(*items)
except(EOFError):
pass
Executable
+90
View File
@@ -0,0 +1,90 @@
#!/usr/bin/env python
from __future__ import print_function
import socket
import struct
import sys
import time
TCP_IP = '127.0.0.1'
TCP_PORT = 7777
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((TCP_IP, TCP_PORT))
# socket #0 is never used
socks = [False]
CMD_SOCKET = 1
CMD_CLOSE = 2
CMD_CONNECT_TCP = 3
CMD_SEND = 4
CMD_RECV = 5
CMD_HELLO = 0xAA
def recvall(sock, count):
s = b''
while len(s) < count:
part = sock.recv(count - len(s))
if part is None: break
s += part
return s
while True:
cmd = ord(sock.recv(1))
#print('%02X' % cmd)
if cmd is None: break
if cmd == CMD_HELLO:
print('hello!')
sock.send(b'\xAA')
elif cmd == CMD_CLOSE:
sockfd = ord(sock.recv(1))
print('close(%d)' % (sockfd))
socks[sockfd].close()
socks[sockfd] = None
sock.send(struct.pack('B', 0))
elif cmd == CMD_CONNECT_TCP:
sockfd, length = struct.unpack('BB', recvall(sock, 2))
hostname = recvall(sock, length).decode()
port, = struct.unpack('H', recvall(sock, 2))
print('connectTcp(%d, %s, %d)' % (sockfd, hostname, port))
try:
socks[sockfd].connect((hostname, port))
rc = 0
except socket.error as e:
print(e)
rc = 0xff
sock.send(struct.pack('B', rc))
elif cmd == CMD_RECV:
sockfd, length, flags = struct.unpack('BBB', recvall(sock, 3))
#print('recv(%d, %d, %d)' % (sockfd, length, flags))
try:
data = socks[sockfd].recv(length)
except socket.error as e:
print(e)
data = b''
sock.send(struct.pack('B', len(data)))
sock.send(data)
elif cmd == CMD_SEND:
sockfd, length, flags = struct.unpack('BBB', recvall(sock, 3))
data = recvall(sock, length)
#print('send(%d, %s, %d)' % (sockfd, data, flags))
rc = socks[sockfd].send(data)
sock.send(struct.pack('B', rc))
elif cmd == CMD_SOCKET:
af, type = struct.unpack('BB', recvall(sock, 2))
id = len(socks)
print("socket(%d, %d)" % (af, type))
socks.append(socket.socket(socket.AF_INET, socket.SOCK_STREAM))
sock.send(struct.pack('B', id))