Unix.TCP.Mod 11 KB

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