const AF_INET = 2 const SOCK_STREAM = 1 const SOCK_DGRAM = 2 const SOL_SOCKET = 1 const SO_REUSEADDR = 2 func net.listen[packed_host: i64, port: i64] : i64 let s: i64 = _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0) if s < 0 return -1 let optval = 1 if _builtin_syscall(SYS_setsockopt, s, SOL_SOCKET, SO_REUSEADDR, ^optval, 8) < 0 _builtin_syscall(SYS_close, s) return -1 let sa: ptr = mem.alloc(16) mem.zero(sa, 16) sa[0] = 2 sa[1] = 0 sa[2] = (port >> 8) & 255 sa[3] = port & 255 sa[4] = (packed_host >> 24) & 255 sa[5] = (packed_host >> 16) & 255 sa[6] = (packed_host >> 8) & 255 sa[7] = packed_host & 255 if _builtin_syscall(SYS_bind, s, sa, 16) < 0 _builtin_syscall(SYS_close, s) mem.free(sa) return -1 mem.free(sa) if _builtin_syscall(SYS_listen, s, 128) < 0 _builtin_syscall(SYS_close, s) return -1 return s // TODO: resolve DNS func net.connect[packed_host: i64, port: i64] : i64 let s: i64 = _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0) if s < 0 return -1 let sa: ptr = mem.alloc(16) mem.zero(sa, 16) sa[0] = AF_INET sa[1] = 0 sa[2] = (port >> 8) & 255 sa[3] = port & 255 sa[4] = (packed_host >> 24) & 255 sa[5] = (packed_host >> 16) & 255 sa[6] = (packed_host >> 8) & 255 sa[7] = packed_host & 255 if _builtin_syscall(SYS_connect, s, sa, 16) < 0 mem.free(sa) _builtin_syscall(SYS_close, s) return -1 mem.free(sa) return s func net.accept[s: i64] : i64 return _builtin_syscall(SYS_accept, s, 0, 0) func net.send[s: i64, data: ptr, size: i64] : void _builtin_syscall(SYS_sendto, s, data, size, 0, 0, 0) func net.read[s: i64, buffer: ptr, size: i64] : i64 return _builtin_syscall(SYS_read, s, buffer, size) func net.close[s: i64] : void _builtin_syscall(SYS_close, s) func net.pack_addr[a: i64, b: i64, c: i64, d: i64] : i64 return (a << 24) | (b << 16) | (c << 8) | d struct net.UDPSocket fd: i64 addr: ptr struct net.UDPPacket data: ptr source_addr: ptr size: i64 func net.create_udp_client[packed_host: i64, port: i64] : net.UDPSocket let s: net.UDPSocket = new net.UDPSocket s->fd = _builtin_syscall(SYS_socket, AF_INET, SOCK_DGRAM, 0) if s->fd < 0 mem.free(s) return 0 s->addr = mem.alloc(16) mem.zero(s->addr, 16) s->addr[0] = AF_INET s->addr[1] = 0 s->addr[2] = (port >> 8) & 255 s->addr[3] = port & 255 s->addr[4] = (packed_host >> 24) & 255 s->addr[5] = (packed_host >> 16) & 255 s->addr[6] = (packed_host >> 8) & 255 s->addr[7] = packed_host & 255 return s func net.udp_listen[packed_host: i64, port: i64] : net.UDPSocket let s: net.UDPSocket = net.create_udp_client(packed_host, port) if !s return 0 if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0 net.UDPSocket.close_and_free(s) return 0 return s func net.udp_send_to[s: net.UDPSocket, addr: ptr, data: ptr, size: i64] : void _builtin_syscall(SYS_sendto, s->fd, data, size, 0, addr, 16) func net.udp_receive[s: net.UDPSocket, size: i64] : net.UDPPacket let pkt: net.UDPPacket = new net.UDPPacket pkt->data = mem.alloc(size) pkt->source_addr = mem.alloc(16) mem.zero(pkt->source_addr, 16) let addrlen: i64 = 16 pkt->size = _builtin_syscall(SYS_recvfrom, s->fd, pkt->data, size, 0, pkt->source_addr, ^addrlen) return pkt func net.UDPSocket.close_and_free[s: net.UDPSocket] : void _builtin_syscall(SYS_close, s->fd) mem.free(s->addr) mem.free(s) func net.UDPPacket.free[pkt: net.UDPPacket] : void mem.free(pkt->data) mem.free(pkt->source_addr) mem.free(pkt) const DNS_TYPE_A = 1 const DNS_CLASS_IN = 1 const DNS_RECURSION_DESIRED = 256 func net.encode_dns_name[domain: str] : io.Buffer let domain_len: i64 = str.len(domain) let buf: io.Buffer = io.Buffer.alloc(domain_len + 2) let out_pos: i64 = 0 let part_start: i64 = 0 let i: i64 = 0 while i <= domain_len if i == domain_len || domain[i] == '.' let part_len: i64 = i - part_start buf->data[out_pos] = part_len & 0xff out_pos = out_pos + 1 mem.copy(domain + part_start, buf->data + out_pos, part_len) out_pos = out_pos + part_len part_start = i + 1 i = i + 1 buf->data[out_pos] = 0 return buf func net.build_dns_query[domain_name: str, record_type: i64] : io.Buffer let name: io.Buffer = net.encode_dns_name(domain_name) let out: io.Buffer = io.Buffer.alloc(12 + name->size + 4) // header let id: i64 = math.abs(os.urandom_i64() % 65536) mem.write16be(out->data + 0, id) mem.write16be(out->data + 2, DNS_RECURSION_DESIRED) mem.write16be(out->data + 4, 1) // num_questions mem.write16be(out->data + 6, 0) // num_answers mem.write16be(out->data + 8, 0) // num_authorities mem.write16be(out->data + 10, 0) // num_additionals // question mem.copy(name->data, out->data + 12, name->size) mem.write16be(out->data + 12 + name->size, record_type) mem.write16be(out->data + 12 + name->size + 2, DNS_CLASS_IN) io.Buffer.free(name) return out func net.resolve[domain: str] : i64 let query: io.Buffer = net.build_dns_query(domain, DNS_TYPE_A) // TODO: this probably shouldnt be hardcoded let s: net.UDPSocket = net.create_udp_client(net.pack_addr(8, 8, 8, 8), 53) if s == 0 return 0 net.udp_send_to(s, s->addr, query->data, query->size) io.Buffer.free(query) let pkt: net.UDPPacket = net.udp_receive(s, 1024) net.UDPSocket.close_and_free(s) // TODO: do actual parsing let pos: i64 = 12 // skip header (12 bytes) while pkt->data[pos] != 0 pos = pos + pkt->data[pos] + 1 // skip question pos = pos + 5 // skip null byte, type(2), class(2) pos = pos + 12 // skip name(2), type(2), class(2), ttl(4), rdlength(2) let out: i64 = net.pack_addr(pkt->data[pos], pkt->data[pos+1], pkt->data[pos+2], pkt->data[pos+3]) net.UDPPacket.free(pkt) return out