<> <> <> <> <> <> <> DIRECTORY Arpa, ArpaUDP, Basics, DatagramSocket, UnixErrno, UnixNetInet, UnixSysCallExtensions, UnixSysCalls, UnixTypes ; DatagramSocketImpl: CEDAR PROGRAM IMPORTS UnixErrno, UnixSysCalls, UnixSysCallExtensions EXPORTS DatagramSocket ~ { <> maxDatagramBytes: CARD ¬ 8*1024 + 512; <> CHARPtr: TYPE ~ UnixTypes.CHARPtr; FileDescriptor: TYPE ~ UnixTypes.FileDescriptor; RES: TYPE ~ UnixTypes.SysCallResult; Handle: TYPE ~ REF Object; Object: PUBLIC TYPE ~ RECORD [ onlySocket: FileDescriptor ]; <> Create: PUBLIC PROC [localPort: ArpaUDP.Port ¬ ArpaUDP.nullPort] RETURNS [Handle] ~ { sIn: FileDescriptor; res: RES; sockAddr: UnixNetInet.SockAddrIn ¬ []; <> sockAddr.port ¬ ( SELECT localPort FROM ArpaUDP.nullPort => UnixNetInet.unspecifiedPort, ENDCASE => localPort ); sIn ¬ UnixSysCalls.Socket[UnixTypes.AddressFamily.inet, UnixTypes.SocketType.dgram, UnixTypes.ProtocolFamily.unspec]; IF sIn = error THEN RaiseError[]; TRUSTED { res ¬ UnixSysCalls.Bind[sIn, LOOPHOLE[@sockAddr], BYTES[UnixNetInet.SockAddrIn]] }; IF res # success THEN { savedErrno: UnixErrno.Errno ¬ UnixErrno.GetErrno[]; [] ¬ UnixSysCalls.Close[sIn]; RaiseError[savedErrno]; }; <> IF sIn = error THEN { savedErrno: UnixErrno.Errno ¬ UnixErrno.GetErrno[]; [] ¬ UnixSysCalls.Close[sIn]; RaiseError[savedErrno]; }; RETURN[ NEW[Object ¬ [sIn]] ]; }; GetLocal: PUBLIC PROC [h: Handle] RETURNS [address: Arpa.Address, port: ArpaUDP.Port] ~ { res: RES; sockAddr: UnixNetInet.SockAddrIn; sockAddrLen: INT ¬ BYTES[UnixNetInet.SockAddrIn]; TRUSTED { res ¬ UnixSysCalls.GetSockName[h.onlySocket, LOOPHOLE[@sockAddr], @sockAddrLen] }; IF res # success THEN RaiseError[]; RETURN [LOOPHOLE[sockAddr.addr], sockAddr.port]; }; GetMaxDatagramSize: PUBLIC PROC [h: Handle] RETURNS [maxBytes: CARD] ~ { maxBytes ¬ maxDatagramBytes; }; Destroy: PUBLIC PROC [h: Handle] ~ { IF h.onlySocket # error THEN { [] ¬ UnixSysCalls.Close[h.onlySocket]; h.onlySocket ¬ error }; }; Send: PUBLIC PROC [h: Handle, toAddress: Arpa.Address, toPort: ArpaUDP.Port, b: REF TEXT, startIndex: NAT ¬ 0, count: NAT ¬ NAT.LAST] ~ { ans: INT; sockAddr: UnixNetInet.SockAddrIn; IF startIndex > b.length THEN ERROR Error[$datagramTooShort]; count ¬ MIN[count, (b.length - startIndex)]; sockAddr.addr ¬ LOOPHOLE[toAddress]; sockAddr.port ¬ toPort; TRUSTED { ans ¬ UnixSysCalls.SendTo[ s~h.onlySocket, msg~LOOPHOLE[LOOPHOLE[b, CHARPtr]+UNITS[TEXT[0]]+startIndex], len~count, flags~[], to~LOOPHOLE[@sockAddr], toLen~BYTES[UnixNetInet.SockAddrIn] ]; }; IF ans # count THEN RaiseError[]; }; UnsafeSend: PUBLIC UNSAFE PROC [h: Handle, toAddress: Arpa.Address, toPort: ArpaUDP.Port, b: Basics.UnsafeBlock] ~ { ans: INT; sockAddr: UnixNetInet.SockAddrIn; sockAddr.addr ¬ LOOPHOLE[toAddress]; sockAddr.port ¬ toPort; TRUSTED { ans ¬ UnixSysCalls.SendTo[ s~h.onlySocket, msg~LOOPHOLE[LOOPHOLE[b.base, CHARPtr]+b.startIndex], len~b.count, flags~[], to~LOOPHOLE[@sockAddr], toLen~BYTES[UnixNetInet.SockAddrIn] ]; }; IF ans # b.count THEN RaiseError[]; }; Recv: PUBLIC PROC [h: Handle, b: REF TEXT, startIndex: NAT ¬ 0, count: NAT ¬ NAT.LAST, timeoutMsec: CARD ¬ UnixSysCallExtensions.waitForever] RETURNS [nBytesRead: NAT, fromAddress: Arpa.Address, fromPort: ArpaUDP.Port] ~ { ans: INT; res: RES; sockAddr: UnixNetInet.SockAddrIn; addrLen: INT ¬ BYTES[UnixNetInet.SockAddrIn]; IF startIndex > b.maxLength THEN ERROR Error[$invalidBuffer]; count ¬ MIN[count, (b.maxLength - startIndex)]; res ¬ UnixSysCallExtensions.SetGetTimeout[h.onlySocket, timeoutMsec]; IF res # success THEN RaiseError[]; TRUSTED { ans ¬ UnixSysCalls.RecvFrom[ fd~h.onlySocket, buf~LOOPHOLE[LOOPHOLE[b, CHARPtr]+UNITS[TEXT[0]]+startIndex], len~count, flags~[], from~LOOPHOLE[@sockAddr], fromLen~@addrLen ]; }; IF ans < 0 THEN RaiseError[]; b.length ¬ startIndex + ans; RETURN [ans, LOOPHOLE[sockAddr.addr], sockAddr.port]; }; UnsafeRecv: PUBLIC UNSAFE PROC [h: Handle, b: Basics.UnsafeBlock, timeoutMsec: CARD ¬ UnixSysCallExtensions.waitForever] RETURNS [nBytesRead: NAT, fromAddress: Arpa.Address, fromPort: ArpaUDP.Port] ~ { ans: INT; res: RES; sockAddr: UnixNetInet.SockAddrIn; addrLen: INT ¬ BYTES[UnixNetInet.SockAddrIn]; res ¬ UnixSysCallExtensions.SetGetTimeout[h.onlySocket, timeoutMsec]; IF res # success THEN RaiseError[]; TRUSTED { ans ¬ UnixSysCalls.RecvFrom[ fd~h.onlySocket, buf~LOOPHOLE[LOOPHOLE[b.base, CHARPtr]+b.startIndex], len~b.count, flags~[], from~LOOPHOLE[@sockAddr], fromLen~@addrLen ]; }; IF ans < 0 THEN RaiseError[]; b.count ¬ ans; RETURN [ans, LOOPHOLE[sockAddr.addr], sockAddr.port]; }; Kick: PUBLIC PROC [h: Handle] ~ { NULL }; <> Error: PUBLIC ERROR [code: ATOM] ~ CODE; <> RaiseError: PROC [errno: UnixErrno.Errno ¬ VAL[CARD.LAST]] ~ { code: ATOM; IF errno = VAL[CARD.LAST] THEN errno ¬ UnixErrno.GetErrno[]; code ¬ SELECT errno FROM EIO, ENFILE, EMFILE => $transientError, EDESTADDRREQ, ENETDOWN, ENETUNREACH, EHOSTDOWN, EHOSTUNREACH => $unreachable, ETIMEDOUT => $timeout, EMSGSIZE => $datagramTooLong, ENDCASE => $failure; ERROR Error[code]; }; }.