net: add net.Dialer and net.Connection interfaces, abstracting the different types of connections, already supported by the V network stack (#21657)

This commit is contained in:
dasidiot 2024-08-05 11:16:06 -04:00 committed by GitHub
parent 49f5ebf717
commit 576a0abcc7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 136 additions and 3 deletions

13
vlib/net/connection.v Normal file
View File

@ -0,0 +1,13 @@
module net
// Connection provides a generic SOCK_STREAM style interface that protocols can use
// as a base connection object to support TCP, UNIX Domain Sockets and various
// proxying solutions.
pub interface Connection {
addr() !Addr
peer_addr() !Addr
mut:
read(mut []u8) !int
write([]u8) !int
close() !
}

6
vlib/net/dialer.v Normal file
View File

@ -0,0 +1,6 @@
module net
// Dialer is an abstract dialer interface for producing connections to adresses.
pub interface Dialer {
dial(address string) !Connection
}

View File

@ -244,6 +244,11 @@ enum Select {
except except
} }
// close terminates the ssl connection and does cleanup
pub fn (mut s SSLConn) close() ! {
s.shutdown()!
}
// shutdown terminates the ssl connection and does cleanup // shutdown terminates the ssl connection and does cleanup
pub fn (mut s SSLConn) shutdown() ! { pub fn (mut s SSLConn) shutdown() ! {
$if trace_ssl ? { $if trace_ssl ? {
@ -407,6 +412,11 @@ pub fn (mut s SSLConn) dial(hostname string, port int) ! {
s.opened = true s.opened = true
} }
// addr retrieves the local ip address and port number for this connection
pub fn (s &SSLConn) addr() !net.Addr {
return net.addr_from_socket_handle(s.handle)
}
// peer_addr retrieves the ip address and port number used by the peer // peer_addr retrieves the ip address and port number used by the peer
pub fn (s &SSLConn) peer_addr() !net.Addr { pub fn (s &SSLConn) peer_addr() !net.Addr {
return net.peer_addr_from_socket_handle(s.handle) return net.peer_addr_from_socket_handle(s.handle)

View File

@ -51,6 +51,11 @@ enum Select {
except except
} }
// close closes the ssl connection and does cleanup
pub fn (mut s SSLConn) close() ! {
s.shutdown()!
}
// shutdown closes the ssl connection and does cleanup // shutdown closes the ssl connection and does cleanup
pub fn (mut s SSLConn) shutdown() ! { pub fn (mut s SSLConn) shutdown() ! {
$if trace_ssl ? { $if trace_ssl ? {
@ -247,6 +252,11 @@ fn (mut s SSLConn) complete_connect() ! {
} }
} }
// addr retrieves the local ip address and port number for this connection
pub fn (s &SSLConn) addr() !net.Addr {
return net.addr_from_socket_handle(s.handle)
}
// peer_addr retrieves the ip address and port number used by the peer // peer_addr retrieves the ip address and port number used by the peer
pub fn (s &SSLConn) peer_addr() !net.Addr { pub fn (s &SSLConn) peer_addr() !net.Addr {
return net.peer_addr_from_socket_handle(s.handle) return net.peer_addr_from_socket_handle(s.handle)

View File

@ -18,7 +18,9 @@ const auth_user_password = u8(2)
// socks5_dial create new instance of &net.TcpConn // socks5_dial create new instance of &net.TcpConn
pub fn socks5_dial(proxy_url string, host string, username string, password string) !&net.TcpConn { pub fn socks5_dial(proxy_url string, host string, username string, password string) !&net.TcpConn {
mut con := net.dial_tcp(proxy_url)! mut con := net.dial_tcp(proxy_url)!
return handshake(mut con, host, username, password)! socks_conn_as_interface := handshake(mut con, host, username, password)!
socks_conn := socks_conn_as_interface as net.TcpConn
return &socks_conn
} }
// socks5_ssl_dial create new instance of &ssl.SSLConn // socks5_ssl_dial create new instance of &ssl.SSLConn
@ -35,7 +37,35 @@ pub fn socks5_ssl_dial(proxy_url string, host string, username string, password
return ssl_conn return ssl_conn
} }
fn handshake(mut con net.TcpConn, host string, username string, password string) !&net.TcpConn { // SOCKS5Dialer implements the Dialer interface initiating connections through a SOCKS5 proxy.
pub struct SOCKS5Dialer {
pub:
dialer net.Dialer
proxy_address string
username string
password string
}
// new_socks5_dialer creates a dialer that will use a SOCKS5 proxy server to
// initiate connections. An underlying dialer is required to initiate the
// connection to the proxy server. Most users should use either
// net.default_tcp_dialer or ssl.create_ssl_dialer.
pub fn new_socks5_dialer(base net.Dialer, proxy_address string, username string, password string) net.Dialer {
return &SOCKS5Dialer{
dialer: base
proxy_address: proxy_address
username: username
password: password
}
}
// dial initiates a new connection through the SOCKS5 proxy.
pub fn (sd SOCKS5Dialer) dial(address string) !net.Connection {
mut conn := sd.dialer.dial(sd.proxy_address)!
return handshake(mut conn, address, sd.username, sd.password)!
}
fn handshake(mut con net.Connection, host string, username string, password string) !net.Connection {
mut v := [socks.socks_version5, 1] mut v := [socks.socks_version5, 1]
if username.len > 0 { if username.len > 0 {
v << socks.auth_user_password v << socks.auth_user_password

22
vlib/net/ssl/dialer.v Normal file
View File

@ -0,0 +1,22 @@
module ssl
import net
// SSLDialer is a concrete instance of the Dialer interface,
// for creating SSL socket connections.
pub struct SSLDialer {
config SSLConnectConfig
}
// create_ssl_dialer creates a dialer that will initiate SSL secured
// connections.
pub fn new_ssl_dialer(config SSLConnectConfig) net.Dialer {
return &SSLDialer{
config: config
}
}
// dial initiates a new SSL connection.
pub fn (d SSLDialer) dial(address string) !net.Connection {
return new_ssl_conn(d.config)!
}

View File

@ -6,6 +6,21 @@ import strings
pub const tcp_default_read_timeout = 30 * time.second pub const tcp_default_read_timeout = 30 * time.second
pub const tcp_default_write_timeout = 30 * time.second pub const tcp_default_write_timeout = 30 * time.second
// TCPDialer is a concrete instance of the Dialer interface,
// for creating tcp connections.
pub struct TCPDialer {}
// dial will try to create a new abstract connection to the given address.
// It will return an error, if that is not possible.
pub fn (t TCPDialer) dial(address string) !Connection {
return dial_tcp(address)!
}
// default_tcp_dialer will give you an instance of Dialer, that is suitable for making new tcp connections.
pub fn default_tcp_dialer() Dialer {
return &TCPDialer{}
}
@[heap] @[heap]
pub struct TcpConn { pub struct TcpConn {
pub mut: pub mut:
@ -18,6 +33,7 @@ pub mut:
is_blocking bool = true is_blocking bool = true
} }
// dial_tcp will try to create a new TcpConn to the given address.
pub fn dial_tcp(oaddress string) !&TcpConn { pub fn dial_tcp(oaddress string) !&TcpConn {
mut address := oaddress mut address := oaddress
$if windows { $if windows {
@ -75,7 +91,7 @@ pub fn dial_tcp(oaddress string) !&TcpConn {
return error(err_builder.str()) return error(err_builder.str())
} }
// bind local address and dial. // dial_tcp_with_bind will bind the given local address `laddr` and dial.
pub fn dial_tcp_with_bind(saddr string, laddr string) !&TcpConn { pub fn dial_tcp_with_bind(saddr string, laddr string) !&TcpConn {
addrs := resolve_addrs_fuzzy(saddr, .tcp) or { addrs := resolve_addrs_fuzzy(saddr, .tcp) or {
return error('${err.msg()}; could not resolve address ${saddr} in dial_tcp_with_bind') return error('${err.msg()}; could not resolve address ${saddr} in dial_tcp_with_bind')
@ -111,6 +127,7 @@ pub fn dial_tcp_with_bind(saddr string, laddr string) !&TcpConn {
return error('dial_tcp_with_bind failed for address ${saddr}') return error('dial_tcp_with_bind failed for address ${saddr}')
} }
// close closes the tcp connection
pub fn (mut c TcpConn) close() ! { pub fn (mut c TcpConn) close() ! {
$if trace_tcp ? { $if trace_tcp ? {
eprintln(' TcpConn.close | c.sock.handle: ${c.sock.handle:6}') eprintln(' TcpConn.close | c.sock.handle: ${c.sock.handle:6}')
@ -118,6 +135,8 @@ pub fn (mut c TcpConn) close() ! {
c.sock.close()! c.sock.close()!
} }
// read_ptr reads data from the tcp connection to the given buffer. It reads at most `len` bytes.
// It returns the number of actually read bytes, which can vary between 0 to `len`.
pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int { pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int {
mut should_ewouldblock := false mut should_ewouldblock := false
mut res := $if is_coroutine ? { mut res := $if is_coroutine ? {
@ -175,6 +194,9 @@ pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int {
return error('none') return error('none')
} }
// read reads data from the tcp connection into the mutable buffer `buf`.
// The number of bytes read is limited to the length of the buffer `buf.len`.
// The returned value is the number of read bytes (between 0 and `buf.len`).
pub fn (c TcpConn) read(mut buf []u8) !int { pub fn (c TcpConn) read(mut buf []u8) !int {
return c.read_ptr(buf.data, buf.len)! return c.read_ptr(buf.data, buf.len)!
} }

View File

@ -9,6 +9,16 @@ const unix_default_write_timeout = 30 * time.second
const connect_timeout = 5 * time.second const connect_timeout = 5 * time.second
const msg_nosignal = 0x4000 const msg_nosignal = 0x4000
// UnixDialer is a concrete instance of the Dialer interface,
// for creating unix socket connections.
pub struct UnixDialer {}
// dial will try to create a new abstract connection to the given address.
// It will return an error, if that is not possible.
pub fn (u UnixDialer) dial(address string) !net.Connection {
return connect_stream(address)!
}
@[heap] @[heap]
pub struct StreamConn { pub struct StreamConn {
pub mut: pub mut:
@ -40,6 +50,16 @@ pub fn connect_stream(socket_path string) !&StreamConn {
} }
} }
// addr returns the local address of the stream
pub fn (c StreamConn) addr() !net.Addr {
return error('not implemented for unix connections')
}
// peer_addr returns the address of the remote peer of the stream
pub fn (c StreamConn) peer_addr() !net.Addr {
return error('not implemented for unix connections')
}
// close closes the connection // close closes the connection
pub fn (mut c StreamConn) close() ! { pub fn (mut c StreamConn) close() ! {
$if trace_unix ? { $if trace_unix ? {