diff --git a/vlib/net/connection.v b/vlib/net/connection.v new file mode 100644 index 0000000000..b131f987ae --- /dev/null +++ b/vlib/net/connection.v @@ -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() ! +} diff --git a/vlib/net/dialer.v b/vlib/net/dialer.v new file mode 100644 index 0000000000..25b75bdbe3 --- /dev/null +++ b/vlib/net/dialer.v @@ -0,0 +1,6 @@ +module net + +// Dialer is an abstract dialer interface for producing connections to adresses. +pub interface Dialer { + dial(address string) !Connection +} diff --git a/vlib/net/mbedtls/ssl_connection.c.v b/vlib/net/mbedtls/ssl_connection.c.v index 00fc075889..445a9b3bc4 100644 --- a/vlib/net/mbedtls/ssl_connection.c.v +++ b/vlib/net/mbedtls/ssl_connection.c.v @@ -244,6 +244,11 @@ enum Select { 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 pub fn (mut s SSLConn) shutdown() ! { $if trace_ssl ? { @@ -407,6 +412,11 @@ pub fn (mut s SSLConn) dial(hostname string, port int) ! { 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 pub fn (s &SSLConn) peer_addr() !net.Addr { return net.peer_addr_from_socket_handle(s.handle) diff --git a/vlib/net/openssl/ssl_connection.c.v b/vlib/net/openssl/ssl_connection.c.v index e6fff45f67..8114d5e6e0 100644 --- a/vlib/net/openssl/ssl_connection.c.v +++ b/vlib/net/openssl/ssl_connection.c.v @@ -51,6 +51,11 @@ enum Select { 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 pub fn (mut s SSLConn) shutdown() ! { $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 pub fn (s &SSLConn) peer_addr() !net.Addr { return net.peer_addr_from_socket_handle(s.handle) diff --git a/vlib/net/socks/socks5.v b/vlib/net/socks/socks5.v index 6a1591783e..73739e25de 100644 --- a/vlib/net/socks/socks5.v +++ b/vlib/net/socks/socks5.v @@ -18,7 +18,9 @@ const auth_user_password = u8(2) // socks5_dial create new instance of &net.TcpConn pub fn socks5_dial(proxy_url string, host string, username string, password string) !&net.TcpConn { 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 @@ -35,7 +37,35 @@ pub fn socks5_ssl_dial(proxy_url string, host string, username string, password 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] if username.len > 0 { v << socks.auth_user_password diff --git a/vlib/net/ssl/dialer.v b/vlib/net/ssl/dialer.v new file mode 100644 index 0000000000..b86fd14e89 --- /dev/null +++ b/vlib/net/ssl/dialer.v @@ -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)! +} diff --git a/vlib/net/tcp.c.v b/vlib/net/tcp.c.v index d164646eec..8035d12aae 100644 --- a/vlib/net/tcp.c.v +++ b/vlib/net/tcp.c.v @@ -6,6 +6,21 @@ import strings pub const tcp_default_read_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] pub struct TcpConn { pub mut: @@ -18,6 +33,7 @@ pub mut: is_blocking bool = true } +// dial_tcp will try to create a new TcpConn to the given address. pub fn dial_tcp(oaddress string) !&TcpConn { mut address := oaddress $if windows { @@ -75,7 +91,7 @@ pub fn dial_tcp(oaddress string) !&TcpConn { 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 { addrs := resolve_addrs_fuzzy(saddr, .tcp) or { 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}') } +// close closes the tcp connection pub fn (mut c TcpConn) close() ! { $if trace_tcp ? { eprintln(' TcpConn.close | c.sock.handle: ${c.sock.handle:6}') @@ -118,6 +135,8 @@ pub fn (mut c TcpConn) 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 { mut should_ewouldblock := false mut res := $if is_coroutine ? { @@ -175,6 +194,9 @@ pub fn (c TcpConn) read_ptr(buf_ptr &u8, len int) !int { 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 { return c.read_ptr(buf.data, buf.len)! } diff --git a/vlib/net/unix/stream.c.v b/vlib/net/unix/stream.c.v index 7c7d77ff7b..5f96adcd40 100644 --- a/vlib/net/unix/stream.c.v +++ b/vlib/net/unix/stream.c.v @@ -9,6 +9,16 @@ const unix_default_write_timeout = 30 * time.second const connect_timeout = 5 * time.second 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] pub struct StreamConn { 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 pub fn (mut c StreamConn) close() ! { $if trace_unix ? {