1# net - TCP and UDP sockets, in the shape of Go's net package.2#3# Everything goes through syscall, there is no dependency on the C library of4# the host. Addresses are written "host:port", an empty host meaning every5# interface.67syscall = import("syscall")8strconv = import("strconv")910bufsize = 40961112# JoinHostPort builds an address out of a host and a port.13JoinHostPort = fn(host, port) { "{host}:{string(port)}" }1415# SplitHostPort returns an object with Host and Port, or an error if the16# address carries no port. The last colon wins, so IPv6 forms survive.17SplitHostPort = fn(hostport) {18 colon = -119 for i = len(hostport) - 1; i >= 0; --i {20 if hostport[i] == ":" {21 colon = i22 break23 }24 }2526 if colon == -1 {27 return error("net: missing port in address \"{hostport}\"")28 }2930 if failed(port = strconv.Atoi(slice(hostport, colon + 1, len(hostport)))) {31 return error("net: invalid port in address \"{hostport}\"")32 }3334 out = new()35 out.Host = slice(hostport, 0, colon)36 out.Port = port37 return out38}3940# sockaddr turns an address into the bytes bind and connect want.41sockaddr = fn(address) {42 if failed(hp = SplitHostPort(address)) {43 return hp44 }45 return syscall.SockaddrIn(hp.Host, hp.Port)46}4748# newConn wraps an open socket.49newConn = fn(fd, raddr) {50 conn = new()51 conn.fd = fd52 conn.RemoteAddr = raddr5354 # Read returns up to n bytes, or an empty bytes value when the other end55 # has closed the connection.56 conn.Read = fn(n) {57 if n == null {58 n = bufsize59 }6061 buf = bytes(n)62 if failed(read = syscall.Recv(conn.fd, buf, n, 0)) {63 return read64 }65 return slice(buf, 0, read)66 }6768 # Write sends a string or bytes and returns how many bytes went out.69 conn.Write = fn(data) {70 if type(data) != "bytes" {71 if failed(data = bytes(string(data))) {72 return data73 }74 }75 return syscall.Send(conn.fd, data, len(data), 0)76 }7778 conn.Close = fn() { syscall.Close(conn.fd) }7980 # SetTimeout gives up on a read or a write after ms milliseconds.81 conn.SetTimeout = fn(ms) {82 tv = syscall.Timeval(ms)83 syscall.Setsockopt(conn.fd, syscall.SOL_SOCKET, syscall.SO_RCVTIMEO, tv, len(tv))84 return syscall.Setsockopt(conn.fd, syscall.SOL_SOCKET, syscall.SO_SNDTIMEO, tv, len(tv))85 }8687 return conn88}8990# Dial connects to an address. Network is "tcp" or "udp".91Dial = fn(network, address) {92 socktype = if network == "udp" { syscall.SOCK_DGRAM } else { syscall.SOCK_STREAM }9394 if failed(sa = sockaddr(address)) {95 return sa96 }97 if failed(fd = syscall.Socket(syscall.AF_INET, socktype, 0)) {98 return fd99 }100 if failed(err = syscall.Connect(fd, sa, len(sa))) {101 syscall.Close(fd)102 return err103 }104105 return newConn(fd, address)106}107108# Listen returns a listener accepting connections on address.109Listen = fn(network, address) {110 if failed(sa = sockaddr(address)) {111 return sa112 }113 if failed(fd = syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, 0)) {114 return fd115 }116117 # Without this a restarted server can't bind again until the kernel118 # releases the port.119 one = syscall.Int32(1)120 syscall.Setsockopt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, one, len(one))121122 if failed(err = syscall.Bind(fd, sa, len(sa))) {123 syscall.Close(fd)124 return err125 }126 if failed(err = syscall.Listen(fd, syscall.SOMAXCONN)) {127 syscall.Close(fd)128 return err129 }130131 ln = new()132 ln.fd = fd133134 # The address the kernel actually bound: with a port of 0 it picks a free135 # one, and the caller learns which through ln.Addr. Falls back to what was136 # asked for if the lookup fails.137 name = bytes(syscall.SockaddrInSize())138 namelen = syscall.Int32(syscall.SockaddrInSize())139 if failed(syscall.Getsockname(fd, name, namelen)) {140 ln.Addr = address141 } else {142 ln.Addr = JoinHostPort(syscall.SockaddrIP(name), syscall.SockaddrPort(name))143 }144145 # Accept waits for the next connection.146 ln.Accept = fn() {147 peer = bytes(syscall.SockaddrInSize())148 size = syscall.Int32(syscall.SockaddrInSize())149150 if failed(cfd = syscall.Accept(ln.fd, peer, size)) {151 return cfd152 }153 return newConn(cfd, JoinHostPort(syscall.SockaddrIP(peer), syscall.SockaddrPort(peer)))154 }155156 ln.Close = fn() { syscall.Close(ln.fd) }157158 return ln159}160161# ListenPacket returns a UDP socket bound to address.162ListenPacket = fn(network, address) {163 if failed(sa = sockaddr(address)) {164 return sa165 }166 if failed(fd = syscall.Socket(syscall.AF_INET, syscall.SOCK_DGRAM, 0)) {167 return fd168 }169 if failed(err = syscall.Bind(fd, sa, len(sa))) {170 syscall.Close(fd)171 return err172 }173174 pc = new()175 pc.fd = fd176 pc.Addr = address177178 # ReadFrom returns an object with Data and Addr, the sender of the packet.179 pc.ReadFrom = fn(n) {180 if n == null {181 n = bufsize182 }183184 buf = bytes(n)185 from = bytes(syscall.SockaddrInSize())186 size = syscall.Int32(syscall.SockaddrInSize())187188 if failed(read = syscall.Recvfrom(pc.fd, buf, n, 0, from, size)) {189 return read190 }191192 out = new()193 out.Data = slice(buf, 0, read)194 out.Addr = JoinHostPort(syscall.SockaddrIP(from), syscall.SockaddrPort(from))195 return out196 }197198 # WriteTo sends a packet to address.199 pc.WriteTo = fn(data, address) {200 if type(data) != "bytes" {201 if failed(data = bytes(string(data))) {202 return data203 }204 }205 if failed(sa = sockaddr(address)) {206 return sa207 }208 return syscall.Sendto(pc.fd, data, len(data), 0, sa, len(sa))209 }210211 pc.Close = fn() { syscall.Close(pc.fd) }212213 return pc214}215216# ParseIP reports whether s is a valid dotted IPv4 address, returning it.217ParseIP = fn(s) {218 parts = 0219 start = 0220 i = 0221222 for i <= len(s) {223 if i == len(s) || s[i] == "." {224 if failed(n = strconv.Atoi(slice(s, start, i))) {225 return error("net: invalid IP address \"{s}\"")226 }227 if n < 0 || n > 255 {228 return error("net: invalid IP address \"{s}\"")229 }230 ++parts231 start = i + 1232 }233 ++i234 }235236 if parts != 4 {237 return error("net: invalid IP address \"{s}\"")238 }239 return s240}241242# LookupPort returns the port of a well known service, or the number itself.243LookupPort = fn(network, service) {244 ports = {245 "http": 80,246 "https": 443,247 "ftp": 21,248 "ssh": 22,249 "smtp": 25,250 "dns": 53251 }252253 if ports[service] != null {254 return ports[service]255 }256 return strconv.Atoi(service)257}