// SPDX-License-Identifier: GPL-3.0-only // Copyright (c) 2022, Sylvain Huet, Ambermind // Minimacy (r) System struct TcpSrv=Stream+[maxConnectionsS, idleTimeoutTS, evictionTimeoutS, connectionsS];; struct Tcp=Stream+[remoteAddrT, remotePortT, incomingT];; struct Udp=Stream+[remoteAddrU, remotePortU, localAddrU, localPortU];; var _UdpCreateFunction=#udpCreateNative;; var _TcpOpenFunction=#tcpOpenNative;; var _TcpSrvCreateFunction=#tcpSrvCreateNative;; fun ipStackOverwriteUdp(fUdpCreate) = set _UdpCreateFunction = fUdpCreate;; fun ipStackOverwriteTcp(fTcpOpen) = set _TcpOpenFunction = fTcpOpen;; fun ipStackOverwriteTcpSrv(fTcpSrv) = set _TcpSrvCreateFunction = fTcpSrv;; fun strFromMac(mac) = if mac<>nil then strJoin(":", arrayInit(6, lambda(i) = hexFromIntN(2, strGet(mac, i))));; fun strFromIp(ip) = strFormat("*.*.*.*", strGet(ip, 0), strGet(ip, 1), strGet(ip, 2), strGet(ip, 3));; fun ipFromStr(str) = if !strEmpty(str) then strListConcat(listMap(strSplit(".", str), lambda(val)= strInt8(intFromStr(val))));; fun macFromStr(str) = if !strEmpty(str) then strFromHex(strReplace(str, ":", ""));; //-------------- Bsd TCP server fun tcpSrvClose(tcpSrv)= streamClose(TcpSrv socket in if socket<>nil then let [TcpSrv] -> tcpSrv in streamInit(tcpSrv, socket, "TCPsrv", strFormat("*:*", addr, port), lambda()= // onSelectRead let _tcpAccept(socket) -> [socket, addr, port] in let _tcpCreate(socket, addr, port, true) -> tcp in streamAcceptEvent(tcpSrv, Stream=s.maxConnectionsS then ( set s.connectionsS = listFilter(s.connectionsS, lambda(tcp) = !streamTerminated(tcp)); if listLength(s.connectionsS)>=s.maxConnectionsS then ( if s.evictionTimeoutS<>nil then set s.connectionsS = listFilter(s.connectionsS, lambda(tcp) = if (streamLastTouch(tcp) + s.evictionTimeoutS - time()) >0 then true else void streamAbort(tcp) ); if listLength(s.connectionsS)>=s.maxConnectionsS then ( if _DEBUG then echoLn strFormat("> TcpSrvExt: reject *", streamDetail(tcp)); return void streamClose(tcp) ) ) ); set s.connectionsS= tcp::s.connectionsS; true );; fun tcpSrvSetIdleTimeout(tcpSrv, idleTimeout)= set tcpSrv.idleTimeoutTS = idleTimeout; tcpSrv;; fun tcpSrvSetMaxConnections(tcpSrv, maxConnections, evictionTimeout)= set tcpSrv.evictionTimeoutS = evictionTimeout; set tcpSrv.maxConnectionsS = maxConnections; tcpSrv;; fun tcpSrvOnAccept(tcpSrv, onAccept)= streamOnAccept(tcpSrv, lambda(stream)= if _DEBUG then echoLn strFormat("> TcpSrvExt: incoming *", streamDetail(stream)); if _DEBUG then echoLn strFormat("> TcpSrvExt: open connections: *", listLength(tcpSrv.connectionsS)); if _tcpSrvAddConnection(tcpSrv, stream) then ( streamSetIdleTimeout(stream, tcpSrv.idleTimeoutTS); if _DEBUG then echoLn strFormat("> TcpSrvExt: accept *", streamDetail(stream)); void threadStart("tcpIn",lambda()= call onAccept(stream)) ) );; //-------------- Bsd TCP fun tcpClose(tcp)= streamClose(Tcp tcp in streamInit(tcp, socket, if tcp.incomingT then "TCPin" else "TCPout", strFormat("*:*", addr, port), lambda()= // onSelectRead let _tcpRead(socket) -> data in ( streamReadEvent(tcp, data); if data==nil then void tcpClose(tcp); ) , lambda(data, start) = _tcpWrite(socket, data, start) , // write lambda()= _socketClose(socket); streamCommonClose(tcp) // close );; // show fun tcpOnEvent(tcp, onRead, onWrite)= streamOnEvent(Tcp socket in if socket<>nil then _tcpCreate(socket, addr, port, false);; fun tcpOpen(addr, port) = call _TcpOpenFunction(addr, port);; fun tcpWrite(tcp, data, start)= streamWrite(Tcp tcp;; //-------------- Bsd UDP fun udpClose(udp)= streamClose(Udp socket in if socket<>nil then let [ localAddrU=localAddr, localPortU=localPort, remoteAddrU=remoteAddr, remotePortU=remotePort ] -> udp in streamInit(udp, socket, "UDP", strFormat("*:*", localAddr, localPort), lambda ()= // onSelectRead let _udpRead(socket) -> [data, ip, port] in if port<>nil then ( set udp.remoteAddrU=ip; set udp.remotePortU=port; streamReadEvent(udp, data) ) , lambda(data, start) = // write _udpSend(socket, udp.remoteAddrU, udp.remotePortU, strTail(data, start)) , lambda ()= _socketClose(socket); streamCommonClose(udp));; // close fun udpCreate(remoteAddr, remotePort, localAddr, localPort)= call _UdpCreateFunction(remoteAddr, remotePort, localAddr, localPort);; fun udpSend(udp, msg)= streamWrite(Udp Bool), loI];; // macI is 6 bytes // ipI, submaskI, gatewayI, dnsI are 4 bytes // ipInt, submaskIntI are integers const AF_INET=2;; var _NetList;; fun netList ()= _NetList;; fun netDefault ()= head(_NetList);; fun netAdd(net) = set _NetList=(Networknil;; fun netSetup(net, name, mac, mtu) = set net.nameI= name; set net.macI= macFromStr(mac); set net.mtuI=mtu; net;; fun netUpdateAddr(net, ip, submask, gateway, dns)= set net.ipI=ipFromStr(ip); set net.submaskI=ipFromStr(submask); set net.gatewayI=ipFromStr(gateway); set net.dnsI=ipFromStr(dns); set net.ipIntI =strRead32Msb(net.ipI, 0); set net.submaskIntI =strRead32Msb(net.submaskI, 0); net;; fun netOnActivate(net, fActivate) = set net.fActivateI=fActivate;; fun netActivate(net)= if !netIsActive(net) then call net.fActivateI(); net;; fun _nativeNetworksInit ()= // declare native Ethernet Interfaces if any let netSetup([loI=true], "lo", nil, nil) -> net in ( netOnActivate(net, (lambda ()= void set net._socketS= socketEmpty())); netUpdateAddr(net, "127.0.0.1", "255.0.0.0", nil, nil); netAdd(net) ); for [index, name, family, mac, mtu, ip, submask] in _ethList() do if mac<>"\0\0\0\0\0\0" then let netSetup([Network], name, mac, mtu) -> net in ( netOnActivate(net, lambda ()= let _ethCreate(index) -> socket in if socket<>nil then streamInit(net, socket, "ethernet", net.nameI, lambda ()= // onSelectRead let _ethRead(socket) -> data in streamReadEvent(net, data) , lambda(data, start) = _ethSend(socket, index, strTail(data, start)), // write lambda ()= streamCommonClose(net) // close ) ); netUpdateAddr(net, ip, submask, nil, nil); netAdd(net) );;