net: low level socket api
parent
d09758f723
commit
571410dd48
|
@ -1,32 +1,44 @@
|
||||||
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
|
||||||
struct Listener {
|
proto int
|
||||||
listener int
|
|
||||||
their_addr voidptr
|
|
||||||
}
|
}
|
||||||
|
|
||||||
import const (
|
import const (
|
||||||
AF_UNSPEC
|
|
||||||
AF_INET
|
AF_INET
|
||||||
|
AF_INET6
|
||||||
|
AF_UNSPEC
|
||||||
SOCK_STREAM
|
SOCK_STREAM
|
||||||
AI_PASSIVE
|
|
||||||
PF_INET
|
|
||||||
SOL_SOCKET
|
SOL_SOCKET
|
||||||
SO_REUSEADDR
|
SO_REUSEADDR
|
||||||
|
SO_REUSEPORT
|
||||||
INADDR_ANY
|
INADDR_ANY
|
||||||
|
AI_PASSIVE
|
||||||
|
SHUT_RD
|
||||||
|
SHUT_WR
|
||||||
|
SHUT_RDWR
|
||||||
)
|
)
|
||||||
|
|
||||||
|
struct C.in_addr {
|
||||||
|
mut:
|
||||||
|
s_addr int
|
||||||
|
}
|
||||||
|
|
||||||
|
struct C.sockaddr_in {
|
||||||
|
mut:
|
||||||
|
sin_family int
|
||||||
|
sin_port int
|
||||||
|
sin_addr C.in_addr
|
||||||
|
}
|
||||||
|
|
||||||
struct C.addrinfo {
|
struct C.addrinfo {
|
||||||
mut:
|
mut:
|
||||||
ai_family int
|
ai_family int
|
||||||
|
@ -38,113 +50,120 @@ mut:
|
||||||
ai_addr voidptr
|
ai_addr voidptr
|
||||||
}
|
}
|
||||||
|
|
||||||
struct C.in_addr {
|
struct C.sockaddr_storage {}
|
||||||
mut:
|
|
||||||
s_addr int
|
|
||||||
|
|
||||||
|
// create socket
|
||||||
|
pub fn socket(family int, _type int, proto int) Socket {
|
||||||
|
sockfd := C.socket(family, _type, proto)
|
||||||
|
s := Socket {
|
||||||
|
sockfd: sockfd
|
||||||
|
family: family
|
||||||
|
_type: _type
|
||||||
|
proto: proto
|
||||||
|
}
|
||||||
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
struct C.sockaddr_in {
|
// set socket options
|
||||||
mut:
|
pub fn (s Socket) setsockopt(level int, optname int, optvalue int) int {
|
||||||
sin_family int
|
res := C.setsockopt(s.sockfd, level, optname, optvalue, C.sizeof(optvalue))
|
||||||
sin_port int
|
return res
|
||||||
sin_addr C.in_addr
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct C.sockaddr_storage {
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dial(addr string, port int) Conn {
|
// put socket into passive mode and wait to receive
|
||||||
println('net.dial("$addr":$port)')
|
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{}
|
mut hints := C.addrinfo{}
|
||||||
servinfo := &C.addrinfo{!}
|
|
||||||
mut rp := &C.addrinfo{!}
|
|
||||||
mut sockfd := -1
|
|
||||||
// allow IPv4 or IPv6
|
|
||||||
hints.ai_family = AF_UNSPEC
|
hints.ai_family = AF_UNSPEC
|
||||||
// avoid name lookup for port
|
|
||||||
// hints.ai_flags = AI_NUMERICSERV
|
|
||||||
hints.ai_socktype = SOCK_STREAM
|
hints.ai_socktype = SOCK_STREAM
|
||||||
hints.ai_flags = AI_PASSIVE
|
hints.ai_flags = AI_PASSIVE
|
||||||
strport := '$port'
|
|
||||||
connbad := Conn{}
|
info := &C.addrinfo{!}
|
||||||
c_addr := addr.cstr()
|
sport := '$port'
|
||||||
rv := C.getaddrinfo(c_addr, strport.cstr(), &hints, &servinfo)
|
info_res := C.getaddrinfo(address.cstr(), sport.cstr(), &hints, &info)
|
||||||
if rv != 0 {
|
if info_res != 0 {
|
||||||
println('Getaddrinfo failed ')
|
println('socket: getaddrinfo failed')
|
||||||
return connbad
|
return info_res
|
||||||
}
|
}
|
||||||
// Loop through all the results and connect to the first we can
|
|
||||||
for rp = servinfo; !isnil(rp); rp = rp.ai_next {
|
res := C.connect(s.sockfd, info.ai_addr, info.ai_addrlen)
|
||||||
sockfd = C.socket(rp.ai_family, rp.ai_socktype, rp.ai_protocol)
|
return res
|
||||||
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 {
|
// helper method to create socket and connect
|
||||||
mut hints := C.addrinfo{}
|
pub fn dial(address string, port int) Socket {
|
||||||
res := &C.addrinfo{!}
|
s := socket(AF_INET, SOCK_STREAM, 0)
|
||||||
new_fd := 0
|
res := s.connect(address, port)
|
||||||
BACKLOG := 10
|
if res < 0 {
|
||||||
strport := port.str()
|
println('socket: failed to connect')
|
||||||
option := 1
|
|
||||||
listener := C.socket(PF_INET, SOCK_STREAM, 0)
|
|
||||||
C.setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(int))
|
|
||||||
bad_listener := Listener{their_addr: 0}
|
|
||||||
if listener < 0 {
|
|
||||||
println('couldnt create listen scoket')
|
|
||||||
return bad_listener
|
|
||||||
}
|
}
|
||||||
// socket address used for the server
|
return s
|
||||||
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
|
// shutdown and close socket
|
||||||
fn (l Listener) accept() Conn {
|
pub fn (s Socket) close() int {
|
||||||
their_addr := C.sockaddr_storage{}
|
shutdown_res := C.shutdown(s.sockfd, SHUT_RDWR)
|
||||||
addr_size := 128 // sizeof (l.their_addr)
|
if shutdown_res < 0 {
|
||||||
return Conn {
|
println('socket: shutdown failed')
|
||||||
sockfd: C.accept(l.listener, &their_addr, &addr_size)
|
|
||||||
}
|
}
|
||||||
|
res := C.close(s.sockfd)
|
||||||
|
if res < 0 {
|
||||||
|
println('socket: close failed')
|
||||||
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue