| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T . S O C K E T S . T H I N -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2001-2004 Ada Core Technologies, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 2, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING. If not, write -- |
| -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- |
| -- MA 02111-1307, USA. -- |
| -- -- |
| -- As a special exception, if other files instantiate generics from this -- |
| -- unit, or you link this unit with other files to produce an executable, -- |
| -- this unit does not by itself cause the resulting executable to be -- |
| -- covered by the GNU General Public License. This exception does not -- |
| -- however invalidate any other reasons why the executable file might be -- |
| -- covered by the GNU Public License. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| -- This package provides a target dependent thin interface to the sockets |
| -- layer for use by the GNAT.Sockets package (g-socket.ads). This package |
| -- should not be directly with'ed by an applications program. |
| |
| -- This version is for NT. |
| |
| with GNAT.Sockets.Constants; use GNAT.Sockets.Constants; |
| with Interfaces.C.Strings; use Interfaces.C.Strings; |
| |
| with System; use System; |
| |
| package body GNAT.Sockets.Thin is |
| |
| use type C.unsigned; |
| |
| WSAData_Dummy : array (1 .. 512) of C.int; |
| |
| WS_Version : constant := 16#0101#; |
| Initialized : Boolean := False; |
| |
| SYSNOTREADY : constant := 10091; |
| VERNOTSUPPORTED : constant := 10092; |
| NOTINITIALISED : constant := 10093; |
| EDISCON : constant := 10101; |
| |
| function Standard_Connect |
| (S : C.int; |
| Name : System.Address; |
| Namelen : C.int) |
| return C.int; |
| pragma Import (Stdcall, Standard_Connect, "connect"); |
| |
| function Standard_Select |
| (Nfds : C.int; |
| Readfds : Fd_Set_Access; |
| Writefds : Fd_Set_Access; |
| Exceptfds : Fd_Set_Access; |
| Timeout : Timeval_Access) |
| return C.int; |
| pragma Import (Stdcall, Standard_Select, "select"); |
| |
| type Error_Type is |
| (N_EINTR, |
| N_EBADF, |
| N_EACCES, |
| N_EFAULT, |
| N_EINVAL, |
| N_EMFILE, |
| N_EWOULDBLOCK, |
| N_EINPROGRESS, |
| N_EALREADY, |
| N_ENOTSOCK, |
| N_EDESTADDRREQ, |
| N_EMSGSIZE, |
| N_EPROTOTYPE, |
| N_ENOPROTOOPT, |
| N_EPROTONOSUPPORT, |
| N_ESOCKTNOSUPPORT, |
| N_EOPNOTSUPP, |
| N_EPFNOSUPPORT, |
| N_EAFNOSUPPORT, |
| N_EADDRINUSE, |
| N_EADDRNOTAVAIL, |
| N_ENETDOWN, |
| N_ENETUNREACH, |
| N_ENETRESET, |
| N_ECONNABORTED, |
| N_ECONNRESET, |
| N_ENOBUFS, |
| N_EISCONN, |
| N_ENOTCONN, |
| N_ESHUTDOWN, |
| N_ETOOMANYREFS, |
| N_ETIMEDOUT, |
| N_ECONNREFUSED, |
| N_ELOOP, |
| N_ENAMETOOLONG, |
| N_EHOSTDOWN, |
| N_EHOSTUNREACH, |
| N_SYSNOTREADY, |
| N_VERNOTSUPPORTED, |
| N_NOTINITIALISED, |
| N_EDISCON, |
| N_HOST_NOT_FOUND, |
| N_TRY_AGAIN, |
| N_NO_RECOVERY, |
| N_NO_DATA, |
| N_OTHERS); |
| |
| Error_Messages : constant array (Error_Type) of chars_ptr := |
| (N_EINTR => |
| New_String ("Interrupted system call"), |
| N_EBADF => |
| New_String ("Bad file number"), |
| N_EACCES => |
| New_String ("Permission denied"), |
| N_EFAULT => |
| New_String ("Bad address"), |
| N_EINVAL => |
| New_String ("Invalid argument"), |
| N_EMFILE => |
| New_String ("Too many open files"), |
| N_EWOULDBLOCK => |
| New_String ("Operation would block"), |
| N_EINPROGRESS => |
| New_String ("Operation now in progress. This error is " |
| & "returned if any Windows Sockets API " |
| & "function is called while a blocking " |
| & "function is in progress"), |
| N_EALREADY => |
| New_String ("Operation already in progress"), |
| N_ENOTSOCK => |
| New_String ("Socket operation on nonsocket"), |
| N_EDESTADDRREQ => |
| New_String ("Destination address required"), |
| N_EMSGSIZE => |
| New_String ("Message too long"), |
| N_EPROTOTYPE => |
| New_String ("Protocol wrong type for socket"), |
| N_ENOPROTOOPT => |
| New_String ("Protocol not available"), |
| N_EPROTONOSUPPORT => |
| New_String ("Protocol not supported"), |
| N_ESOCKTNOSUPPORT => |
| New_String ("Socket type not supported"), |
| N_EOPNOTSUPP => |
| New_String ("Operation not supported on socket"), |
| N_EPFNOSUPPORT => |
| New_String ("Protocol family not supported"), |
| N_EAFNOSUPPORT => |
| New_String ("Address family not supported by protocol family"), |
| N_EADDRINUSE => |
| New_String ("Address already in use"), |
| N_EADDRNOTAVAIL => |
| New_String ("Cannot assign requested address"), |
| N_ENETDOWN => |
| New_String ("Network is down. This error may be " |
| & "reported at any time if the Windows " |
| & "Sockets implementation detects an " |
| & "underlying failure"), |
| N_ENETUNREACH => |
| New_String ("Network is unreachable"), |
| N_ENETRESET => |
| New_String ("Network dropped connection on reset"), |
| N_ECONNABORTED => |
| New_String ("Software caused connection abort"), |
| N_ECONNRESET => |
| New_String ("Connection reset by peer"), |
| N_ENOBUFS => |
| New_String ("No buffer space available"), |
| N_EISCONN => |
| New_String ("Socket is already connected"), |
| N_ENOTCONN => |
| New_String ("Socket is not connected"), |
| N_ESHUTDOWN => |
| New_String ("Cannot send after socket shutdown"), |
| N_ETOOMANYREFS => |
| New_String ("Too many references: cannot splice"), |
| N_ETIMEDOUT => |
| New_String ("Connection timed out"), |
| N_ECONNREFUSED => |
| New_String ("Connection refused"), |
| N_ELOOP => |
| New_String ("Too many levels of symbolic links"), |
| N_ENAMETOOLONG => |
| New_String ("File name too long"), |
| N_EHOSTDOWN => |
| New_String ("Host is down"), |
| N_EHOSTUNREACH => |
| New_String ("No route to host"), |
| N_SYSNOTREADY => |
| New_String ("Returned by WSAStartup(), indicating that " |
| & "the network subsystem is unusable"), |
| N_VERNOTSUPPORTED => |
| New_String ("Returned by WSAStartup(), indicating that " |
| & "the Windows Sockets DLL cannot support " |
| & "this application"), |
| N_NOTINITIALISED => |
| New_String ("Winsock not initialized. This message is " |
| & "returned by any function except WSAStartup(), " |
| & "indicating that a successful WSAStartup() has " |
| & "not yet been performed"), |
| N_EDISCON => |
| New_String ("Disconnect"), |
| N_HOST_NOT_FOUND => |
| New_String ("Host not found. This message indicates " |
| & "that the key (name, address, and so on) was not found"), |
| N_TRY_AGAIN => |
| New_String ("Nonauthoritative host not found. This error may " |
| & "suggest that the name service itself is not " |
| & "functioning"), |
| N_NO_RECOVERY => |
| New_String ("Nonrecoverable error. This error may suggest that the " |
| & "name service itself is not functioning"), |
| N_NO_DATA => |
| New_String ("Valid name, no data record of requested type. " |
| & "This error indicates that the key (name, address, " |
| & "and so on) was not found."), |
| N_OTHERS => |
| New_String ("Unknown system error")); |
| |
| --------------- |
| -- C_Connect -- |
| --------------- |
| |
| function C_Connect |
| (S : C.int; |
| Name : System.Address; |
| Namelen : C.int) |
| return C.int |
| is |
| Res : C.int; |
| |
| begin |
| Res := Standard_Connect (S, Name, Namelen); |
| |
| if Res = -1 then |
| if Socket_Errno = EWOULDBLOCK then |
| Set_Socket_Errno (EINPROGRESS); |
| end if; |
| end if; |
| |
| return Res; |
| end C_Connect; |
| |
| ------------- |
| -- C_Readv -- |
| ------------- |
| |
| function C_Readv |
| (Socket : C.int; |
| Iov : System.Address; |
| Iovcnt : C.int) |
| return C.int |
| is |
| Res : C.int; |
| Count : C.int := 0; |
| |
| Iovec : array (0 .. Iovcnt - 1) of Vector_Element; |
| for Iovec'Address use Iov; |
| pragma Import (Ada, Iovec); |
| |
| begin |
| for J in Iovec'Range loop |
| Res := C_Recv |
| (Socket, |
| Iovec (J).Base.all'Address, |
| C.int (Iovec (J).Length), |
| 0); |
| |
| if Res < 0 then |
| return Res; |
| else |
| Count := Count + Res; |
| end if; |
| end loop; |
| return Count; |
| end C_Readv; |
| |
| -------------- |
| -- C_Select -- |
| -------------- |
| |
| function C_Select |
| (Nfds : C.int; |
| Readfds : Fd_Set_Access; |
| Writefds : Fd_Set_Access; |
| Exceptfds : Fd_Set_Access; |
| Timeout : Timeval_Access) |
| return C.int |
| is |
| pragma Warnings (Off, Exceptfds); |
| |
| RFS : constant Fd_Set_Access := Readfds; |
| WFS : constant Fd_Set_Access := Writefds; |
| WFSC : Fd_Set_Access := No_Fd_Set; |
| EFS : Fd_Set_Access := Exceptfds; |
| Res : C.int; |
| S : aliased C.int; |
| Last : aliased C.int; |
| |
| begin |
| -- Asynchronous connection failures are notified in the |
| -- exception fd set instead of the write fd set. To ensure |
| -- POSIX compatitibility, copy write fd set into exception fd |
| -- set. Once select() returns, check any socket present in the |
| -- exception fd set and peek at incoming out-of-band data. If |
| -- the test is not successfull and if the socket is present in |
| -- the initial write fd set, then move the socket from the |
| -- exception fd set to the write fd set. |
| |
| if WFS /= No_Fd_Set then |
| -- Add any socket present in write fd set into exception fd set |
| |
| if EFS = No_Fd_Set then |
| EFS := New_Socket_Set (WFS); |
| |
| else |
| WFSC := New_Socket_Set (WFS); |
| |
| Last := Nfds - 1; |
| loop |
| Get_Socket_From_Set |
| (WFSC, S'Unchecked_Access, Last'Unchecked_Access); |
| exit when S = -1; |
| Insert_Socket_In_Set (EFS, S); |
| end loop; |
| |
| Free_Socket_Set (WFSC); |
| end if; |
| |
| -- Keep a copy of write fd set |
| |
| WFSC := New_Socket_Set (WFS); |
| end if; |
| |
| Res := Standard_Select (Nfds, RFS, WFS, EFS, Timeout); |
| |
| if EFS /= No_Fd_Set then |
| declare |
| EFSC : constant Fd_Set_Access := New_Socket_Set (EFS); |
| Flag : constant C.int := MSG_PEEK + MSG_OOB; |
| Buffer : Character; |
| Length : C.int; |
| Fromlen : aliased C.int; |
| |
| begin |
| Last := Nfds - 1; |
| loop |
| Get_Socket_From_Set |
| (EFSC, S'Unchecked_Access, Last'Unchecked_Access); |
| |
| -- No more sockets in EFSC |
| |
| exit when S = -1; |
| |
| -- Check out-of-band data |
| |
| Length := C_Recvfrom |
| (S, Buffer'Address, 1, Flag, |
| null, Fromlen'Unchecked_Access); |
| |
| -- If the signal is not an out-of-band data, then it |
| -- is a connection failure notification. |
| |
| if Length = -1 then |
| Remove_Socket_From_Set (EFS, S); |
| |
| -- If S is present in the initial write fd set, |
| -- move it from exception fd set back to write fd |
| -- set. Otherwise, ignore this event since the user |
| -- is not watching for it. |
| |
| if WFSC /= No_Fd_Set |
| and then Is_Socket_In_Set (WFSC, S) |
| then |
| Insert_Socket_In_Set (WFS, S); |
| end if; |
| end if; |
| end loop; |
| |
| Free_Socket_Set (EFSC); |
| end; |
| |
| if Exceptfds = No_Fd_Set then |
| Free_Socket_Set (EFS); |
| end if; |
| end if; |
| |
| -- Free any copy of write fd set |
| |
| if WFSC /= No_Fd_Set then |
| Free_Socket_Set (WFSC); |
| end if; |
| |
| return Res; |
| end C_Select; |
| |
| -------------- |
| -- C_Writev -- |
| -------------- |
| |
| function C_Writev |
| (Socket : C.int; |
| Iov : System.Address; |
| Iovcnt : C.int) |
| return C.int |
| is |
| Res : C.int; |
| Count : C.int := 0; |
| |
| Iovec : array (0 .. Iovcnt - 1) of Vector_Element; |
| for Iovec'Address use Iov; |
| pragma Import (Ada, Iovec); |
| |
| begin |
| for J in Iovec'Range loop |
| Res := C_Send |
| (Socket, |
| Iovec (J).Base.all'Address, |
| C.int (Iovec (J).Length), |
| 0); |
| |
| if Res < 0 then |
| return Res; |
| else |
| Count := Count + Res; |
| end if; |
| end loop; |
| return Count; |
| end C_Writev; |
| |
| -------------- |
| -- Finalize -- |
| -------------- |
| |
| procedure Finalize is |
| begin |
| if Initialized then |
| WSACleanup; |
| Initialized := False; |
| end if; |
| end Finalize; |
| |
| ---------------- |
| -- Initialize -- |
| ---------------- |
| |
| procedure Initialize (Process_Blocking_IO : Boolean := False) is |
| pragma Unreferenced (Process_Blocking_IO); |
| |
| Return_Value : Interfaces.C.int; |
| |
| begin |
| if not Initialized then |
| Return_Value := WSAStartup (WS_Version, WSAData_Dummy'Address); |
| pragma Assert (Interfaces.C."=" (Return_Value, 0)); |
| Initialized := True; |
| end if; |
| end Initialize; |
| |
| ----------------- |
| -- Set_Address -- |
| ----------------- |
| |
| procedure Set_Address |
| (Sin : Sockaddr_In_Access; |
| Address : In_Addr) |
| is |
| begin |
| Sin.Sin_Addr := Address; |
| end Set_Address; |
| |
| ---------------- |
| -- Set_Family -- |
| ---------------- |
| |
| procedure Set_Family |
| (Sin : Sockaddr_In_Access; |
| Family : C.int) |
| is |
| begin |
| Sin.Sin_Family := C.unsigned_short (Family); |
| end Set_Family; |
| |
| ---------------- |
| -- Set_Length -- |
| ---------------- |
| |
| procedure Set_Length |
| (Sin : Sockaddr_In_Access; |
| Len : C.int) |
| is |
| pragma Unreferenced (Sin); |
| pragma Unreferenced (Len); |
| |
| begin |
| null; |
| end Set_Length; |
| |
| -------------- |
| -- Set_Port -- |
| -------------- |
| |
| procedure Set_Port |
| (Sin : Sockaddr_In_Access; |
| Port : C.unsigned_short) |
| is |
| begin |
| Sin.Sin_Port := Port; |
| end Set_Port; |
| |
| -------------------------- |
| -- Socket_Error_Message -- |
| -------------------------- |
| |
| function Socket_Error_Message |
| (Errno : Integer) |
| return C.Strings.chars_ptr |
| is |
| use GNAT.Sockets.Constants; |
| |
| begin |
| case Errno is |
| when EINTR => return Error_Messages (N_EINTR); |
| when EBADF => return Error_Messages (N_EBADF); |
| when EACCES => return Error_Messages (N_EACCES); |
| when EFAULT => return Error_Messages (N_EFAULT); |
| when EINVAL => return Error_Messages (N_EINVAL); |
| when EMFILE => return Error_Messages (N_EMFILE); |
| when EWOULDBLOCK => return Error_Messages (N_EWOULDBLOCK); |
| when EINPROGRESS => return Error_Messages (N_EINPROGRESS); |
| when EALREADY => return Error_Messages (N_EALREADY); |
| when ENOTSOCK => return Error_Messages (N_ENOTSOCK); |
| when EDESTADDRREQ => return Error_Messages (N_EDESTADDRREQ); |
| when EMSGSIZE => return Error_Messages (N_EMSGSIZE); |
| when EPROTOTYPE => return Error_Messages (N_EPROTOTYPE); |
| when ENOPROTOOPT => return Error_Messages (N_ENOPROTOOPT); |
| when EPROTONOSUPPORT => return Error_Messages (N_EPROTONOSUPPORT); |
| when ESOCKTNOSUPPORT => return Error_Messages (N_ESOCKTNOSUPPORT); |
| when EOPNOTSUPP => return Error_Messages (N_EOPNOTSUPP); |
| when EPFNOSUPPORT => return Error_Messages (N_EPFNOSUPPORT); |
| when EAFNOSUPPORT => return Error_Messages (N_EAFNOSUPPORT); |
| when EADDRINUSE => return Error_Messages (N_EADDRINUSE); |
| when EADDRNOTAVAIL => return Error_Messages (N_EADDRNOTAVAIL); |
| when ENETDOWN => return Error_Messages (N_ENETDOWN); |
| when ENETUNREACH => return Error_Messages (N_ENETUNREACH); |
| when ENETRESET => return Error_Messages (N_ENETRESET); |
| when ECONNABORTED => return Error_Messages (N_ECONNABORTED); |
| when ECONNRESET => return Error_Messages (N_ECONNRESET); |
| when ENOBUFS => return Error_Messages (N_ENOBUFS); |
| when EISCONN => return Error_Messages (N_EISCONN); |
| when ENOTCONN => return Error_Messages (N_ENOTCONN); |
| when ESHUTDOWN => return Error_Messages (N_ESHUTDOWN); |
| when ETOOMANYREFS => return Error_Messages (N_ETOOMANYREFS); |
| when ETIMEDOUT => return Error_Messages (N_ETIMEDOUT); |
| when ECONNREFUSED => return Error_Messages (N_ECONNREFUSED); |
| when ELOOP => return Error_Messages (N_ELOOP); |
| when ENAMETOOLONG => return Error_Messages (N_ENAMETOOLONG); |
| when EHOSTDOWN => return Error_Messages (N_EHOSTDOWN); |
| when EHOSTUNREACH => return Error_Messages (N_EHOSTUNREACH); |
| when SYSNOTREADY => return Error_Messages (N_SYSNOTREADY); |
| when VERNOTSUPPORTED => return Error_Messages (N_VERNOTSUPPORTED); |
| when NOTINITIALISED => return Error_Messages (N_NOTINITIALISED); |
| when EDISCON => return Error_Messages (N_EDISCON); |
| when HOST_NOT_FOUND => return Error_Messages (N_HOST_NOT_FOUND); |
| when TRY_AGAIN => return Error_Messages (N_TRY_AGAIN); |
| when NO_RECOVERY => return Error_Messages (N_NO_RECOVERY); |
| when NO_DATA => return Error_Messages (N_NO_DATA); |
| when others => return Error_Messages (N_OTHERS); |
| end case; |
| end Socket_Error_Message; |
| |
| end GNAT.Sockets.Thin; |