The following text is provided for informational purposes only and should not be construed as imposing normative requirements.
For brevity in the following discussion:
Each listening endpoint can only be listening on one local address. When an incoming connection request is detected by the provider it looks for a matching listener in the TS_BIND state. If it does not find one, it fails the connection request, otherwise it constructs a T_CONN_IND message and sends it up the listener to the user. The user sends a T_CONN_RES if it wants to accept the connection, or a T_DISCON_REQ if it does not.
It is permissible for the listener to conduct the actual connection, but this is unusual in practice because, while it is doing so, it cannot also perform its listening task because it will be in some other state than TS_BIND. By far the more usual methodology is for the user to establish a new endpoint and use that for conducting the actual connection while the listener continues to listen for further incoming connections. The T_CONN_RES message contains a field ACCEPTOR_id which is used to identify the endpoint on which the user wishes to conduct the connection. The encoding of this field is implementation specific as are the methods of acquiring a valid value for it, and the method employed by the provider in interpreting it.
In older versions of the TPI standard the ACCEPTOR_id field was called QUEUE_ptr and had the type queue_t *. This unfortunately exposed an implementation detail which made the use of TPI difficult on systems where a pointer is a different length at different times (for example a 64-bit system supporting both 32-bit and 64-bit user applications), and also on systems where transport provider operates in a different address space from other parts of the operating system. Nevertheless, on many systems, the ACCEPTOR_id is still given the value of the provider queue pair read pointer of the endpoint which is to be used to conduct the connection. This remains a perfectly good implementation strategy for those systems which do not suffer the problems mentioned above. The value of the QUEUE_ptr variable was never used by the user as any more than an opaque identifier value (in fact most implementations did not even expose the value to the user).
When the user receives the T_CONN_IND message, it usually opens an entirely new endpoint (to the same transport provider). It may choose to bind that new endpoint to a local address, or it may leave the provider to perform that task on receipt of the T_CONN_RES. Any address it binds to must satisfy the requirements of the provider for the connection. The new endpoint should not have a CONIND_number greater than 0.
The user constructs a T_CONN_RES message. It copies in the SEQ_number from the T_CONN_IND (otherwise the transport will not know to which connection it is responding), removes (if necessary) the options it is not prepared to support, and copies the remainder into the T_CONN_RES . The T_CONN_RES is now complete except for the ACCEPTOR_id. The user does not directly have the information to include in this field; only the operating system kernel can derive that. The usual solution is for the kernel to supply a special ioctl(2) call called I_FDINSERT which expects as argument a T_CONN_RES message and the file-descriptor of the new (accepting) endpoint. This ioctl(2) call is specially treated. Before the message is sent down to the provider, the kernel uses the file-descriptor to access the endpoint. It extracts the value of the provider read queue pointer from that endpoint and places its value in the ACCEPTOR_id field. Then it sends the message to the provider.
The provider cross-references the SEQ_number and determines that it has such a pending connection, then it checks that the ACCEPTOR_id matches the read queue pointer of a valid endpoint (it must exist and obey all the general and provider specific rules). If it is not already bound to a local address the provider will bind it to the same address as that to which the listener is bound. If the ACCEPTOR_id identifies the listener, then the listener becomes the acceptor and further incoming connection requests for its address will fail, at least until the connection terminates. In the usual case, however, a new endpoint is used to conduct the new connection.
If the listener concocts an ACCEPTOR_id which does not represent one of its own endpoints, and gets it exactly correct, then it is possible that it could foist one of its own connections off onto an unsuspecting endpoint if it was in the correct state, etc. This could be a denial of service attack. What it cannot do, is to hijack a connection from another listener.
The STREAM head generates the identifier and places the result in ACCEPTOR_id. When the T_CONN_RES message reaches the provider, it decodes the ACCEPTOR_id to identify the accepting endpoint.
Contents | Next section | Index |