include "statement"
This commit is contained in:
236
std/net.zr
Normal file
236
std/net.zr
Normal file
@@ -0,0 +1,236 @@
|
||||
const AF_INET = 2
|
||||
const SOCK_STREAM = 1
|
||||
const SOCK_DGRAM = 2
|
||||
const SOL_SOCKET = 1
|
||||
const SO_REUSEADDR = 2
|
||||
const SOMAXCONN = 128
|
||||
const IP_MAXPACKET = 65535
|
||||
|
||||
const DNS_TYPE_A = 1
|
||||
const DNS_CLASS_IN = 1
|
||||
const DNS_RECURSION_DESIRED = 256
|
||||
|
||||
func net.listen[host: str, port: i64] : i64, bool
|
||||
s := _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0)
|
||||
if s < 0
|
||||
return -1, false
|
||||
|
||||
optval := 1
|
||||
if _builtin_syscall(SYS_setsockopt, s, SOL_SOCKET, SO_REUSEADDR, ^optval, 8) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
return -1, false
|
||||
|
||||
~packed_host, ok := net.resolve(host)
|
||||
if !ok
|
||||
return -1, false
|
||||
|
||||
sa := _stackalloc(16)
|
||||
net._build_sa(sa, packed_host, port)
|
||||
|
||||
if _builtin_syscall(SYS_bind, s, sa, 16) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
return -1, false
|
||||
|
||||
if _builtin_syscall(SYS_listen, s, SOMAXCONN) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
return -1, false
|
||||
|
||||
return s, true
|
||||
|
||||
func net.connect[host: str, port: i64] : i64, bool
|
||||
s := _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0)
|
||||
if s < 0
|
||||
return -1, false
|
||||
|
||||
~packed_host, ok := net.resolve(host)
|
||||
if !ok
|
||||
return -1, false
|
||||
|
||||
sa := _stackalloc(16)
|
||||
net._build_sa(sa, packed_host, port)
|
||||
|
||||
if _builtin_syscall(SYS_connect, s, sa, 16) < 0
|
||||
_builtin_syscall(SYS_close, s)
|
||||
return -1, false
|
||||
|
||||
return s, true
|
||||
|
||||
func net.accept[s: i64, addr: ptr] : i64
|
||||
addrlen := 16
|
||||
return _builtin_syscall(SYS_accept, s, addr, ^addrlen)
|
||||
|
||||
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
|
||||
|
||||
func net._build_sa[sa: ptr, packed_host: i64, port: i64] : void
|
||||
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
|
||||
|
||||
struct net.UDPSocket
|
||||
fd: i64
|
||||
addr: ptr
|
||||
|
||||
func net.UDPSocket.receive[s: net.UDPSocket, size: i64] : net.UDPPacket
|
||||
pkt := new* net.UDPPacket
|
||||
pkt->data = mem.alloc(size)
|
||||
pkt->source_addr = mem.alloc(16)
|
||||
mem.zero(pkt->source_addr, 16)
|
||||
|
||||
addrlen := 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)
|
||||
s->addr->free()
|
||||
mem.free(s)
|
||||
|
||||
func net.create_udp_client[host: str, port: i64] : net.UDPSocket, bool
|
||||
s := new* net.UDPSocket
|
||||
|
||||
~packed_host, ok := net.resolve(host)
|
||||
if !ok
|
||||
return 0 as net.UDPSocket, false
|
||||
|
||||
s->fd = _builtin_syscall(SYS_socket, AF_INET, SOCK_DGRAM, 0)
|
||||
if s->fd < 0
|
||||
mem.free(s)
|
||||
return 0 as net.UDPSocket, false
|
||||
|
||||
s->addr = mem.alloc(16)
|
||||
net._build_sa(s->addr, packed_host, port)
|
||||
return s, true
|
||||
|
||||
func net.create_udp_server[host: str, port: i64] : net.UDPSocket, bool
|
||||
~s, ok := net.create_udp_client(host, port)
|
||||
if !ok
|
||||
return 0 as net.UDPSocket, false
|
||||
|
||||
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
|
||||
s->close_and_free()
|
||||
return 0 as net.UDPSocket, false
|
||||
|
||||
return s, true
|
||||
|
||||
func net.UDPSocket.send_to[s: net.UDPSocket, addr: ptr, data: ptr, size: i64] : void
|
||||
_builtin_syscall(SYS_sendto, s->fd, data, size, 0, addr, 16)
|
||||
|
||||
struct net.UDPPacket
|
||||
data: ptr
|
||||
source_addr: ptr
|
||||
size: i64
|
||||
|
||||
func net.UDPPacket.free[pkt: net.UDPPacket] : void
|
||||
pkt->data->free()
|
||||
pkt->source_addr->free()
|
||||
mem.free(pkt)
|
||||
|
||||
func net.resolve[domain: str] : i64, bool
|
||||
// if domain is already an ip, skip dns resolution
|
||||
parts := domain->split(".")
|
||||
if parts->size == 4
|
||||
valid := true
|
||||
for i in 0..4
|
||||
p := parts->nth(i) as str
|
||||
if p->len() < 1 || p->len() > 3
|
||||
valid = false
|
||||
break
|
||||
for j in 0..p->len()
|
||||
if !p[j]->is_digit()
|
||||
valid = false
|
||||
break
|
||||
if !valid
|
||||
break
|
||||
if valid
|
||||
a := (parts->nth(0) as str)->parse_i64()
|
||||
b := (parts->nth(1) as str)->parse_i64()
|
||||
c := (parts->nth(2) as str)->parse_i64()
|
||||
d := (parts->nth(3) as str)->parse_i64()
|
||||
if a >= 0 && a <= 255 && b >= 0 && b <= 255 && c >= 0 && c <= 255 && d >= 0 && d <= 255
|
||||
parts->free_with_items()
|
||||
return net.pack_addr(a, b, c, d), true
|
||||
parts->free_with_items()
|
||||
|
||||
query := net.build_dns_query(domain, DNS_TYPE_A)
|
||||
|
||||
// TODO: this probably shouldnt be hardcoded
|
||||
~s, ok := net.create_udp_client("1.1.1.1", 53)
|
||||
if !ok
|
||||
query->free()
|
||||
return -1, false
|
||||
|
||||
s->send_to(s->addr, query->data, query->size)
|
||||
query->free()
|
||||
pkt := s->receive(1024)
|
||||
s->close_and_free()
|
||||
|
||||
// TODO: do actual parsing
|
||||
|
||||
pos := 12 // skip header (12 bytes)
|
||||
|
||||
while pkt->data[pos] != 0
|
||||
pos += pkt->data[pos] + 1 // skip question
|
||||
|
||||
pos += 5 // skip null byte, type(2), class(2)
|
||||
|
||||
pos += 12 // skip name(2), type(2), class(2), ttl(4), rdlength(2)
|
||||
|
||||
out := net.pack_addr(pkt->data[pos] as i64, pkt->data[pos+1] as i64, pkt->data[pos+2] as i64, pkt->data[pos+3] as i64)
|
||||
pkt->free()
|
||||
return out, true
|
||||
|
||||
func net.encode_dns_name[domain: str] : Blob
|
||||
domain_len := domain->len()
|
||||
buf := Blob.alloc(domain_len + 2)
|
||||
out_pos := 0
|
||||
part_start := 0
|
||||
i := 0
|
||||
while i <= domain_len
|
||||
if i == domain_len || domain[i] == '.'
|
||||
part_len := i - part_start
|
||||
buf->data[out_pos] = part_len & 0xff
|
||||
out_pos += 1
|
||||
mem.copy(domain as ptr + part_start, buf->data + out_pos, part_len)
|
||||
out_pos += part_len
|
||||
part_start = i + 1
|
||||
i += 1
|
||||
|
||||
buf->data[out_pos] = 0
|
||||
return buf
|
||||
|
||||
func net.build_dns_query[domain_name: str, record_type: i64] : Blob
|
||||
name := net.encode_dns_name(domain_name)
|
||||
out := Blob.alloc(12 + name->size + 4)
|
||||
|
||||
// header
|
||||
id := (os.urandom_i64() % 65536)->abs()
|
||||
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)
|
||||
|
||||
name->free()
|
||||
return out
|
||||
Reference in New Issue
Block a user