2
0

Unix.TCP.Mod 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. (* Aos, Copyright 2001, Pieter Muller, ETH Zurich *)
  2. MODULE TCP; (** AUTHOR "pjm, mvt, G.F."; PURPOSE "TCP protocol"; *)
  3. IMPORT Out := KernelLog, IP, Streams, Unix, Sockets, Objects;
  4. CONST
  5. NilPort* = 0;
  6. (** Error codes *)
  7. Ok* = 0;
  8. ConnectionRefused* = 3701;
  9. ConnectionReset* = 3702;
  10. WrongInterface* = 3703;
  11. TimedOut* = 3704;
  12. NotConnected* = 3705;
  13. NoInterface* = 3706;
  14. InterfaceClosed* = 3707;
  15. (** TCP connection states *)
  16. NumStates* = 4;
  17. Closed* = 0;
  18. Listen* = 1;
  19. Established* = 2;
  20. Unused* = 4; (* no real state, only used in this implementation *)
  21. OpenStates* = {Listen, Established};
  22. ClosedStates* = {Unused, Closed};
  23. HalfClosedStates* = ClosedStates + {};
  24. FinStates* = {Unused, Closed};
  25. VAR
  26. trace: BOOLEAN;
  27. TYPE
  28. (** Connection object.
  29. NOTE: Only one process should access a Connection! *)
  30. Connection* = OBJECT (Streams.Connection)
  31. VAR
  32. int- : IP.Interface; (*! Unix port: dummy, only 'int.localAdr' contains valid data *)
  33. lport- : LONGINT;
  34. fip- : IP.Adr; (* foreign protocol address *)
  35. fport- : LONGINT;
  36. state* : SHORTINT; (* TCP state *)
  37. socket : LONGINT; localAdr, foreignAdr: Sockets.SocketAdr;
  38. (* the next variables are for interface compatibility only *)
  39. irs- : LONGINT; (* initial receive sequence number *)
  40. rcvnxt- : LONGINT; (* receive next *)
  41. iss- : LONGINT; (* initial send sequence number *)
  42. sndnxt- : LONGINT; (* send next *)
  43. PROCEDURE & Init*;
  44. BEGIN
  45. state := Unused;
  46. irs := 0; iss := 0; rcvnxt := 0; sndnxt := 0
  47. END Init;
  48. (** Open a TCP connection (only use once per Connection instance).
  49. Use TCP.NilPort for lport to automatically assign an unused local port.*)
  50. PROCEDURE Open*( lport: LONGINT; fip: IP.Adr; fport: LONGINT; VAR res: WORD );
  51. VAR ignore: BOOLEAN;
  52. BEGIN {EXCLUSIVE}
  53. ASSERT( (state = Unused) & (lport >= 0) & (lport < 10000H) & (fport >= 0) & (fport < 10000H) );
  54. IF trace THEN Out.String( "Open connection " ) END;
  55. socket := Sockets.Socket( Unix.AFINET, Unix.SockStream, Unix.IpProtoTCP );
  56. IF socket # 0 THEN
  57. IF (~IP.IsNilAdr( fip )) & (fport # NilPort) THEN
  58. IF trace THEN Out.String( "(inout) " ) END;
  59. (* active open (connect) *)
  60. foreignAdr := Sockets.NewSocketAdr( fip, fport );
  61. IF Sockets.Connect( socket, foreignAdr ) THEN
  62. ignore := Sockets.SetLinger( socket );
  63. SELF.fip := fip; SELF.fport := fport;
  64. localAdr := Sockets.GetSockName( socket );
  65. SELF.lport := Sockets.GetPortNumber( localAdr );
  66. state := Established; res := Ok
  67. ELSE
  68. Out.String( "connect failed" ); Out.Ln;
  69. Sockets.Close( socket ); res := ConnectionRefused
  70. END
  71. ELSE
  72. IF trace THEN Out.String( "(listen) " ) END;
  73. (* passive open (listen) *)
  74. ASSERT( (fport = NilPort) & (IP.IsNilAdr( fip )) );
  75. localAdr := Sockets.NewSocketAdr( IP.NilAdr, lport );
  76. IF Sockets.Bind( socket, localAdr ) THEN
  77. localAdr := Sockets.GetSockName( socket );
  78. SELF.lport := Sockets.GetPortNumber( localAdr );
  79. IF Sockets.Listen( socket ) THEN
  80. ignore := Sockets.SetLinger( socket );
  81. SELF.fip := IP.NilAdr;
  82. state := Listen; res := Ok
  83. ELSE Sockets.Close( socket ); res := NotConnected
  84. END
  85. ELSE Sockets.Close( socket ); res := NotConnected
  86. END
  87. END
  88. ELSE
  89. Out.String( "open socket failed" ); Out.Ln;
  90. res := NotConnected
  91. END;
  92. IF res = Ok THEN
  93. (* create a dummy interface with correct local IP-adr *)
  94. NEW( int, Sockets.SockAdrToIPAdr( localAdr ) )
  95. END;
  96. IF trace THEN
  97. IF res = Ok THEN
  98. Out.String( "socket=" ); Out.Int( socket, 0 );
  99. Out.String( ", locport=" ); Out.Int( SELF.lport, 0 );
  100. Out.String( " done." )
  101. ELSE
  102. Out.String( " failed." )
  103. END;
  104. Out.Ln
  105. END;
  106. END Open;
  107. (** Send data on a TCP connection. *)
  108. PROCEDURE Send*( CONST data: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: WORD );
  109. VAR n: LONGINT;
  110. BEGIN {EXCLUSIVE}
  111. IF trace THEN Out.String( "Send: socket=" ); Out.Int( socket, 0 ) END;
  112. IF state = Established THEN
  113. res := Ok;
  114. WHILE len > 0 DO
  115. n := len;
  116. IF Sockets.Send( socket, data, ofs, n ) THEN
  117. DEC( len, n ); INC( ofs, n )
  118. ELSE
  119. res := ConnectionReset; len := 0
  120. END
  121. END
  122. ELSE
  123. res := NotConnected (* Send on a Connection with state=Listen *)
  124. END;
  125. INC( sndnxt )
  126. END Send;
  127. (** Receive data on a TCP connection. The data parameter specifies the buffer.
  128. The ofs parameters specify the position in the buffer where data should be received (usually 0),
  129. and the size parameters specifies how many bytes of data can be received in the buffer.
  130. The min parameter specifies the minimum number of bytes to receive before Receive returns
  131. and must by <= size. The len parameter returns the number of bytes received, and the
  132. res parameter returns 0 if ok, or a non-zero error code otherwise (e.g. if the connection is closed
  133. by the communication partner, or by a call of the Close method). *)
  134. PROCEDURE Receive*( VAR data: ARRAY OF CHAR; ofs, size, min: LONGINT; VAR len: LONGINT; VAR res: WORD );
  135. VAR p, x: LONGINT;
  136. BEGIN {EXCLUSIVE}
  137. ASSERT( (ofs >= 0) & (ofs + size <= LEN( data )) & (min <= size) ); (* parameter consistency check *)
  138. IF trace THEN
  139. Out.String( "Receive: socket=" ); Out.Int( socket, 0 );
  140. Out.String( " min=" ); Out.Int( min, 0 );
  141. p := ofs
  142. END;
  143. len := 0; res := Ok;
  144. IF size = 0 THEN RETURN END;
  145. IF state IN {Listen, Established} THEN
  146. LOOP
  147. x := size;
  148. IF Sockets.Recv( socket, data, ofs, x, 0 ) THEN
  149. IF x > 0 THEN
  150. DEC( size, x ); INC( len, x ); INC( ofs, x );
  151. IF len >= min THEN
  152. INC( rcvnxt );
  153. RETURN
  154. END
  155. ELSE
  156. (* x = 0: closed by peer *)
  157. Sockets.Close( socket ); state := Closed;
  158. res := NotConnected; RETURN
  159. END
  160. ELSE
  161. Sockets.Close( socket ); state := Closed;
  162. res := NotConnected; RETURN
  163. END
  164. END; (* loop *)
  165. ELSE
  166. res := NotConnected
  167. END;
  168. INC( rcvnxt )
  169. END Receive;
  170. (** Enable or disable delayed send (Nagle algorithm).
  171. If enabled, the sending of a segment is delayed if it is not filled by one call to Send, in order to be able
  172. to be filled by further calls to Send. This is the default option.
  173. If disabled, a segment is sent immediatly after a call to Send, even if it is not filled.
  174. This option is normally chosen by applications like telnet or VNC client, which send verly little data but
  175. shall not be delayed.*)
  176. PROCEDURE DelaySend*( enable: BOOLEAN );
  177. VAR ignore: BOOLEAN;
  178. BEGIN {EXCLUSIVE}
  179. ignore := Sockets.NoDelay( socket, ~enable )
  180. END DelaySend;
  181. (** Enable or disable keep-alive. (default: disabled) *)
  182. PROCEDURE KeepAlive*( enable: BOOLEAN );
  183. VAR ignore: BOOLEAN;
  184. BEGIN {EXCLUSIVE}
  185. ignore := Sockets.KeepAlive( socket, enable )
  186. END KeepAlive;
  187. (** Return number of bytes that may be read without blocking. *)
  188. PROCEDURE Available*( ): LONGINT;
  189. VAR available: LONGINT;
  190. BEGIN {EXCLUSIVE}
  191. IF state IN {Established, Listen} THEN
  192. IF Sockets.Requested( socket ) THEN
  193. available := Sockets.Available( socket );
  194. IF available >= 0 THEN
  195. RETURN available
  196. END;
  197. END
  198. END;
  199. RETURN 0
  200. END Available;
  201. (** Return connection state. *)
  202. PROCEDURE State*( ): LONGINT;
  203. BEGIN
  204. RETURN state
  205. END State;
  206. (** Wait until the connection state is either in the good or bad set, up to "ms" milliseconds. *)
  207. PROCEDURE AwaitState*( good, bad: SET; ms: LONGINT; VAR res: WORD );
  208. BEGIN
  209. WHILE (ms > 0) & ~(state IN (good+bad)) DO Objects.Sleep( 10 ); DEC( ms, 10 ) END;
  210. IF state IN good THEN
  211. res := Ok
  212. ELSIF state IN bad THEN
  213. res := NotConnected
  214. ELSE
  215. res := TimedOut
  216. END
  217. END AwaitState;
  218. (** Close a TCP connection (half-close). *)
  219. PROCEDURE Close*;
  220. BEGIN
  221. Sockets.Close( socket ); state := Closed;
  222. END Close;
  223. (** Discard a TCP connection (shutdown). *)
  224. PROCEDURE Discard*;
  225. BEGIN
  226. Sockets.Close( socket ); state := Closed;
  227. END Discard;
  228. (** Accept a client waiting on a listening connection. Blocks until a client is available or the
  229. connection is closed. *)
  230. PROCEDURE Accept*( VAR client: Connection; VAR res: WORD );
  231. VAR newsocket: LONGINT; peerAdr: Sockets.SocketAdr;
  232. BEGIN {EXCLUSIVE}
  233. IF trace THEN
  234. Out.String( "Accept: socket=" ); Out.Int( socket, 0 ); Out.String( " ... " )
  235. END;
  236. IF state = Listen THEN
  237. newsocket := Sockets.Accept( socket );
  238. IF newsocket > 0 THEN
  239. peerAdr := Sockets.GetPeerName( newsocket );
  240. NEW( client );
  241. client.int := int;
  242. client.socket := newsocket;
  243. client.state := Established;
  244. client.fip := Sockets.SockAdrToIPAdr( peerAdr );
  245. client.fport := Sockets.GetPortNumber( peerAdr );
  246. IF trace THEN
  247. Out.String( "Accept done, client socket=" ); Out.Int( newsocket, 0 ); Out.Ln
  248. END;
  249. res := Ok
  250. ELSE
  251. res := NotConnected ;
  252. IF trace THEN Out.String( "Accept failed." ); Out.Ln END
  253. END;
  254. ELSE
  255. res := NotConnected ;
  256. IF trace THEN Out.String( "Accept failed (state # Listen)." ); Out.Ln END
  257. END;
  258. END Accept;
  259. (** Return TRUE iff a listening connection has clients waiting to be accepted. *)
  260. PROCEDURE Requested*( ): BOOLEAN;
  261. BEGIN {EXCLUSIVE}
  262. RETURN (state = Listen) & Sockets.Requested( socket )
  263. END Requested;
  264. END Connection;
  265. (** Aos command - display all errors *)
  266. PROCEDURE DisplayErrors*( par: ANY ): ANY;
  267. BEGIN
  268. RETURN NIL;
  269. END DisplayErrors;
  270. (** Aos command - discard and finalize all connections *)
  271. PROCEDURE DiscardAll*( par: ANY ): ANY;
  272. BEGIN
  273. RETURN NIL;
  274. END DiscardAll;
  275. (** Temporary trace procedure. *)
  276. PROCEDURE ToggleTrace*;
  277. BEGIN
  278. trace := ~trace;
  279. Out.Enter;
  280. Out.String( "TCP trace " );
  281. IF trace THEN Out.String( "on" ) ELSE Out.String( "off" ) END;
  282. Out.Exit
  283. END ToggleTrace;
  284. END TCP.