1 # Byte-exact TCP, UDP, and TLS sockets. Source: https://github.com/paymog/bend-kit/tree/main/wire 2 import Base 3 4 # Socket IO on octets: one Char per byte (0..255). Base's TCP/UDP effects 5 # decode UTF-8, which breaks byte counts and binary protocols. 6 # ms arguments are deadlines in milliseconds (0: none); a miss is ETIMEDOUT. 7 # import ./wire.bend as Wire 8 9 # TCP connect to a numeric IPv4 or IPv6 address, with a deadline. 10 def connect(host: String, port: U32, ms: U32) -> 11 IO(Result<&1, &1, U32 & String, Socket>): 12 import "./effs/wire.c" 13 import "./effs/wire.js" 14 15 def recv(sock: Socket, max: U32, ms: U32) -> 16 IO(Socket & Result<&1, &1, U32 & String, String>): 17 import "./effs/wire.c" 18 import "./effs/wire.js" 19 20 def send(sock: Socket, data: String) -> 21 IO(Socket & Result<&1, &1, U32 & String, Unit>): 22 import "./effs/wire.c" 23 import "./effs/wire.js" 24 25 def recv_from(sock: Socket, max: U32, ms: U32) -> 26 IO(Socket & Result<&1, &1, U32 & String, String & U32 & String>): 27 import "./effs/wire.c" 28 import "./effs/wire.js" 29 30 def send_to(sock: Socket, host: String, port: U32, data: String) -> 31 IO(Socket & Result<&1, &1, U32 & String, Unit>): 32 import "./effs/wire.c" 33 import "./effs/wire.js" 34 35 # TLS over a connected TCP socket (OpenSSL 3, loaded at run time; BEND_LIBSSL 36 # overrides its path). The certificate chain and host name are always checked. 37 def tls.connect(sock: Socket, host: String, ms: U32) -> 38 IO(Socket & Result<&1, &1, U32 & String, Unit>): 39 import "./effs/wire.c" 40 import "./effs/wire.js" 41 42 # Use a PEM certificate chain and its private key for mutual TLS. 43 # A load failure is EINVAL; a handshake or server verification failure is EPROTO. 44 def tls.connect.cert(sock: Socket, host: String, ms: U32, cert_path: String, key_path: String) -> 45 IO(Socket & Result<&1, &1, U32 & String, Unit>): 46 import "./effs/wire.c" 47 import "./effs/wire.js" 48 49 # protos is a comma-separated ALPN list (for example "h2,http/1.1"). 50 def tls.connect.alpn(sock: Socket, host: String, ms: U32, protos: String) -> 51 IO(Socket & Result<&1, &1, U32 & String, String>): 52 import "./effs/wire.c" 53 import "./effs/wire.js" 54 55 def tls.send(sock: Socket, data: String) -> 56 IO(Socket & Result<&1, &1, U32 & String, Unit>): 57 import "./effs/wire.c" 58 import "./effs/wire.js" 59 60 # "" once the peer sends close_notify. A bare EOF is an error. 61 def tls.recv(sock: Socket, max: U32, ms: U32) -> 62 IO(Socket & Result<&1, &1, U32 & String, String>): 63 import "./effs/wire.c" 64 import "./effs/wire.js" 65 66 # Ends the TLS session and closes the socket. 67 def tls.close(sock: Socket) -> IO(Unit): 68 import "./effs/wire.c" 69 import "./effs/wire.js" 70 71 # The .words forms move (len, words): bytes packed four to a U32, laid out as 72 # bytes/bytes.bend packs them, so no String cell is made per byte. 73 def recv.words(sock: Socket, max: U32, ms: U32) -> 74 IO(Socket & Result<&1, &1, U32 & String, U32 & Array>): 75 import "./effs/wire.c" 76 import "./effs/wire.js" 77 78 # len past the end of words fails with EINVAL. 79 def send.words(sock: Socket, len: U32, words: Array) -> 80 IO(Socket & Result<&1, &1, U32 & String, Unit>): 81 import "./effs/wire.c" 82 import "./effs/wire.js" 83 84 # len 0 once the peer sends close_notify. 85 def tls.recv.words(sock: Socket, max: U32, ms: U32) -> 86 IO(Socket & Result<&1, &1, U32 & String, U32 & Array>): 87 import "./effs/wire.c" 88 import "./effs/wire.js" 89 90 def tls.send.words(sock: Socket, len: U32, words: Array) -> 91 IO(Socket & Result<&1, &1, U32 & String, Unit>): 92 import "./effs/wire.c" 93 import "./effs/wire.js"