Mercurial > hg > CbC > CbC_gcc
diff gcc/ada/libgnat/g-socket.adb @ 145:1830386684a0
gcc-9.2.0
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 11:34:05 +0900 |
parents | 84e7813d76e9 |
children |
line wrap: on
line diff
--- a/gcc/ada/libgnat/g-socket.adb Thu Oct 25 07:37:49 2018 +0900 +++ b/gcc/ada/libgnat/g-socket.adb Thu Feb 13 11:34:05 2020 +0900 @@ -6,7 +6,7 @@ -- -- -- B o d y -- -- -- --- Copyright (C) 2001-2018, AdaCore -- +-- Copyright (C) 2001-2019, AdaCore -- -- -- -- 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- -- @@ -31,6 +31,7 @@ with Ada.Streams; use Ada.Streams; with Ada.Exceptions; use Ada.Exceptions; +with Ada.Containers.Generic_Array_Sort; with Ada.Finalization; with Ada.Unchecked_Conversion; @@ -50,6 +51,12 @@ package C renames Interfaces.C; + type IPV6_Mreq is record + ipv6mr_multiaddr : In6_Addr; + ipv6mr_interface : C.unsigned; + end record with Convention => C; + -- Record to Add/Drop_Membership for multicast in IPv6 + ENOERROR : constant := 0; Netdb_Buffer_Size : constant := SOSC.Need_Netdb_Buffer * 1024; @@ -62,14 +69,19 @@ -- Correspondence tables Levels : constant array (Level_Type) of C.int := - (Socket_Level => SOSC.SOL_SOCKET, - IP_Protocol_For_IP_Level => SOSC.IPPROTO_IP, - IP_Protocol_For_UDP_Level => SOSC.IPPROTO_UDP, - IP_Protocol_For_TCP_Level => SOSC.IPPROTO_TCP); + (Socket_Level => SOSC.SOL_SOCKET, + IP_Protocol_For_IP_Level => SOSC.IPPROTO_IP, + IP_Protocol_For_IPv6_Level => SOSC.IPPROTO_IPV6, + IP_Protocol_For_UDP_Level => SOSC.IPPROTO_UDP, + IP_Protocol_For_TCP_Level => SOSC.IPPROTO_TCP, + IP_Protocol_For_ICMP_Level => SOSC.IPPROTO_ICMP, + IP_Protocol_For_IGMP_Level => SOSC.IPPROTO_IGMP, + IP_Protocol_For_RAW_Level => SOSC.IPPROTO_RAW); Modes : constant array (Mode_Type) of C.int := (Socket_Stream => SOSC.SOCK_STREAM, - Socket_Datagram => SOSC.SOCK_DGRAM); + Socket_Datagram => SOSC.SOCK_DGRAM, + Socket_Raw => SOSC.SOCK_RAW); Shutmodes : constant array (Shutmode_Type) of C.int := (Shut_Read => SOSC.SHUT_RD, @@ -89,12 +101,18 @@ Linger => SOSC.SO_LINGER, Error => SOSC.SO_ERROR, No_Delay => SOSC.TCP_NODELAY, - Add_Membership => SOSC.IP_ADD_MEMBERSHIP, - Drop_Membership => SOSC.IP_DROP_MEMBERSHIP, - Multicast_If => SOSC.IP_MULTICAST_IF, + Add_Membership_V4 => SOSC.IP_ADD_MEMBERSHIP, + Drop_Membership_V4 => SOSC.IP_DROP_MEMBERSHIP, + Multicast_If_V4 => SOSC.IP_MULTICAST_IF, + Multicast_Loop_V4 => SOSC.IP_MULTICAST_LOOP, + Receive_Packet_Info => SOSC.IP_PKTINFO, Multicast_TTL => SOSC.IP_MULTICAST_TTL, - Multicast_Loop => SOSC.IP_MULTICAST_LOOP, - Receive_Packet_Info => SOSC.IP_PKTINFO, + Add_Membership_V6 => SOSC.IPV6_ADD_MEMBERSHIP, + Drop_Membership_V6 => SOSC.IPV6_DROP_MEMBERSHIP, + Multicast_If_V6 => SOSC.IPV6_MULTICAST_IF, + Multicast_Loop_V6 => SOSC.IPV6_MULTICAST_LOOP, + Multicast_Hops => SOSC.IPV6_MULTICAST_HOPS, + IPv6_Only => SOSC.IPV6_V6ONLY, Send_Timeout => SOSC.SO_SNDTIMEO, Receive_Timeout => SOSC.SO_RCVTIMEO, Busy_Polling => SOSC.SO_BUSY_POLL); @@ -110,8 +128,14 @@ Socket_Error_Id : constant Exception_Id := Socket_Error'Identity; Host_Error_Id : constant Exception_Id := Host_Error'Identity; - Hex_To_Char : constant String (1 .. 16) := "0123456789ABCDEF"; - -- Use to print in hexadecimal format + type In_Addr_Union (Family : Family_Inet_4_6) is record + case Family is + when Family_Inet => + In4 : In_Addr; + when Family_Inet6 => + In6 : In6_Addr; + end case; + end record with Unchecked_Union; ----------------------- -- Local subprograms -- @@ -133,24 +157,6 @@ function Set_Forced_Flags (F : C.int) return C.int; -- Return F with the bits from SOSC.MSG_Forced_Flags forced set - function Short_To_Network - (S : C.unsigned_short) return C.unsigned_short; - pragma Inline (Short_To_Network); - -- Convert a port number into a network port number - - function Network_To_Short - (S : C.unsigned_short) return C.unsigned_short - renames Short_To_Network; - -- Symmetric operation - - function Image - (Val : Inet_Addr_Bytes; - Hex : Boolean := False) return String; - -- Output an array of inet address components in hex or decimal mode - - function Is_IP_Address (Name : String) return Boolean; - -- Return true when Name is an IPv4 address in dotted quad notation - procedure Netdb_Lock; pragma Inline (Netdb_Lock); procedure Netdb_Unlock; @@ -158,12 +164,6 @@ -- Lock/unlock operation used to protect netdb access for platforms that -- require such protection. - function To_In_Addr (Addr : Inet_Addr_Type) return In_Addr; - procedure To_Inet_Addr - (Addr : In_Addr; - Result : out Inet_Addr_Type); - -- Conversion functions - function To_Host_Entry (E : Hostent_Access) return Host_Entry_Type; -- Conversion function @@ -180,6 +180,12 @@ -- Reconstruct a Duration value from a Timeval record (seconds and -- microseconds). + function Dedot (Value : String) return String + is (if Value /= "" and then Value (Value'Last) = '.' + then Value (Value'First .. Value'Last - 1) + else Value); + -- Removes dot at the end of error message + procedure Raise_Socket_Error (Error : Integer); -- Raise Socket_Error with an exception message describing the error code -- from errno. @@ -189,6 +195,13 @@ -- hstrerror seems to be obsolete) from h_errno. Name is the name -- or address that was being looked up. + procedure Raise_GAI_Error (RC : C.int; Name : String); + -- Raise Host_Error with exception message in case of errors in + -- getaddrinfo and getnameinfo. + + function Is_Windows return Boolean with Inline; + -- Returns True on Windows platform + procedure Narrow (Item : in out Socket_Set_Type); -- Update Last as it may be greater than the real last socket @@ -276,7 +289,7 @@ -- or the null selector. function Create_Address - (Family : Family_Type; Bytes : Inet_Addr_Bytes) return Inet_Addr_Type + (Family : Family_Inet_4_6; Bytes : Inet_Addr_Bytes) return Inet_Addr_Type with Inline; -- Creates address from family and Inet_Addr_Bytes array. @@ -328,7 +341,7 @@ Address : out Sock_Addr_Type) is Res : C.int; - Sin : aliased Sockaddr_In; + Sin : aliased Sockaddr; Len : aliased C.int := Sin'Size / 8; begin @@ -339,9 +352,7 @@ end if; Socket := Socket_Type (Res); - - To_Inet_Addr (Sin.Sin_Addr, Address.Addr); - Address.Port := Port_Type (Network_To_Short (Sin.Sin_Port)); + Address := Get_Address (Sin, Len); end Accept_Socket; ------------------- @@ -451,20 +462,11 @@ Address : Sock_Addr_Type) is Res : C.int; - Sin : aliased Sockaddr_In; - Len : constant C.int := Sin'Size / 8; - -- This assumes that Address.Family = Family_Inet??? + Sin : aliased Sockaddr; + Len : C.int; begin - if Address.Family = Family_Inet6 then - raise Socket_Error with "IPv6 not supported"; - end if; - - Set_Family (Sin.Sin_Family, Address.Family); - Set_Address (Sin'Unchecked_Access, To_In_Addr (Address.Addr)); - Set_Port - (Sin'Unchecked_Access, - Short_To_Network (C.unsigned_short (Address.Port))); + Set_Address (Sin'Unchecked_Access, Address, Len); Res := C_Bind (C.int (Socket), Sin'Address, Len); @@ -478,14 +480,12 @@ ---------------------- procedure Check_For_Fd_Set (Fd : Socket_Type) is - use SOSC; - begin -- On Windows, fd_set is a FD_SETSIZE array of socket ids: -- no check required. Warnings suppressed because condition -- is known at compile time. - if Target_OS = Windows then + if Is_Windows then return; @@ -667,19 +667,10 @@ (Socket : Socket_Type; Server : Sock_Addr_Type) return C.int is - Sin : aliased Sockaddr_In; - Len : constant C.int := Sin'Size / 8; - + Sin : aliased Sockaddr; + Len : C.int; begin - if Server.Family = Family_Inet6 then - raise Socket_Error with "IPv6 not supported"; - end if; - - Set_Family (Sin.Sin_Family, Server.Family); - Set_Address (Sin'Unchecked_Access, To_In_Addr (Server.Addr)); - Set_Port - (Sin'Unchecked_Access, - Short_To_Network (C.unsigned_short (Server.Port))); + Set_Address (Sin'Unchecked_Access, Server, Len); return C_Connect (C.int (Socket), Sin'Address, Len); end Connect_Socket; @@ -861,12 +852,13 @@ procedure Create_Socket (Socket : out Socket_Type; Family : Family_Type := Family_Inet; - Mode : Mode_Type := Socket_Stream) + Mode : Mode_Type := Socket_Stream; + Level : Level_Type := IP_Protocol_For_IP_Level) is Res : C.int; begin - Res := C_Socket (Families (Family), Modes (Mode), 0); + Res := C_Socket (Families (Family), Modes (Mode), Levels (Level)); if Res = Failure then Raise_Socket_Error (Socket_Errno); @@ -875,6 +867,34 @@ Socket := Socket_Type (Res); end Create_Socket; + ------------------------ + -- Create_Socket_Pair -- + ------------------------ + + procedure Create_Socket_Pair + (Left : out Socket_Type; + Right : out Socket_Type; + Family : Family_Type := Family_Unspec; + Mode : Mode_Type := Socket_Stream; + Level : Level_Type := IP_Protocol_For_IP_Level) + is + Res : C.int; + Pair : aliased Thin_Common.Fd_Pair; + + begin + Res := C_Socketpair + ((if Family = Family_Unspec then Default_Socket_Pair_Family + else Families (Family)), + Modes (Mode), Levels (Level), Pair'Access); + + if Res = Failure then + Raise_Socket_Error (Socket_Errno); + end if; + + Left := Socket_Type (Pair (Pair'First)); + Right := Socket_Type (Pair (Pair'Last)); + end Create_Socket_Pair; + ----------- -- Empty -- ----------- @@ -959,6 +979,232 @@ end if; end Get_Address; + --------------------- + -- Raise_GAI_Error -- + --------------------- + + procedure Raise_GAI_Error (RC : C.int; Name : String) is + begin + if RC = SOSC.EAI_SYSTEM then + declare + Errcode : constant Integer := Socket_Errno; + begin + raise Host_Error with Err_Code_Image (Errcode) + & Dedot (Socket_Error_Message (Errcode)) & ": " & Name; + end; + else + raise Host_Error with Err_Code_Image (Integer (RC)) + & Dedot (CS.Value (C_GAI_Strerror (RC))) & ": " & Name; + end if; + end Raise_GAI_Error; + + ---------------------- + -- Get_Address_Info -- + ---------------------- + + function Get_Address_Info + (Host : String; + Service : String; + Family : Family_Type := Family_Unspec; + Mode : Mode_Type := Socket_Stream; + Level : Level_Type := IP_Protocol_For_IP_Level; + Numeric_Host : Boolean := False; + Passive : Boolean := False; + Unknown : access procedure + (Family, Mode, Level, Length : Integer) := null) + return Address_Info_Array + is + A : aliased Addrinfo_Access; + N : aliased C.char_array := C.To_C (Host); + S : aliased C.char_array := C.To_C (if Service = "" then "0" + else Service); + Hints : aliased constant Addrinfo := + (ai_family => Families (Family), + ai_socktype => Modes (Mode), + ai_protocol => Levels (Level), + ai_flags => (if Numeric_Host then SOSC.AI_NUMERICHOST else 0) + + (if Passive then SOSC.AI_PASSIVE else 0), + ai_addrlen => 0, + others => <>); + + R : C.int; + Iter : Addrinfo_Access; + Found : Boolean; + + function To_Array return Address_Info_Array; + -- Convert taken from OS addrinfo list A into Address_Info_Array + + -------------- + -- To_Array -- + -------------- + + function To_Array return Address_Info_Array is + Result : Address_Info_Array (1 .. 8); + + procedure Unsupported; + -- Calls Unknown callback if defiend + + ----------------- + -- Unsupported -- + ----------------- + + procedure Unsupported is + begin + if Unknown /= null then + Unknown + (Integer (Iter.ai_family), + Integer (Iter.ai_socktype), + Integer (Iter.ai_protocol), + Integer (Iter.ai_addrlen)); + end if; + end Unsupported; + + -- Start of processing for To_Array + + begin + for J in Result'Range loop + Look_For_Supported : loop + if Iter = null then + pragma Warnings + (Off, "may be referenced before it has a value"); + + return Result (1 .. J - 1); + + pragma Warnings + (On, "may be referenced before it has a value"); + end if; + + Result (J).Addr := + Get_Address (Iter.ai_addr.all, C.int (Iter.ai_addrlen)); + + if Result (J).Addr.Family = Family_Unspec then + Unsupported; + else + for M in Modes'Range loop + Found := False; + if Modes (M) = Iter.ai_socktype then + Result (J).Mode := M; + Found := True; + exit; + end if; + end loop; + + if Found then + for L in Levels'Range loop + if Levels (L) = Iter.ai_protocol then + Result (J).Level := L; + exit; + end if; + end loop; + + exit Look_For_Supported; + else + Unsupported; + end if; + end if; + + Iter := Iter.ai_next; + end loop Look_For_Supported; + + Iter := Iter.ai_next; + end loop; + + return Result & To_Array; + end To_Array; + + -- Start of processing for Get_Address_Info + + begin + R := C_Getaddrinfo + (Node => (if Host = "" then null else N'Unchecked_Access), + Service => S'Unchecked_Access, + Hints => Hints'Unchecked_Access, + Res => A'Access); + + if R /= 0 then + Raise_GAI_Error + (R, Host & (if Service = "" then "" else ':' & Service)); + end if; + + Iter := A; + + return Result : constant Address_Info_Array := To_Array do + C_Freeaddrinfo (A); + end return; + end Get_Address_Info; + + ---------- + -- Sort -- + ---------- + + procedure Sort + (Addr_Info : in out Address_Info_Array; + Compare : access function (Left, Right : Address_Info) return Boolean) + is + function Comp (Left, Right : Address_Info) return Boolean is + (Compare (Left, Right)); + procedure Sorter is new Ada.Containers.Generic_Array_Sort + (Positive, Address_Info, Address_Info_Array, Comp); + begin + Sorter (Addr_Info); + end Sort; + + ------------------------ + -- IPv6_TCP_Preferred -- + ------------------------ + + function IPv6_TCP_Preferred (Left, Right : Address_Info) return Boolean is + begin + pragma Assert (Family_Inet < Family_Inet6); + -- To be sure that Family_Type enumeration has appropriate elements + -- order + + if Left.Addr.Family /= Right.Addr.Family then + return Left.Addr.Family > Right.Addr.Family; + end if; + + pragma Assert (Socket_Stream < Socket_Datagram); + -- To be sure that Mode_Type enumeration has appropriate elements order + + return Left.Mode < Right.Mode; + end IPv6_TCP_Preferred; + + ------------------- + -- Get_Name_Info -- + ------------------- + + function Get_Name_Info + (Addr : Sock_Addr_Type; + Numeric_Host : Boolean := False; + Numeric_Serv : Boolean := False) return Host_Service + is + SA : aliased Sockaddr; + H : aliased C.char_array := (1 .. SOSC.NI_MAXHOST => C.nul); + S : aliased C.char_array := (1 .. SOSC.NI_MAXSERV => C.nul); + RC : C.int; + Len : C.int; + begin + Set_Address (SA'Unchecked_Access, Addr, Len); + + RC := C_Getnameinfo + (SA'Unchecked_Access, socklen_t (Len), + H'Unchecked_Access, H'Length, + S'Unchecked_Access, S'Length, + (if Numeric_Host then SOSC.NI_NUMERICHOST else 0) + + (if Numeric_Serv then SOSC.NI_NUMERICSERV else 0)); + + if RC /= 0 then + Raise_GAI_Error (RC, Image (Addr)); + end if; + + declare + HR : constant String := C.To_Ada (H); + SR : constant String := C.To_Ada (S); + begin + return (HR'Length, SR'Length, HR, SR); + end; + end Get_Name_Info; + ------------------------- -- Get_Host_By_Address -- ------------------------- @@ -969,17 +1215,29 @@ is pragma Unreferenced (Family); - HA : aliased In_Addr := To_In_Addr (Address); + HA : aliased In_Addr_Union (Address.Family); Buflen : constant C.int := Netdb_Buffer_Size; Buf : aliased C.char_array (1 .. Netdb_Buffer_Size); Res : aliased Hostent; Err : aliased C.int; begin + case Address.Family is + when Family_Inet => + HA.In4 := To_In_Addr (Address); + when Family_Inet6 => + HA.In6 := To_In6_Addr (Address); + end case; + Netdb_Lock; - if C_Gethostbyaddr (HA'Address, HA'Size / 8, SOSC.AF_INET, - Res'Access, Buf'Address, Buflen, Err'Access) /= 0 + if C_Gethostbyaddr + (HA'Address, + (case Address.Family is + when Family_Inet => HA.In4'Size, + when Family_Inet6 => HA.In6'Size) / 8, + Families (Address.Family), + Res'Access, Buf'Address, Buflen, Err'Access) /= 0 then Netdb_Unlock; Raise_Host_Error (Integer (Err), Image (Address)); @@ -1007,7 +1265,7 @@ -- If the given name actually is the string representation of -- an IP address, use Get_Host_By_Address instead. - if Is_IP_Address (Name) then + if Is_IPv4_Address (Name) or else Is_IPv6_Address (Name) then return Get_Host_By_Address (Inet_Addr (Name)); end if; @@ -1041,19 +1299,14 @@ ------------------- function Get_Peer_Name (Socket : Socket_Type) return Sock_Addr_Type is - Sin : aliased Sockaddr_In; + Sin : aliased Sockaddr; Len : aliased C.int := Sin'Size / 8; - Res : Sock_Addr_Type (Family_Inet); - begin if C_Getpeername (C.int (Socket), Sin'Address, Len'Access) = Failure then Raise_Socket_Error (Socket_Errno); end if; - To_Inet_Addr (Sin.Sin_Addr, Res.Addr); - Res.Port := Port_Type (Network_To_Short (Sin.Sin_Port)); - - return Res; + return Get_Address (Sin, Len); end Get_Peer_Name; ------------------------- @@ -1127,20 +1380,17 @@ function Get_Socket_Name (Socket : Socket_Type) return Sock_Addr_Type is - Sin : aliased Sockaddr_In; - Len : aliased C.int := Sin'Size / 8; - Res : C.int; - Addr : Sock_Addr_Type := No_Sock_Addr; - + Sin : aliased Sockaddr; + Len : aliased C.int := Sin'Size / 8; + Res : C.int; begin Res := C_Getsockname (C.int (Socket), Sin'Address, Len'Access); - if Res /= Failure then - To_Inet_Addr (Sin.Sin_Addr, Addr.Addr); - Addr.Port := Port_Type (Network_To_Short (Sin.Sin_Port)); + if Res = Failure then + return No_Sock_Addr; end if; - return Addr; + return Get_Address (Sin, Len); end Get_Socket_Name; ----------------------- @@ -1149,15 +1399,16 @@ function Get_Socket_Option (Socket : Socket_Type; - Level : Level_Type := Socket_Level; + Level : Level_Type; Name : Option_Name; Optname : Interfaces.C.int := -1) return Option_Type is - use SOSC; + use type C.unsigned; use type C.unsigned_char; V8 : aliased Two_Ints; V4 : aliased C.int; + U4 : aliased C.unsigned; V1 : aliased C.unsigned_char; VT : aliased Timeval; Len : aliased C.int; @@ -1178,8 +1429,7 @@ end if; case Name is - when Multicast_Loop - | Multicast_TTL + when Multicast_TTL | Receive_Packet_Info => Len := V1'Size / 8; @@ -1190,11 +1440,16 @@ | Error | Generic_Option | Keep_Alive - | Multicast_If + | Multicast_If_V4 + | Multicast_If_V6 + | Multicast_Loop_V4 + | Multicast_Loop_V6 + | Multicast_Hops | No_Delay | Receive_Buffer | Reuse_Address | Send_Buffer + | IPv6_Only => Len := V4'Size / 8; Add := V4'Address; @@ -1206,18 +1461,23 @@ -- struct timeval, but on Windows it is a milliseconds count in -- a DWORD. - if Target_OS = Windows then - Len := V4'Size / 8; - Add := V4'Address; - + if Is_Windows then + Len := U4'Size / 8; + Add := U4'Address; else Len := VT'Size / 8; Add := VT'Address; end if; - when Add_Membership - | Drop_Membership - | Linger + when Add_Membership_V4 + | Add_Membership_V6 + | Drop_Membership_V4 + | Drop_Membership_V6 + => + raise Socket_Error with + "Add/Drop membership valid only for Set_Socket_Option"; + + when Linger => Len := V8'Size / 8; Add := V8'Address; @@ -1243,6 +1503,9 @@ | Keep_Alive | No_Delay | Reuse_Address + | Multicast_Loop_V4 + | Multicast_Loop_V6 + | IPv6_Only => Opt.Enabled := (V4 /= 0); @@ -1261,35 +1524,43 @@ when Error => Opt.Error := Resolve_Error (Integer (V4)); - when Add_Membership - | Drop_Membership + when Add_Membership_V4 + | Add_Membership_V6 + | Drop_Membership_V4 + | Drop_Membership_V6 => - To_Inet_Addr (To_In_Addr (V8 (V8'First)), Opt.Multicast_Address); - To_Inet_Addr (To_In_Addr (V8 (V8'Last)), Opt.Local_Interface); - - when Multicast_If => + -- No way to be here. Exception raised in the first case Name + -- expression. + null; + + when Multicast_If_V4 => To_Inet_Addr (To_In_Addr (V4), Opt.Outgoing_If); + when Multicast_If_V6 => + Opt.Outgoing_If_Index := Natural (V4); + when Multicast_TTL => Opt.Time_To_Live := Integer (V1); - when Multicast_Loop - | Receive_Packet_Info + when Multicast_Hops => + Opt.Hop_Limit := Integer (V4); + + when Receive_Packet_Info => Opt.Enabled := (V1 /= 0); when Receive_Timeout | Send_Timeout => - if Target_OS = Windows then + if Is_Windows then -- Timeout is in milliseconds, actual value is 500 ms + -- returned value (unless it is 0). - if V4 = 0 then + if U4 = 0 then Opt.Timeout := 0.0; else - Opt.Timeout := Natural (V4) * 0.001 + 0.500; + Opt.Timeout := Duration (U4) / 1000 + 0.500; end if; else @@ -1322,78 +1593,31 @@ -- Image -- ----------- - function Image - (Val : Inet_Addr_Bytes; - Hex : Boolean := False) return String - is - -- The largest Inet_Addr_Comp_Type image occurs with IPv4. It - -- has at most a length of 3 plus one '.' character. - - Buffer : String (1 .. 4 * Val'Length); - Length : Natural := 1; - Separator : Character; - - procedure Img10 (V : Inet_Addr_Comp_Type); - -- Append to Buffer image of V in decimal format - - procedure Img16 (V : Inet_Addr_Comp_Type); - -- Append to Buffer image of V in hexadecimal format - - ----------- - -- Img10 -- - ----------- - - procedure Img10 (V : Inet_Addr_Comp_Type) is - Img : constant String := V'Img; - Len : constant Natural := Img'Length - 1; - begin - Buffer (Length .. Length + Len - 1) := Img (2 .. Img'Last); - Length := Length + Len; - end Img10; - - ----------- - -- Img16 -- - ----------- - - procedure Img16 (V : Inet_Addr_Comp_Type) is - begin - Buffer (Length) := Hex_To_Char (Natural (V / 16) + 1); - Buffer (Length + 1) := Hex_To_Char (Natural (V mod 16) + 1); - Length := Length + 2; - end Img16; - - -- Start of processing for Image - + function Image (Value : Inet_Addr_Type) return String is + use type CS.char_array_access; + Size : constant socklen_t := + (case Value.Family is + when Family_Inet => 4 * Value.Sin_V4'Length, + when Family_Inet6 => 6 * 5 + 4 * 4); + -- 1234:1234:1234:1234:1234:1234:123.123.123.123 + Dst : aliased C.char_array := (1 .. C.size_t (Size) => C.nul); + Ia : aliased In_Addr_Union (Value.Family); begin - Separator := (if Hex then ':' else '.'); - - for J in Val'Range loop - if Hex then - Img16 (Val (J)); - else - Img10 (Val (J)); - end if; - - if J /= Val'Last then - Buffer (Length) := Separator; - Length := Length + 1; - end if; - end loop; - - return Buffer (1 .. Length - 1); - end Image; - - ----------- - -- Image -- - ----------- - - function Image (Value : Inet_Addr_Type) return String is - begin - if Value.Family = Family_Inet then - return Image (Inet_Addr_Bytes (Value.Sin_V4), Hex => False); - else - return Image (Inet_Addr_Bytes (Value.Sin_V6), Hex => True); + case Value.Family is + when Family_Inet6 => + Ia.In6 := To_In6_Addr (Value); + when Family_Inet => + Ia.In4 := To_In_Addr (Value); + end case; + + if Inet_Ntop + (Families (Value.Family), Ia'Address, + Dst'Unchecked_Access, Size) = null + then + Raise_Socket_Error (Socket_Errno); end if; + + return C.To_Ada (Dst); end Image; ----------- @@ -1401,9 +1625,30 @@ ----------- function Image (Value : Sock_Addr_Type) return String is - Port : constant String := Value.Port'Img; + function Ipv6_Brackets (S : String) return String is + (if Value.Family = Family_Inet6 then "[" & S & "]" else S); begin - return Image (Value.Addr) & ':' & Port (2 .. Port'Last); + case Value.Family is + when Family_Unix => + if ASU.Length (Value.Name) > 0 + and then ASU.Element (Value.Name, 1) = ASCII.NUL + then + return '@' & ASU.Slice (Value.Name, 2, ASU.Length (Value.Name)); + else + return ASU.To_String (Value.Name); + end if; + + when Family_Inet_4_6 => + declare + Port : constant String := Value.Port'Img; + begin + return Ipv6_Brackets (Image (Value.Addr)) & ':' + & Port (2 .. Port'Last); + end; + + when Family_Unspec => + return ""; + end case; end Image; ----------- @@ -1454,10 +1699,11 @@ use Interfaces.C; Img : aliased char_array := To_C (Image); - Addr : aliased C.int; Res : C.int; Result : Inet_Addr_Type; - + IPv6 : constant Boolean := Is_IPv6_Address (Image); + Ia : aliased In_Addr_Union + (if IPv6 then Family_Inet6 else Family_Inet); begin -- Special case for an empty Image as on some platforms (e.g. Windows) -- calling Inet_Addr("") will not return an error. @@ -1466,7 +1712,9 @@ Raise_Socket_Error (SOSC.EINVAL); end if; - Res := Inet_Pton (SOSC.AF_INET, Img'Address, Addr'Address); + Res := Inet_Pton + ((if IPv6 then SOSC.AF_INET6 else SOSC.AF_INET), Img'Address, + Ia'Address); if Res < 0 then Raise_Socket_Error (Socket_Errno); @@ -1475,7 +1723,12 @@ Raise_Socket_Error (SOSC.EINVAL); end if; - To_Inet_Addr (To_In_Addr (Addr), Result); + if IPv6 then + To_Inet_Addr (Ia.In6, Result); + else + To_Inet_Addr (Ia.In4, Result); + end if; + return Result; end Inet_Addr; @@ -1525,6 +1778,16 @@ null; end Initialize; + ---------------- + -- Is_Windows -- + ---------------- + + function Is_Windows return Boolean is + use SOSC; + begin + return Target_OS = Windows; + end Is_Windows; + -------------- -- Is_Empty -- -------------- @@ -1534,11 +1797,56 @@ return Item.Last = No_Socket; end Is_Empty; - ------------------- - -- Is_IP_Address -- - ------------------- - - function Is_IP_Address (Name : String) return Boolean is + --------------------- + -- Is_IPv6_Address -- + --------------------- + + function Is_IPv6_Address (Name : String) return Boolean is + Prev_Colon : Natural := 0; + Double_Colon : Boolean := False; + Colons : Natural := 0; + begin + for J in Name'Range loop + if Name (J) = ':' then + Colons := Colons + 1; + + if Prev_Colon > 0 and then J = Prev_Colon + 1 then + if Double_Colon then + -- Only one double colon allowed + return False; + end if; + + Double_Colon := True; + + elsif J = Name'Last then + -- Single colon at the end is not allowed + return False; + end if; + + Prev_Colon := J; + + elsif Prev_Colon = Name'First then + -- Single colon at start is not allowed + return False; + + elsif Name (J) = '.' then + return Prev_Colon > 0 + and then Is_IPv4_Address (Name (Prev_Colon + 1 .. Name'Last)); + + elsif Name (J) not in '0' .. '9' | 'A' .. 'F' | 'a' .. 'f' then + return False; + + end if; + end loop; + + return Colons in 2 .. 8; + end Is_IPv6_Address; + + --------------------- + -- Is_IPv4_Address -- + --------------------- + + function Is_IPv4_Address (Name : String) return Boolean is Dots : Natural := 0; begin @@ -1569,7 +1877,7 @@ end loop; return Dots in 1 .. 3; - end Is_IP_Address; + end Is_IPv4_Address; ------------- -- Is_Open -- @@ -1658,6 +1966,19 @@ end if; end Netdb_Unlock; + ---------------------------- + -- Network_Socket_Address -- + ---------------------------- + + function Network_Socket_Address + (Addr : Inet_Addr_Type; Port : Port_Type) return Sock_Addr_Type is + begin + return Result : Sock_Addr_Type (Addr.Family) do + Result.Addr := Addr; + Result.Port := Port; + end return; + end Network_Socket_Address; + -------------------------------- -- Normalize_Empty_Socket_Set -- -------------------------------- @@ -1758,13 +2079,6 @@ ---------------------- procedure Raise_Host_Error (H_Error : Integer; Name : String) is - function Dedot (Value : String) return String is - (if Value /= "" and then Value (Value'Last) = '.' then - Value (Value'First .. Value'Last - 1) - else - Value); - -- Removes dot at the end of error message - begin raise Host_Error with Err_Code_Image (H_Error) @@ -1861,7 +2175,7 @@ Flags : Request_Flag_Type := No_Request_Flag) is Res : C.int; - Sin : aliased Sockaddr_In; + Sin : aliased Sockaddr; Len : aliased C.int := Sin'Size / 8; begin @@ -1880,8 +2194,7 @@ Last := Last_Index (First => Item'First, Count => size_t (Res)); - To_Inet_Addr (Sin.Sin_Addr, From.Addr); - From.Port := Port_Type (Network_To_Short (Sin.Sin_Port)); + From := Get_Address (Sin, Len); end Receive_Socket; -------------------- @@ -2140,19 +2453,14 @@ is Res : C.int; - Sin : aliased Sockaddr_In; + Sin : aliased Sockaddr; C_To : System.Address; Len : C.int; begin if To /= null then - Set_Family (Sin.Sin_Family, To.Family); - Set_Address (Sin'Unchecked_Access, To_In_Addr (To.Addr)); - Set_Port - (Sin'Unchecked_Access, - Short_To_Network (C.unsigned_short (To.Port))); + Set_Address (Sin'Unchecked_Access, To.all, Len); C_To := Sin'Address; - Len := Sin'Size / 8; else C_To := System.Null_Address; @@ -2289,13 +2597,15 @@ procedure Set_Socket_Option (Socket : Socket_Type; - Level : Level_Type := Socket_Level; + Level : Level_Type; Option : Option_Type) is - use SOSC; - + use type C.unsigned; + + MR : aliased IPV6_Mreq; V8 : aliased Two_Ints; V4 : aliased C.int; + U4 : aliased C.unsigned; V1 : aliased C.unsigned_char; VT : aliased Timeval; Len : C.int; @@ -2314,6 +2624,9 @@ | Keep_Alive | No_Delay | Reuse_Address + | Multicast_Loop_V4 + | Multicast_Loop_V6 + | IPv6_Only => V4 := C.int (Boolean'Pos (Option.Enabled)); Len := V4'Size / 8; @@ -2342,26 +2655,42 @@ Len := V4'Size / 8; Add := V4'Address; - when Add_Membership - | Drop_Membership + when Add_Membership_V4 + | Drop_Membership_V4 => V8 (V8'First) := To_Int (To_In_Addr (Option.Multicast_Address)); V8 (V8'Last) := To_Int (To_In_Addr (Option.Local_Interface)); Len := V8'Size / 8; Add := V8'Address; - when Multicast_If => + when Add_Membership_V6 + | Drop_Membership_V6 => + MR.ipv6mr_multiaddr := To_In6_Addr (Option.Multicast_Address); + MR.ipv6mr_interface := C.unsigned (Option.Interface_Index); + Len := MR'Size / 8; + Add := MR'Address; + + when Multicast_If_V4 => V4 := To_Int (To_In_Addr (Option.Outgoing_If)); Len := V4'Size / 8; Add := V4'Address; + when Multicast_If_V6 => + V4 := C.int (Option.Outgoing_If_Index); + Len := V4'Size / 8; + Add := V4'Address; + when Multicast_TTL => V1 := C.unsigned_char (Option.Time_To_Live); Len := V1'Size / 8; Add := V1'Address; - when Multicast_Loop - | Receive_Packet_Info + when Multicast_Hops => + V4 := C.int (Option.Hop_Limit); + Len := V4'Size / 8; + Add := V4'Address; + + when Receive_Packet_Info => V1 := C.unsigned_char (Boolean'Pos (Option.Enabled)); Len := V1'Size / 8; @@ -2370,23 +2699,31 @@ when Receive_Timeout | Send_Timeout => - if Target_OS = Windows then - - -- On Windows, the timeout is a DWORD in milliseconds, and - -- the actual timeout is 500 ms + the given value (unless it - -- is 0). - - V4 := C.int (Option.Timeout / 0.001); - - if V4 > 500 then - V4 := V4 - 500; - - elsif V4 > 0 then - V4 := 1; + if Is_Windows then + + -- On Windows, the timeout is a DWORD in milliseconds + + Len := U4'Size / 8; + Add := U4'Address; + + U4 := C.unsigned (Option.Timeout / 0.001); + + if Option.Timeout > 0.0 and then U4 = 0 then + -- Avoid round to zero. Zero timeout mean unlimited. + U4 := 1; end if; - Len := V4'Size / 8; - Add := V4'Address; + -- Old windows versions actual timeout is 500 ms + the given + -- value (unless it is 0). + + if Minus_500ms_Windows_Timeout /= 0 then + if U4 > 500 then + U4 := U4 - 500; + + elsif U4 > 0 then + U4 := 1; + end if; + end if; else VT := To_Timeval (Option.Timeout); @@ -2416,28 +2753,6 @@ end if; end Set_Socket_Option; - ---------------------- - -- Short_To_Network -- - ---------------------- - - function Short_To_Network (S : C.unsigned_short) return C.unsigned_short is - use type C.unsigned_short; - - begin - -- Big-endian case. No conversion needed. On these platforms, htons() - -- defaults to a null procedure. - - if Default_Bit_Order = High_Order_First then - return S; - - -- Little-endian case. We must swap the high and low bytes of this - -- short to make the port number network compliant. - - else - return (S / 256) + (S mod 256) * 256; - end if; - end Short_To_Network; - --------------------- -- Shutdown_Socket -- --------------------- @@ -2509,8 +2824,24 @@ ----------------- function To_Duration (Val : Timeval) return Timeval_Duration is + Max_D : constant Long_Long_Integer := Long_Long_Integer (Forever - 0.5); + Tv_sec_64 : constant Boolean := SOSC.SIZEOF_tv_sec = 8; + -- Need to separate this condition into the constant declaration to + -- avoid GNAT warning about "always true" or "always false". begin - return Natural (Val.Tv_Sec) * 1.0 + Natural (Val.Tv_Usec) * 1.0E-6; + if Tv_sec_64 then + -- Check for possible Duration overflow when Tv_Sec field is 64 bit + -- integer. + + if Val.Tv_Sec > time_t (Max_D) or else + (Val.Tv_Sec = time_t (Max_D) and then + Val.Tv_Usec > suseconds_t ((Forever - Duration (Max_D)) * 1E6)) + then + return Forever; + end if; + end if; + + return Duration (Val.Tv_Sec) + Duration (Val.Tv_Usec) * 1.0E-6; end To_Duration; ------------------- @@ -2518,15 +2849,18 @@ ------------------- function To_Host_Entry (E : Hostent_Access) return Host_Entry_Type is - use type C.size_t; - Aliases_Count, Addresses_Count : Natural; - -- H_Length is not used because it is currently only ever set to 4, as - -- we only handle the case of H_Addrtype being AF_INET. + Family : constant Family_Type := + (case Hostent_H_Addrtype (E) is + when SOSC.AF_INET => Family_Inet, + when SOSC.AF_INET6 => Family_Inet6, + when others => Family_Unspec); + + Addr_Len : constant C.size_t := C.size_t (Hostent_H_Length (E)); begin - if Hostent_H_Addrtype (E) /= SOSC.AF_INET then + if Family = Family_Unspec then Raise_Socket_Error (SOSC.EPFNOSUPPORT); end if; @@ -2554,61 +2888,35 @@ for J in Result.Addresses'Range loop declare - Addr : In_Addr; + Ia : In_Addr_Union (Family); -- Hostent_H_Addr (E, <index>) may return an address that is -- not correctly aligned for In_Addr, so we need to use -- an intermediate copy operation on a type with an alignment -- of 1 to recover the value. - subtype Addr_Buf_T is C.char_array (1 .. Addr'Size / 8); + subtype Addr_Buf_T is C.char_array (1 .. Addr_Len); Unaligned_Addr : Addr_Buf_T; for Unaligned_Addr'Address use Hostent_H_Addr (E, C.int (J - Result.Addresses'First)); pragma Import (Ada, Unaligned_Addr); Aligned_Addr : Addr_Buf_T; - for Aligned_Addr'Address use Addr'Address; + for Aligned_Addr'Address use Ia'Address; pragma Import (Ada, Aligned_Addr); begin Aligned_Addr := Unaligned_Addr; - To_Inet_Addr (Addr, Result.Addresses (J)); + if Family = Family_Inet6 then + To_Inet_Addr (Ia.In6, Result.Addresses (J)); + else + To_Inet_Addr (Ia.In4, Result.Addresses (J)); + end if; end; end loop; end return; end To_Host_Entry; - ---------------- - -- To_In_Addr -- - ---------------- - - function To_In_Addr (Addr : Inet_Addr_Type) return In_Addr is - begin - if Addr.Family = Family_Inet then - return (S_B1 => C.unsigned_char (Addr.Sin_V4 (1)), - S_B2 => C.unsigned_char (Addr.Sin_V4 (2)), - S_B3 => C.unsigned_char (Addr.Sin_V4 (3)), - S_B4 => C.unsigned_char (Addr.Sin_V4 (4))); - end if; - - raise Socket_Error with "IPv6 not supported"; - end To_In_Addr; - - ------------------ - -- To_Inet_Addr -- - ------------------ - - procedure To_Inet_Addr - (Addr : In_Addr; - Result : out Inet_Addr_Type) is - begin - Result.Sin_V4 (1) := Inet_Addr_Comp_Type (Addr.S_B1); - Result.Sin_V4 (2) := Inet_Addr_Comp_Type (Addr.S_B2); - Result.Sin_V4 (3) := Inet_Addr_Comp_Type (Addr.S_B3); - Result.Sin_V4 (4) := Inet_Addr_Comp_Type (Addr.S_B4); - end To_Inet_Addr; - ------------ -- To_Int -- ------------ @@ -2701,7 +3009,12 @@ else S := time_t (Val - 0.5); - uS := suseconds_t (1_000_000 * (Val - Selector_Duration (S))); + uS := suseconds_t (1_000_000 * (Val - Selector_Duration (S)) - 0.5); + + if uS = -1 then + -- It happen on integer duration + uS := 0; + end if; end if; return (S, uS); @@ -2796,7 +3109,7 @@ -------------------- function Create_Address - (Family : Family_Type; Bytes : Inet_Addr_Bytes) return Inet_Addr_Type + (Family : Family_Inet_4_6; Bytes : Inet_Addr_Bytes) return Inet_Addr_Type is (case Family is when Family_Inet => (Family_Inet, Bytes), @@ -2816,7 +3129,7 @@ ---------- function Mask - (Family : Family_Type; + (Family : Family_Inet_4_6; Length : Natural; Host : Boolean := False) return Inet_Addr_Type is @@ -2848,6 +3161,15 @@ end; end Mask; + ------------------------- + -- Unix_Socket_Address -- + ------------------------- + + function Unix_Socket_Address (Addr : String) return Sock_Addr_Type is + begin + return Sock_Addr_Type'(Family_Unix, ASU.To_Unbounded_String (Addr)); + end Unix_Socket_Address; + ----------- -- "and" -- -----------