From 0a595158e23e882a7f5361ab20b140ceec86a4a1 Mon Sep 17 00:00:00 2001 From: Kristian Larsson Date: Sat, 8 Jul 2023 23:29:45 +0200 Subject: [PATCH] Add net.TCPConnection & remove TCPIPConnection This adds a new TCP client connection actor which replaces the older TCPIPConnection actor. It improves on the old implementation through the addition of DNS resolution and Happy Eyeballs (RFC6555). While not super complex it is non-trivial compared to the old connection actor and so there are probably bugs lurking. More test cases are needed. Right now we do a rather aggressive Happy Eyeballs where we instantly connect both to IPv4 and IPv6. This effectively doubles the resources consumed, fds on our end and more on the server side. While the RFC allows for this, the recommendation is to have a slight delay for the IPv4 side, like I think Chrome does 300ms or so. We can look into that. Added net.is_ipv4() and net.is_ipv6() as well. Originally had ideas about just adding this and letting the older TCPIPConnection remain but as we've made backwards incompatible changes anyway, it's just easier to replace it right away. --- CHANGELOG.md | 16 ++ base/src/net.act | 126 +++++++++- base/src/net.ext.c | 221 +++++++++++++----- .../src/security/capabilities.md | 4 +- examples/client.act | 6 +- test/Makefile | 2 +- test/rts_db/ddb_test_client.act | 2 +- test/rts_db/test_tcp_client.act | 8 +- test/rts_db/test_tcp_server.act | 6 +- test/stdlib_auto/test_net.act | 24 ++ test/stdlib_auto/test_net_tcp.act | 2 +- 11 files changed, 342 insertions(+), 75 deletions(-) create mode 100644 test/stdlib_auto/test_net.act diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fa01c46b..542d43bca 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,9 +3,25 @@ ## Unreleased ### Added +- `net.TCPConnection`: A new TCP client connection actor [#1398] + - Support DNS lookups so the input address can be a name rather than an IP + address + - It is still possible to connect using an IP address + - Supports Happy Eyeballs (RFC6555) which uses both IPv4 and IPv6 + simultaneously to connect using the fastest transport + - Happy Eyeballs is only used for hostnames. If an IP address is provided, + we connect directly to it. +- `net.is_ipv4(address: str) -> bool` tells you if something is an IPv4 address +- `net.is_ipv6(address: str) -> bool` tells you if something is an IPv6 address - AbE: Documented capability based security [#1267] ### Changed +- `net.TCPIPConnection` is removed and replaced by `net.TCPConnection` + - Originally opted to add `net.TCPConnection` now and phase out + `net.TCPIPConnection` later but as there is already a breaking change with + the change of Auth -> Cap so all user code related to networking (and other + things) need to be changed, we might as well change from + `net.TCPIPConnection` to `net.TCPConnection` - `DNS` actor is replaced with lookup functions [#1406] - The `DNS` actor is removed, the `lookup_a` & `lookup_aaaa` methods it has are now free functions in the `net` module, i.e. simply call `net.lookup_a` diff --git a/base/src/net.act b/base/src/net.act index df31b98d7..d150d2d3f 100644 --- a/base/src/net.act +++ b/base/src/net.act @@ -30,6 +30,11 @@ class _TCPListenConnectCap(): """Internal use only""" pass +def is_ipv4(address: str) -> bool: + NotImplemented + +def is_ipv6(address: str) -> bool: + NotImplemented def _lookup_a(name: str, on_resolve: action(list[str]) -> None, on_error: action(str, str) -> None) -> None: """Perform DNS lookup for name of record type A @@ -53,32 +58,135 @@ def lookup_aaaa(cap: DNSCap, name: str, on_resolve: action(list[str]) -> None, o _lookup_aaaa(name, on_resolve, on_error) -actor TCPIPConnection(cap: TCPConnectCap, address: str, port: int, on_connect: action(TCPIPConnection) -> None, on_receive: action(TCPIPConnection, bytes) -> None, on_error: action(TCPIPConnection, str) -> None): +actor TCPConnection(cap: TCPConnectCap, address: str, port: int, on_connect: action(TCPConnection) -> None, on_receive: action(TCPConnection, bytes) -> None, on_error: action(TCPConnection, str) -> None): """TCP IP Connection""" - _socket = -1 + var _a_res: list[str] = [] + var _aaaa_res: list[str] = [] + var _sock = -1 + var _sock4 = -1 + var _sock4_state = 0 + var _sock4_error = "" + var _sock6 = -1 + var _sock6_state = 0 + var _sock6_error = "" + var _state = 0 + + var _connections = 0 + var _bytes_in: u64 = 0 + var _bytes_out: u64 = 0 + + proc def _pin_affinity() -> None: + NotImplemented + _pin_affinity() - action def close(on_close: action(TCPIPConnection) -> None) -> None: - """Close the connection""" + # DNS A + def _on_dns_a_resolve(result): + _a_res = result + _connect4(result[0]) + + def _on_dns_a_error(name, msg): + _lookup_a(address, _on_dns_a_resolve, _on_dns_a_error) + + # DNS AAAA + def _on_dns_aaaa_resolve(result): + _aaaa_res = result + _connect6(result[0]) + + def _on_dns_aaaa_error(name, msg): + _lookup_aaaa(address, _on_dns_aaaa_resolve, _on_dns_aaaa_error) + + action def _on_tcp_error(sockfamily: int, err: int, errmsg: str): + if sockfamily == 4: + if _sock6_error != "": + on_error(self, errmsg + " IPv6 error: " + _sock6_error) + _sock4_error = errmsg + elif sockfamily == 6: + if _sock4_error != "": + on_error(self, errmsg + " IPv4 error: " + _sock4_error) + _sock6_error = errmsg + + + # TCP connect over IPv4 + proc def _connect4(ip_address: str): NotImplemented - def reconnect(): - close(_connect) + action def _on_connect4(): + _sock4_error = "" + _on_connect(_sock4) + + # TCP connect over IPv6 + proc def _connect6(ip_address: str): + NotImplemented + + action def _on_connect6(): + _sock6_error = "" + _on_connect(_sock6) + + # Handle on_connect event + proc def _on_connect(sock: int): + if _state == 2: + pass + elif _state == 1: + _state = 2 + _sock = sock + _read_start() + _connections += 1 + on_connect(self) + else: + print("Unexpected state", _state) + + proc def _read_start(): + NotImplemented action def write(data: bytes) -> None: """Write data to remote""" NotImplemented + action def close(on_close: action(TCPConnection) -> None) -> None: + """Close the connection""" + NotImplemented + + def reconnect(): + close(_connect) + + def _connect(c): + _state = 1 + if is_ipv4(address): + _connect4(address) + elif is_ipv6(address): + _connect6(address) + else: + _lookup_aaaa(address, _on_dns_aaaa_resolve, _on_dns_aaaa_error) + _lookup_a(address, _on_dns_a_resolve, _on_dns_a_error) + mut def __resume__() -> None: NotImplemented - proc def _init(): + action def ip_version() -> ?int: NotImplemented - proc def _connect(c): + action def local_address() -> str: NotImplemented - _init() + + action def remote_address() -> str: + NotImplemented + + action def metrics() -> list[u64]: + return [ + _connections, + _bytes_in, + _bytes_out + ] + + # TODO: get rid of this, but how to call _on_connect4 directly? + var _fun_oncon4: action() -> None = _on_connect4 + var _fun_oncon6: action() -> None = _on_connect6 + var _fun_on_tcp_error: action(int, int, str) -> None = _on_tcp_error + _connect(self) + + actor TCPListenConnection(cap: _TCPListenConnectCap, server_client: int): """TCP Listener Connection""" var client: int = -1 diff --git a/base/src/net.ext.c b/base/src/net.ext.c index 9e984c566..f4e6d4fc3 100644 --- a/base/src/net.ext.c +++ b/base/src/net.ext.c @@ -9,6 +9,25 @@ void netQ___ext_init__() { // NOP } + +B_bool netQ_is_ipv4 (B_str address) { + struct sockaddr_in sa; + if (inet_pton(AF_INET, fromB_str(address), &(sa.sin_addr)) == 0) { + return B_False; + } else { + return B_True; + } +} + +B_bool netQ_is_ipv6 (B_str address) { + struct sockaddr_in6 sa; + if (inet_pton(AF_INET6, fromB_str(address), &(sa.sin6_addr)) == 0) { + return B_False; + } else { + return B_True; + } +} + struct dns_cb_data { struct addrinfo *hints; char* hostname; @@ -146,75 +165,139 @@ B_NoneType netQ__lookup_aaaa (B_str name, $action on_resolve, $action on_error) return B_None; } -void netQ_TCPIPConnection__on_receive(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { +void netQ_TCPConnection__on_receive(uv_stream_t *stream, ssize_t nread, const uv_buf_t *buf) { if (nread < 0){ if (nread == UV_EOF) { uv_close((uv_handle_t *)stream, NULL); } } else if (nread > 0) { if (stream->data) { - netQ_TCPIPConnection self = stream->data; + netQ_TCPConnection self = stream->data; $action2 f = self->on_receive; f->$class->__asyn__(f, self, to$bytesD_len(buf->base, nread)); + // TODO: count read bytes + self->_bytes_in->val += nread; } } +} - // No free with GC, but maybe one day we do this explicitly? - //if (buf->base) - // free(buf->base); + +$R netQ_TCPConnectionD__pin_affinity (netQ_TCPConnection self, $Cont c$cont) { + pin_actor_affinity(); + return $R_CONT(c$cont, B_None); } -void on_connect(uv_connect_t *connect_req, int status) { - netQ_TCPIPConnection self = (netQ_TCPIPConnection)connect_req->data; +void on_connect4(uv_connect_t *connect_req, int status) { + netQ_TCPConnection self = (netQ_TCPConnection)connect_req->data; if (status != 0) { - char errmsg[1024] = "Error in TCP connect: "; + char errmsg[1024] = "Error in TCP connect over IPv4: "; uv_strerror_r(status, errmsg + strlen(errmsg), sizeof(errmsg)-strlen(errmsg)); log_warn(errmsg); - $action2 f = self->on_error; - f->$class->__asyn__(f, self, to$str(errmsg)); - // NOTE: free() here if do manual memory management in I/O one day + $action3 f = self->_fun_on_tcp_error; + f->$class->__asyn__(f, to$int(4), to$int(status), to$str(errmsg)); return; } + $action f = self->_fun_oncon4; + f->$class->__asyn__(f, self); +} - connect_req->handle->data = self; - int r = uv_read_start(connect_req->handle, alloc_buffer, netQ_TCPIPConnection__on_receive); - if (r < 0) { - char errmsg[1024] = "Failed to start reading from TCP client socket: "; - uv_strerror_r(r, errmsg + strlen(errmsg), sizeof(errmsg)-strlen(errmsg)); +void on_connect6(uv_connect_t *connect_req, int status) { + netQ_TCPConnection self = (netQ_TCPConnection)connect_req->data; + + if (status != 0) { + char errmsg[1024] = "Error in TCP connect over IPv6: "; + uv_strerror_r(status, errmsg + strlen(errmsg), sizeof(errmsg)-strlen(errmsg)); log_warn(errmsg); - $action2 f = self->on_error; - f->$class->__asyn__(f, self, to$str(errmsg)); + $action3 f = self->_fun_on_tcp_error; + f->$class->__asyn__(f, to$int(6), to$int(status), to$str(errmsg)); return; } - - $action f = self->on_connect; + $action f = self->_fun_oncon6; f->$class->__asyn__(f, self); } -$R netQ_TCPIPConnectionD__init (netQ_TCPIPConnection self, $Cont c$cont) { - pin_actor_affinity(); +$R netQ_TCPConnectionD__connect4 (netQ_TCPConnection self, $Cont c$cont, B_str ip_address) { + log_debug("TCP connecting over IPv4 to %s", fromB_str(ip_address)); + uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t)); + uv_tcp_init(get_uv_loop(), socket); + self->_sock4 = to$int((long)socket); + + uv_connect_t* connect_req = (uv_connect_t*)malloc(sizeof(uv_connect_t)); + connect_req->data = (void *)self; + + struct sockaddr_in dest; + uv_ip4_addr(fromB_str(ip_address), from$int(self->port), &dest); + + uv_tcp_connect(connect_req, socket, (const struct sockaddr*)&dest, on_connect4); + return $R_CONT(c$cont, B_None); } -$R netQ_TCPIPConnectionD__connect (netQ_TCPIPConnection self, $Cont c$cont, netQ_TCPIPConnection c) { +$R netQ_TCPConnectionD__connect6 (netQ_TCPConnection self, $Cont c$cont, B_str ip_address) { + log_debug("TCP connecting over IPv6 to %s", fromB_str(ip_address)); uv_tcp_t* socket = (uv_tcp_t*)malloc(sizeof(uv_tcp_t)); uv_tcp_init(get_uv_loop(), socket); - self->_socket = to$int((long)socket); + self->_sock6 = to$int((long)socket); uv_connect_t* connect_req = (uv_connect_t*)malloc(sizeof(uv_connect_t)); connect_req->data = (void *)self; - struct sockaddr_in dest; - uv_ip4_addr(fromB_str(self->address), from$int(self->port), &dest); + struct sockaddr_in6 dest; + uv_ip6_addr(fromB_str(ip_address), from$int(self->port), &dest); - uv_tcp_connect(connect_req, socket, (const struct sockaddr*)&dest, on_connect); + uv_tcp_connect(connect_req, socket, (const struct sockaddr*)&dest, on_connect6); return $R_CONT(c$cont, B_None); } +$R netQ_TCPConnectionD__read_start (netQ_TCPConnection self, $Cont c$cont) { + uv_tcp_t* socket = (uv_tcp_t *)from$int(self->_sock); + socket->data = self; + int r = uv_read_start(socket, alloc_buffer, netQ_TCPConnection__on_receive); + if (r < 0) { + char errmsg[1024] = "Failed to start reading from TCP client socket: "; + uv_strerror_r(r, errmsg + strlen(errmsg), sizeof(errmsg)-strlen(errmsg)); + log_warn(errmsg); + $action2 f = self->on_error; + f->$class->__asyn__(f, self, to$str(errmsg)); + return $R_CONT(c$cont, B_None); + } + + return $R_CONT(c$cont, B_None); +} + +$R netQ_TCPConnectionD_writeG_local (netQ_TCPConnection self, $Cont c$cont, B_bytes data) { + uv_stream_t *stream = (uv_stream_t *)from$int(self->_sock); + // fd == -1 means invalid FD and can happen after __resume__ + if (stream == -1) + return $R_CONT(c$cont, B_None); + + uv_write_t *req = (uv_write_t *)malloc(sizeof(uv_write_t)); + uv_buf_t buf = uv_buf_init(data->str, data->nbytes); + int r = uv_write(req, stream, &buf, 1, NULL); + if (r < 0) { + char errmsg[1024] = "Failed to write to TCP socket: "; + uv_strerror_r(r, errmsg + strlen(errmsg), sizeof(errmsg)-strlen(errmsg)); + log_warn(errmsg); + $action2 f = self->on_error; + f->$class->__asyn__(f, self, to$str(errmsg)); + } + self->_bytes_out->val += data->nbytes; + return $R_CONT(c$cont, B_None); +} + +B_NoneType netQ_TCPConnectionD___resume__ (netQ_TCPConnection self) { + self->_sock = to$int(-1); + self->_sock4 = to$int(-1); + self->_sock6 = to$int(-1); + $action2 f = self->on_error; + f->$class->__asyn__(f, self, to$str("resume")); + return B_None; +} + struct close_cb_data { - netQ_TCPIPConnection self; + netQ_TCPConnection self; $action on_close; }; @@ -228,8 +311,8 @@ static void after_shutdown(uv_shutdown_t* req, int status) { on_close->$class->__asyn__(on_close, cb_data->self); } -$R netQ_TCPIPConnectionD_closeG_local (netQ_TCPIPConnection self, $Cont c$cont, $action on_close) { - uv_stream_t *stream = (uv_stream_t *)from$int(self->_socket); +$R netQ_TCPConnectionD_closeG_local (netQ_TCPConnection self, $Cont c$cont, $action on_close) { + uv_stream_t *stream = (uv_stream_t *)from$int(self->_sock); // fd == -1 means invalid FD and can happen after __resume__ if (stream == -1) return $R_CONT(c$cont, B_None); @@ -241,7 +324,9 @@ static void after_shutdown(uv_shutdown_t* req, int status) { uv_shutdown_t *req = (uv_shutdown_t *)malloc(sizeof(uv_shutdown_t)); req->data = (void *)cb_data; int r = uv_shutdown(req, stream, after_shutdown); - self->_socket = to$int(-1); + self->_sock = to$int(-1); + self->_sock4 = to$int(-1); + self->_sock6 = to$int(-1); if (r < 0) { // TODO: we could probably ignore most or all of these errors as the // purpose is to shutdown our side of the connection and many of these @@ -256,33 +341,60 @@ static void after_shutdown(uv_shutdown_t* req, int status) { return $R_CONT(c$cont, B_None); } - -$R netQ_TCPIPConnectionD_writeG_local (netQ_TCPIPConnection self, $Cont c$cont, B_bytes data) { - uv_stream_t *stream = (uv_stream_t *)from$int(self->_socket); - // fd == -1 means invalid FD and can happen after __resume__ - if (stream == -1) - return $R_CONT(c$cont, B_None); - - uv_write_t *req = (uv_write_t *)malloc(sizeof(uv_write_t)); - uv_buf_t buf = uv_buf_init(data->str, data->nbytes); - int r = uv_write(req, stream, &buf, 1, NULL); - if (r < 0) { - char errmsg[1024] = "Failed to write to TCP socket: "; - uv_strerror_r(r, errmsg + strlen(errmsg), sizeof(errmsg)-strlen(errmsg)); - log_warn(errmsg); - $action2 f = self->on_error; - f->$class->__asyn__(f, self, to$str(errmsg)); +$R netQ_TCPConnectionD_ip_versionG_local (netQ_TCPConnection self, $Cont c$cont) { + struct sockaddr_storage peername; + int namelen = sizeof(peername); + if (uv_tcp_getpeername((const uv_tcp_t *)from$int(self->_sock), (struct sockaddr*)&peername, &namelen) == 0) { + if (peername.ss_family == AF_INET) { + return $R_CONT(c$cont, to$int(4)); + } else if (peername.ss_family == AF_INET6) { + return $R_CONT(c$cont, to$int(6)); + } else { + log_error("Unhandled AFI"); + } + } else { + log_error("Failed to get peer name from handle %d", from$int(self->_sock)); } return $R_CONT(c$cont, B_None); } -B_NoneType netQ_TCPIPConnectionD___resume__ (netQ_TCPIPConnection self) { - self->_socket = to$int(-1); - $action2 f = self->on_error; - f->$class->__asyn__(f, self, to$str("resume")); - return B_None; +$R netQ_TCPConnectionD_local_addressG_local (netQ_TCPConnection self, $Cont c$cont) { + struct sockaddr_storage sockname; + int namelen = sizeof(sockname); + char addr[INET6_ADDRSTRLEN] = { '\0' }; + if (uv_tcp_getsockname((const uv_tcp_t *)from$int(self->_sock), (struct sockaddr*)&sockname, &namelen) == 0) { + if (sockname.ss_family == AF_INET) { + uv_ip4_name((struct sockaddr_in*)&sockname, addr, sizeof(addr)); + } else if (sockname.ss_family == AF_INET6) { + uv_ip6_name((struct sockaddr_in6*)&sockname, addr, sizeof(addr)); + } else { + log_error("Unhandled AFI"); + } + } else { + log_error("Failed to get sock name from handle %d", from$int(self->_sock)); + } + return $R_CONT(c$cont, to$str(addr)); } +$R netQ_TCPConnectionD_remote_addressG_local (netQ_TCPConnection self, $Cont c$cont) { + struct sockaddr_storage peername; + int namelen = sizeof(peername); + char addr[INET6_ADDRSTRLEN] = { '\0' }; + if (uv_tcp_getpeername((const uv_tcp_t *)from$int(self->_sock), (struct sockaddr*)&peername, &namelen) == 0) { + if (peername.ss_family == AF_INET) { + uv_ip4_name((struct sockaddr_in*)&peername, addr, sizeof(addr)); + } else if (peername.ss_family == AF_INET6) { + uv_ip6_name((struct sockaddr_in6*)&peername, addr, sizeof(addr)); + } else { + log_error("Unhandled AFI"); + } + } else { + log_error("Failed to get peer name from handle %d", from$int(self->_sock)); + } + return $R_CONT(c$cont, to$str(addr)); +} + + void on_new_connection(uv_stream_t *server, int status) { netQ_TCPListener self = (netQ_TCPListener)server->data; @@ -447,3 +559,8 @@ B_NoneType netQ_TCPListenConnectionD___resume__ (netQ_TCPListenConnection self) self->client = to$int(-1); return B_None; } + +//netQ_DNSCap netQ_TCPConnectionD__dnscap (netQ_TCPConnection self) { +// netQ_DNSCap c = netQ_DNSCapG_new(void); +// return c; +//} diff --git a/docs/acton-by-example/src/security/capabilities.md b/docs/acton-by-example/src/security/capabilities.md index 82fbcaf58..1795d99fe 100644 --- a/docs/acton-by-example/src/security/capabilities.md +++ b/docs/acton-by-example/src/security/capabilities.md @@ -4,7 +4,7 @@ Any interesting program will need to interact with the outside world, like acces In an Acton program, having a reference to an actor gives you the ability to do something with that actor. Without a reference, it is impossible to access an actor and it is not possible to forge a reference. This provides a simple and effective security model that also extends to accessing things outside of the Acton system, like files or remote hosts over the network. -Things outside of the actor world are represented by actors and to access such actors, a *capability reference* is required. For example, we can use `TCPIPConnection` to connect to a remote host over the network using TCP. The first argument is of the type `TCPConnectCap`, which is the *capability* of using a TCP socket to connect to a remote host. This is enforced by the Acton type system. Not having the correct capability reference will lead to a compilation error. +Things outside of the actor world are represented by actors and to access such actors, a *capability reference* is required. For example, we can use `TCPConnection` to connect to a remote host over the network using TCP. The first argument is of the type `TCPConnectCap`, which is the *capability* of using a TCP socket to connect to a remote host. This is enforced by the Acton type system. Not having the correct capability reference will lead to a compilation error. `TCPConnectCap` is part of a capability hierarchy, starting with the generic `WorldCap` and becoming further and further restricted: @@ -27,7 +27,7 @@ actor main(env): print("Client ERR", msg) connect_cap = net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))) - client = net.TCPIPConnection(connect_cap, env.argv[1], int(env.argv[2]), on_connect, on_receive, on_error) + client = net.TCPConnection(connect_cap, env.argv[1], int(env.argv[2]), on_connect, on_receive, on_error) ``` Capability based privilege restriction prevent some deeply nested part of a program, perhaps in a dependency to a dependency, to perform operations unknown to the application author. Access to capabilities must be explicitly handed out and a program can only perform operations based on the capabilities it has access to. diff --git a/examples/client.act b/examples/client.act index 049febc56..0a85ffff9 100644 --- a/examples/client.act +++ b/examples/client.act @@ -3,7 +3,7 @@ import net actor main(env): def on_connect(c): - print("Client established connection") + print("Client established connection to", c.remote_address(), "from", c.local_address(), "using IP version", c.ip_version()) def conn_write(s): c.write(s.encode()) @@ -14,10 +14,12 @@ actor main(env): def on_error(c, msg): print("Client ERR", msg) + # Should do periodic back-off + c.reconnect() if len(env.argv) != 3: print("usage: client [HOST] [PORT]") await async env.exit(-1) connect_cap = net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))) - client = net.TCPIPConnection(connect_cap, env.argv[1], int(env.argv[2]), on_connect, on_receive, on_error) + client = net.TCPConnection(connect_cap, env.argv[1], int(env.argv[2]), on_connect, on_receive, on_error) diff --git a/test/Makefile b/test/Makefile index 14128ba30..1ec851fee 100644 --- a/test/Makefile +++ b/test/Makefile @@ -1,5 +1,5 @@ MK_PATH:=$(shell dirname $(dir $(abspath $(lastword $(MAKEFILE_LIST))))) -ACTONC=$(MK_PATH)/dist/bin/actonc --quiet --always-build +ACTONC=$(MK_PATH)/dist/bin/actonc --quiet DDB_SERVER=../dist/bin/actondb TESTS= \ $(DDB_TESTS) diff --git a/test/rts_db/ddb_test_client.act b/test/rts_db/ddb_test_client.act index 2136150de..c8d5508cb 100644 --- a/test/rts_db/ddb_test_client.act +++ b/test/rts_db/ddb_test_client.act @@ -28,6 +28,6 @@ actor main(env): def connect(): attempts += 1 connect_cap = net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))) - client = net.TCPIPConnection(connect_cap, "127.0.0.1", port, on_connect, on_receive, on_error) + client = net.TCPConnection(connect_cap, "localhost", port, on_connect, on_receive, on_error) connect() diff --git a/test/rts_db/test_tcp_client.act b/test/rts_db/test_tcp_client.act index 46b76774e..e8f5eb36d 100755 --- a/test/rts_db/test_tcp_client.act +++ b/test/rts_db/test_tcp_client.act @@ -35,7 +35,7 @@ actor Client(connect_auth, port: int): def on_error(c, msg): print("Client ERR", msg) - client = net.TCPIPConnection(connect_auth, "127.0.0.1", port, on_connect, on_receive, on_error) + client = net.TCPConnection(connect_auth, "localhost", port, on_connect, on_receive, on_error) def write(data: bytes): client.write(data) @@ -199,7 +199,7 @@ actor Tester(env, verbose, port_chunk): paid = p.aid() psp[paid] = "app" else: - error("Got unexpected data from server in state %d: %s" % (state, data.decode())) + error("Got unexpected data from server via TCP session in state %d: %s" % (state, data.decode())) def tcpc_on_error(c, msg): error("Client ERR: %s" % (msg)) @@ -212,7 +212,7 @@ actor Tester(env, verbose, port_chunk): log("YAY, got expected data, terminating client, current p_alive: " + str(p_alive)) p.terminate() else: - error("Got unexpected data from server in state %d: %s" % (state, str(data))) + error("Got unexpected data from client over stdout in state %d: %s" % (state, str(data))) def app_client_on_exit(p, exit_code, term_signal): @@ -229,7 +229,7 @@ actor Tester(env, verbose, port_chunk): log("TCP client app exited, doing our TCP client to increase counter...") if exit_code == 0 and term_signal == 0: state = 2 - tcp_client = net.TCPIPConnection(connect_auth, "127.0.0.1", port, tcpc_on_connect, tcpc_on_receive, tcpc_on_error) + tcp_client = net.TCPConnection(connect_auth, "localhost", port, tcpc_on_connect, tcpc_on_receive, tcpc_on_error) if state == 4: if exit_code == 0 and term_signal == 0: diff --git a/test/rts_db/test_tcp_server.act b/test/rts_db/test_tcp_server.act index 859454944..06caefe43 100755 --- a/test/rts_db/test_tcp_server.act +++ b/test/rts_db/test_tcp_server.act @@ -35,7 +35,7 @@ actor Client(connect_cap, port: int): def on_error(c, msg): print("Client ERR", msg) - client = net.TCPIPConnection(connect_cap, "127.0.0.1", port, on_connect, on_receive, on_error) + client = net.TCPConnection(connect_cap, "127.0.0.1", port, on_connect, on_receive, on_error) def write(data: bytes): client.write(data) @@ -191,14 +191,14 @@ actor Tester(env, verbose, port_chunk): if data.find(b"NOW LISTENING", None, None) > -1: log("Server app listening in state %d, starting TCP client" % state) state = 1 - tcp_client = net.TCPIPConnection(connect_cap, "127.0.0.1", port, tcpc_on_connect, tcpc_on_receive, tcpc_on_error) + tcp_client = net.TCPConnection(connect_cap, "127.0.0.1", port, tcpc_on_connect, tcpc_on_receive, tcpc_on_error) else: log("In state " + str(state) + ", read unexpected output from server app:" + str(data)) elif state == 6: if data.find(b"NOW LISTENING", None, None) > -1: log("Server app listening in state %d, starting TCP client" % state) state = 7 - tcp_client = net.TCPIPConnection(connect_cap, "127.0.0.1", port, tcpc_on_connect, tcpc_on_receive, tcpc_on_error) + tcp_client = net.TCPConnection(connect_cap, "127.0.0.1", port, tcpc_on_connect, tcpc_on_receive, tcpc_on_error) else: log("In state " + str(state) + ", read unexpected output from server app:" + str(data)) diff --git a/test/stdlib_auto/test_net.act b/test/stdlib_auto/test_net.act new file mode 100644 index 000000000..7ceeeaa88 --- /dev/null +++ b/test/stdlib_auto/test_net.act @@ -0,0 +1,24 @@ +import net + +actor main(env): + for ip in ["1.2.3.4", "123.123.123.123"]: + if net.is_ipv4(ip) == False: + print(ip, "incorrectly detected as not IPv4") + await async env.exit(1) + + for notip in ["1.2.3.4.5", "256.123.123.123"]: + if net.is_ipv4(notip) == True: + print(notip, "incorrectly detected as correct IPv4") + await async env.exit(1) + + for ip in ["2001:db8::1", "2001:DB8::1"]: + if net.is_ipv6(ip) == False: + print(ip, "incorrectly detected as not IPv6") + await async env.exit(1) + + for notip in ["2001:db8::123456", "2001:db8:::1"]: + if net.is_ipv6(notip) == True: + print(notip, "incorrectly detected as correct IPv6") + await async env.exit(1) + + await async env.exit(0) diff --git a/test/stdlib_auto/test_net_tcp.act b/test/stdlib_auto/test_net_tcp.act index 779d7db5c..3ef132c4c 100644 --- a/test/stdlib_auto/test_net_tcp.act +++ b/test/stdlib_auto/test_net_tcp.act @@ -85,7 +85,7 @@ actor Client(rts_monitor, env: Env, port: int): await async env.exit(0) connect_cap = net.TCPConnectCap(net.TCPCap(net.NetCap(env.cap))) - client = net.TCPIPConnection(connect_cap, "127.0.0.1", port, on_connect, on_receive, on_error) + client = net.TCPConnection(connect_cap, "localhost", port, on_connect, on_receive, on_error) actor main(env):