227 lines
6.9 KiB
Plaintext
227 lines
6.9 KiB
Plaintext
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
|
|
err.clear()
|
|
let s: i64 = _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0)
|
|
if s < 0
|
|
err.set(ERR_SYSCALL_FAILED, "net.listen?: failed to create a socket")
|
|
return -1
|
|
|
|
let optval = 1
|
|
if _builtin_syscall(SYS_setsockopt, s, SOL_SOCKET, SO_REUSEADDR, ^optval, 8) < 0
|
|
_builtin_syscall(SYS_close, s)
|
|
err.set(ERR_SYSCALL_FAILED, "net.listen?: setsockopt() failed")
|
|
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)
|
|
err.set(ERR_SYSCALL_FAILED, "net.listen?: failed to bind")
|
|
return -1
|
|
mem.free(sa)
|
|
|
|
if _builtin_syscall(SYS_listen, s, 128) < 0
|
|
_builtin_syscall(SYS_close, s)
|
|
err.set(ERR_SYSCALL_FAILED, "net.listen?: listen() failed")
|
|
return -1
|
|
|
|
return s
|
|
|
|
func net.connect?[packed_host: i64, port: i64] : i64
|
|
err.clear()
|
|
let s: i64 = _builtin_syscall(SYS_socket, AF_INET, SOCK_STREAM, 0)
|
|
if s < 0
|
|
err.set(ERR_SYSCALL_FAILED, "net.connect?: failed to create a socket")
|
|
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)
|
|
err.set(ERR_CONNECTION_FAILED, "net.connect?: connection failed")
|
|
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
|
|
err.clear()
|
|
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)
|
|
err.set(ERR_SYSCALL_FAILED, "net.create_udp_client?: failed to create a socket")
|
|
return 0 as net.UDPSocket
|
|
|
|
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.create_udp_server?[packed_host: i64, port: i64] : net.UDPSocket
|
|
err.clear()
|
|
let s: net.UDPSocket = net.create_udp_client?(packed_host, port)
|
|
if err.check()
|
|
return 0 as net.UDPSocket
|
|
|
|
if _builtin_syscall(SYS_bind, s->fd, s->addr, 16) < 0
|
|
net.UDPSocket.close_and_free(s)
|
|
err.set(ERR_SYSCALL_FAILED, "net.create_udp_server?: failed to bind")
|
|
return 0 as net.UDPSocket
|
|
|
|
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 = must(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 as ptr + 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 = must(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
|
|
|
|
// TODO: dont resolve IPs
|
|
func net.resolve?[domain: str] : i64
|
|
err.clear()
|
|
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 err.check()
|
|
return -1
|
|
|
|
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] as i64, pkt->data[pos+1] as i64, pkt->data[pos+2] as i64, pkt->data[pos+3] as i64)
|
|
net.UDPPacket.free(pkt)
|
|
return out
|