net: low level socket api

pull/896/head
Justice Suh 2019-07-01 08:55:45 -04:00 committed by Alexander Medvednikov
parent d09758f723
commit 571410dd48
2 changed files with 162 additions and 135 deletions

View File

@ -1,150 +1,169 @@
module net module net
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <errno.h> #include <netinet/in.h>
#include <netdb.h>
struct Conn { struct Socket {
pub: pub:
sockfd int sockfd int
family int
_type int
proto int
} }
struct Listener { import const (
listener int AF_INET
their_addr voidptr AF_INET6
} AF_UNSPEC
SOCK_STREAM
import const ( SOL_SOCKET
AF_UNSPEC SO_REUSEADDR
AF_INET SO_REUSEPORT
SOCK_STREAM INADDR_ANY
AI_PASSIVE AI_PASSIVE
PF_INET SHUT_RD
SOL_SOCKET SHUT_WR
SO_REUSEADDR SHUT_RDWR
INADDR_ANY )
)
struct C.addrinfo {
mut:
ai_family int
ai_socktype int
ai_flags int
ai_protocol int
ai_addrlen int
ai_next voidptr
ai_addr voidptr
}
struct C.in_addr { struct C.in_addr {
mut: mut:
s_addr int s_addr int
}
}
struct C.sockaddr_in { struct C.sockaddr_in {
mut: mut:
sin_family int sin_family int
sin_port int sin_port int
sin_addr C.in_addr sin_addr C.in_addr
}
struct C.sockaddr_storage {
}
fn dial(addr string, port int) Conn {
println('net.dial("$addr":$port)')
mut hints := C.addrinfo{}
servinfo := &C.addrinfo{!}
mut rp := &C.addrinfo{!}
mut sockfd := -1
// allow IPv4 or IPv6
hints.ai_family = AF_UNSPEC
// avoid name lookup for port
// hints.ai_flags = AI_NUMERICSERV
hints.ai_socktype = SOCK_STREAM
hints.ai_flags = AI_PASSIVE
strport := '$port'
connbad := Conn{}
c_addr := addr.cstr()
rv := C.getaddrinfo(c_addr, strport.cstr(), &hints, &servinfo)
if rv != 0 {
println('Getaddrinfo failed ')
return connbad
}
// Loop through all the results and connect to the first we can
for rp = servinfo; !isnil(rp); rp = rp.ai_next {
sockfd = C.socket(rp.ai_family, rp.ai_socktype, rp.ai_protocol)
if sockfd == -1 {
continue
}
if C.connect(sockfd, rp.ai_addr, rp.ai_addrlen) {
C.close(sockfd)
continue
}
break
}
if sockfd == -1 {
println('socket: Cannot connect to host')
return connbad
}
C.freeaddrinfo(servinfo)
conn := Conn {
sockfd: sockfd,
}
return conn
} }
fn listen(addr string, port int) Listener { struct C.addrinfo {
mut hints := C.addrinfo{} mut:
res := &C.addrinfo{!} ai_family int
new_fd := 0 ai_socktype int
BACKLOG := 10 ai_flags int
strport := port.str() ai_protocol int
option := 1 ai_addrlen int
listener := C.socket(PF_INET, SOCK_STREAM, 0) ai_next voidptr
C.setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int)) ai_addr voidptr
bad_listener := Listener{their_addr: 0}
if listener < 0 {
println('couldnt create listen scoket')
return bad_listener
}
// socket address used for the server
mut server_address := C.sockaddr_in{}
server_address.sin_family = AF_INET
// htons: host to network short: transforms a value in host byte
// ordering format to a short value in network byte ordering format
server_address.sin_port = C.htons(port)
// htonl: host to network long: same as htons but to long
server_address.sin_addr.s_addr = C.htonl(INADDR_ANY)
size := 16 // sizeof(C.sockaddr_in)
if C.bind(listener, &server_address, size) < 0 {
println('cant bind')
return bad_listener
}
if C.listen(listener, BACKLOG) < 0 {
println('cant listen')
return bad_listener
}
l := Listener {
listener: listener,
their_addr: 0
}
return l
} }
// accept an incoming connection struct C.sockaddr_storage {}
fn (l Listener) accept() Conn {
their_addr := C.sockaddr_storage{} // create socket
addr_size := 128 // sizeof (l.their_addr) pub fn socket(family int, _type int, proto int) Socket {
return Conn { sockfd := C.socket(family, _type, proto)
sockfd: C.accept(l.listener, &their_addr, &addr_size) s := Socket {
} sockfd: sockfd
family: family
_type: _type
proto: proto
}
return s
} }
// set socket options
pub fn (s Socket) setsockopt(level int, optname int, optvalue int) int {
res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue))
return res
}
// bind socket to port
pub fn (s Socket) bind(port int) int {
mut addr := C.sockaddr_in{}
addr.sin_family = s.family
addr.sin_port = C.htons(port)
addr.sin_addr.s_addr = C.htonl(INADDR_ANY)
size := 16 // sizeof(C.sockaddr_in)
res := C.bind(s.sockfd, &addr, size)
return res
}
// put socket into passive mode and wait to receive
pub fn (s Socket) listen() int {
backlog := 128
res := C.listen(s.sockfd, backlog)
return res
}
// helper method to create, bind, and listen given port number
pub fn listen(port int) Socket {
s := socket(AF_INET, SOCK_STREAM, 0)
if s.sockfd == 0 {
println('socket: init socket failed')
return s
}
bind_res := s.bind(port)
if bind_res < 0 {
println('socket: bind failed')
return s
}
listen_res := s.listen()
if listen_res < 0 {
println('socket: listen failed')
return s
}
return s
}
// accept first connection request from socket queue
pub fn (s Socket) accept() Socket {
addr := C.sockaddr_storage{}
size := 128 // sizeof(sockaddr_storage)
sockfd := C.accept(s.sockfd, &addr, &size)
if sockfd < 0 {
println('socket: accept failed')
}
c := Socket {
sockfd: sockfd
family: s.family
_type: s._type
proto: s.proto
}
return c
}
// connect to given addrress and port
pub fn (s Socket) connect(address string, port int) int {
mut hints := C.addrinfo{}
hints.ai_family = AF_UNSPEC
hints.ai_socktype = SOCK_STREAM
hints.ai_flags = AI_PASSIVE
info := &C.addrinfo{!}
sport := '$port'
info_res := C.getaddrinfo(address.cstr(), sport.cstr(), &hints, &info)
if info_res != 0 {
println('socket: getaddrinfo failed')
return info_res
}
res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen)
return res
}
// helper method to create socket and connect
pub fn dial(address string, port int) Socket {
s := socket(AF_INET, SOCK_STREAM, 0)
res := s.connect(address, port)
if res < 0 {
println('socket: failed to connect')
}
return s
}
// shutdown and close socket
pub fn (s Socket) close() int {
shutdown_res := C.shutdown(s.sockfd, SHUT_RDWR)
if shutdown_res < 0 {
println('socket: shutdown failed')
}
res := C.close(s.sockfd)
if res < 0 {
println('socket: close failed')
}
return 0
}

View File

@ -1,6 +1,14 @@
import net import net
fn test_dial() { fn test_socket() {
//conn := net.dial('irc.freenode.org', 6667) // server := net.listen(8080)
//println(conn.sockfd) // println(server)
// client := net.dial('127.0.0.1', 8080)
// println(client)
// socket := server.accept()
// println(socket)
//
// server.close()
// client.close()
// socket.close()
} }