1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . S O C K E T S . T H I N . C _ S O C K E T P A I R --
9 -- Copyright (C) 2001-2020, AdaCore --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 -- Portable sockets-based implementation of the C_Socketpair used for
33 -- platforms that do not support UNIX socketpair system call.
35 -- Note: this code is only for non-UNIX platforms.
37 separate (GNAT.Sockets.Thin)
42 Fds : not null access Fd_Pair) return C.int
44 use type C.char_array;
46 L_Sock, C_Sock, P_Sock : C.int := Failure;
47 -- Listening socket, client socket and peer socket
49 Family : constant Family_Type :=
51 when SOSC.AF_INET => Family_Inet,
52 when SOSC.AF_INET6 => Family_Inet6,
53 when others => Family_Unspec);
55 Len : aliased C.int := C.int (Lengths (Family));
57 C_Sin : aliased Sockaddr;
58 C_Bin : aliased C.char_array (1 .. C.size_t (Len));
59 for C_Bin'Address use C_Sin'Address;
60 -- Address of listening and client socket and it's binary representation.
61 -- We need binary representation because Ada does not allow to compare
62 -- unchecked union if either of the operands lacks inferable discriminants.
65 P_Sin : aliased Sockaddr;
66 P_Bin : aliased C.char_array (1 .. C.size_t (Len));
67 for P_Bin'Address use P_Sin'Address;
68 -- Address of peer socket and it's binary representation
70 T_Sin : aliased Sockaddr;
71 T_Bin : aliased C.char_array (1 .. C.size_t (Len));
72 for T_Bin'Address use T_Sin'Address;
73 -- Temporary address to compare and check that address and port of the
74 -- socket equal to peer address and port of the opposite connected socket.
76 Res : C.int with Warnings => Off;
79 Set_Family (C_Sin.Sin_Family, Family);
83 C_Sin.Sin_Addr.S_B1 := 127;
84 C_Sin.Sin_Addr.S_B4 := 1;
87 C_Sin.Sin6_Addr (C_Sin.Sin6_Addr'Last) := 1;
90 Set_Socket_Errno (SOSC.EAFNOSUPPORT);
95 -- Retry loop, in case the C_Connect below fails
99 -- Create a listening socket
101 L_Sock := C_Socket (Domain, Typ, Protocol);
102 exit when L_Sock = Failure;
104 -- Bind the socket to an available port on localhost
106 Res := C_Bind (L_Sock, C_Sin'Address, Len);
107 exit when Res = Failure;
111 Res := C_Getsockname (L_Sock, C_Sin'Address, Len'Access);
112 exit when Res = Failure;
114 -- Set socket to listen mode, with a backlog of 1 to guarantee that
115 -- exactly one call to connect(2) succeeds.
117 Res := C_Listen (L_Sock, 1);
118 exit when Res = Failure;
120 -- Create read end (client) socket
122 C_Sock := C_Socket (Domain, Typ, Protocol);
123 exit when C_Sock = Failure;
125 -- Connect listening socket
127 Res := C_Connect (C_Sock, C_Sin'Address, Len);
129 if Res = Failure then
130 -- In rare cases, the above C_Bind chooses a port that is still
131 -- marked "in use", even though it has been closed (perhaps by some
132 -- other process that has already exited). This causes the above
133 -- C_Connect to fail with EADDRINUSE. In this case, we close the
134 -- ports, and loop back to try again. This mysterious Windows
135 -- behavior is documented. See, for example:
136 -- http://msdn2.microsoft.com/en-us/library/ms737625.aspx
137 -- In an experiment with 2000 calls, 21 required exactly one retry, 7
138 -- required two, and none required three or more. Note that no delay
139 -- is needed between retries; retrying C_Bind will typically produce
142 exit when Socket_Errno /= SOSC.EADDRINUSE;
147 -- Since the call to connect(2) has succeeded and the backlog limit
148 -- on the listening socket is 1, we know that there is now exactly
149 -- one pending connection on L_Sock, which is the one from R_Sock.
151 P_Sin.Sun_Path := (others => C.nul);
153 P_Sock := C_Accept (L_Sock, P_Sin'Address, Len'Access);
154 exit when P_Sock = Failure;
156 -- Address and port of the socket equal to peer address and port of the
157 -- opposite connected socket.
159 Res := C_Getsockname (P_Sock, T_Sin'Address, Len'Access);
160 exit when Res = Failure;
162 if T_Bin /= C_Bin then
166 -- Address and port of the socket equal to peer address and port of the
167 -- opposite connected socket.
169 Res := C_Getsockname (C_Sock, T_Sin'Address, Len'Access);
170 exit when Res = Failure;
172 if T_Bin /= P_Bin then
176 -- Close listening socket (ignore exit status)
178 Res := C_Close (L_Sock);
180 Fds.all := (Read_End => C_Sock, Write_End => P_Sock);
182 return Thin_Common.Success;
185 Res := C_Close (C_Sock);
187 Res := C_Close (P_Sock);
189 Res := C_Close (L_Sock);
194 Saved_Errno : constant Integer := Socket_Errno;
197 if P_Sock /= Failure then
198 Res := C_Close (P_Sock);
201 if C_Sock /= Failure then
202 Res := C_Close (C_Sock);
205 if L_Sock /= Failure then
206 Res := C_Close (L_Sock);
209 Set_Socket_Errno (Saved_Errno);