diff --git a/compiler/src/types.ml b/compiler/src/types.ml index b7caf8c..621834a 100644 --- a/compiler/src/types.ml +++ b/compiler/src/types.ml @@ -364,6 +364,7 @@ let stdlib_members : stdlib_member list = m "net" "send_fd" 2 105 (Some (TScalar "Bool")) None; m "net" "recv_fd" 1 106 (Some (TNullable (TScalar "Int"))) None; m "net" "connect_unix" 1 107 (Some (TScalar "Int")) None; + m "net" "connect" 2 110 (Some (TScalar "Int")) None; (* runtime-v2 6: resize's read twin (nil = not a tty), and a codepoint's terminal cell width (libc wcwidth under C.UTF-8) *) m "term" "size" 1 108 (Some (TNullable (TScalar termsize_record_name))) (Some termsize_record_name); diff --git a/runtime/src/builtin.c b/runtime/src/builtin.c index ed6fbc3..04ced17 100644 --- a/runtime/src/builtin.c +++ b/runtime/src/builtin.c @@ -172,7 +172,7 @@ int wo_builtin(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) { if (C == WO_B_JSON_ENCODE || C == WO_B_JSON_DECODE) return wo_builtin_json(vm, R, ins, msg); if ((C >= WO_B_SYS_FIRST && C <= WO_B_PROC_RUN) || C == WO_B_TIME_TICKS - || (C >= WO_B_NET_READ_DL && C <= WO_B_TERM_WIDTH)) + || (C >= WO_B_NET_READ_DL && C <= WO_B_NET_CONNECT)) return wo_builtin_sys(vm, R, ins, msg); if (C >= WO_B_SHA1 && C <= WO_B_HMAC_SHA256) return wo_builtin_crypto(vm, R, ins, msg); diff --git a/runtime/src/loader.c b/runtime/src/loader.c index 28b2605..6a6c88a 100644 --- a/runtime/src/loader.c +++ b/runtime/src/loader.c @@ -78,7 +78,7 @@ static const uint8_t b_arity[WO_B_MAX + 1] = { [WO_B_PROC_SPAWN_PTY] = 5, [WO_B_PROC_RESIZE] = 3, [WO_B_SIGNAL_ON] = 3, [WO_B_TERM_RAW] = 1, [WO_B_TERM_RESTORE] = 1, [WO_B_NET_SEND_FD] = 2, [WO_B_NET_RECV_FD] = 1, [WO_B_NET_CONNECT_UNIX] = 1, - [WO_B_TERM_SIZE] = 2, [WO_B_TERM_WIDTH] = 1, + [WO_B_TERM_SIZE] = 2, [WO_B_TERM_WIDTH] = 1, [WO_B_NET_CONNECT] = 2, /* json (json.c): encode takes the value's static kind, decode the class id to build */ [WO_B_JSON_ENCODE] = 2, [WO_B_JSON_DECODE] = 2, [WO_B_MAP_GET_OPT] = 2, diff --git a/runtime/src/sysio.c b/runtime/src/sysio.c index fd581e7..1673fb6 100644 --- a/runtime/src/sysio.c +++ b/runtime/src/sysio.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -1552,6 +1553,59 @@ int wo_builtin_sys(wo_vm *vm, uint64_t *R, uint32_t ins, const char **msg) { R[A] = (uint64_t)fd; return 0; } + case WO_B_NET_CONNECT: { /* (host, port) -> Int: outbound TCP client fd. + * DNS via getaddrinfo (v4 or v6), blocking + * connect with the same EINTR/stop handling as + * WO_B_NET_CONNECT_UNIX, then nonblocking for the + * plane. A _dl deadline/park variant is the next + * slice; this one can stall the shard during the + * handshake, tolerable while connect is rare. */ + if (cstr_of(R[B], path, sizeof path, msg)) return WO_T_BOUNDS; + int64_t port = (int64_t)R[B + 1]; + if (port < 0 || port > 65535) { + *msg = "port out of range"; + return WO_T_BOUNDS; + } + char portstr[8]; + snprintf(portstr, sizeof portstr, "%u", (unsigned)port); + struct addrinfo hints; + struct addrinfo *res = NULL; + struct addrinfo *ai; + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + int grc = getaddrinfo(path, portstr, &hints, &res); + if (grc != 0) { + *msg = gai_strerror(grc); + return WO_T_IO; + } + int fd = -1; + for (ai = res; ai; ai = ai->ai_next) { + fd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (fd < 0) continue; + int rc; + for (;;) { + rc = connect(fd, ai->ai_addr, ai->ai_addrlen); + if (rc == 0 || errno != EINTR) break; + if (stop_pending()) { + close(fd); + freeaddrinfo(res); + return WO_SYS_STOPPED; + } + } + if (rc == 0) break; + close(fd); + fd = -1; + } + freeaddrinfo(res); + if (fd < 0) { + *msg = strerror(errno); + return WO_T_IO; + } + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) | O_NONBLOCK); + R[A] = (uint64_t)fd; + return 0; + } case WO_B_NET_SEND_FD: { /* (conn, fd) -> Bool: SCM_RIGHTS, one fd */ int conn = (int)(int64_t)R[B]; int pass = (int)(int64_t)R[B + 1]; diff --git a/runtime/src/wob.h b/runtime/src/wob.h index 8325c15..3d6f176 100644 --- a/runtime/src/wob.h +++ b/runtime/src/wob.h @@ -547,9 +547,14 @@ enum { WO_B_TERM_WIDTH = 109, /* (codepoint) -> Int cell width via libc * wcwidth under C.UTF-8: -1 control, * 0 combining, 1, or 2 */ + /* ---- language 38 / jarvis: the outbound-socket client side. TCP + * connect with DNS via getaddrinfo; blocking connect then nonblocking + * for the plane, mirroring WO_B_NET_CONNECT_UNIX. A _dl deadline/park + * variant is the next slice. Underneath runtime-v2 9's outbound TLS. */ + WO_B_NET_CONNECT = 110, /* (host, port) -> Int: outbound TCP client fd */ }; -#define WO_B_MAX 109u +#define WO_B_MAX 110u /* ids at or above this one live in sysio.c, not builtin.c */ #define WO_B_SYS_FIRST WO_B_FS_EXISTS