+2019-09-17 Dmitriy Anisimkov <anisimko@adacore.com>
+
+ * gsocket.h: Include sys/un.h.
+ * s-oscons-tmplt.c (AF_UNIX): New constant generation.
+ (SIZEOF_sockaddr_un): Idem.
+ * libgnat/g-socket.ads (Family_Type): New value Family_Unix
+ added.
+ (Family_Inet_4_6): New subtype only for network families.
+ (Sock_Addr_Type): Add Unbounded_String field for Family_Unix
+ variant.
+ (Unix_Socket_Address): Create Sock_Addr_Type from socket
+ pathname.
+ (Network_Socket_Address): Create Sock_Addr_Type from
+ Inet_Addr_Type and Port_Type parameters.
+ * libgnat/g-socket.adb: Support local unix address in socket
+ routines.
+ (Get_Address_Info): Disable warning about Result may be
+ referenced before it has a value. Remove duplicated code to exit
+ from Look_For_Supported.
+ * libgnat/g-sothco.ads (Unix_Name_Length): New constant defining
+ maximum number of characters in local socket address path.
+ (Sockaddr): Add variant for Family_Unix address family. Move
+ Sin_Port and Sin_Family to Family_Inet section. Add Sin6_Port
+ and Sin6_Family to Family_Inet6 section.
+ (Set_Address): Add out parameter Length to return valuable
+ Sockaddr data length.
+ (Get_Address): Add input parameter Length to set valuable
+ Sockaddr data length.
+ * libgnat/g-sothco.adb: Support local unix address in socket
+ routines.
+
2019-09-17 Eric Botcazou <ebotcazou@adacore.com>
* exp_attr.adb (Expand_Size_Attribute): Chain the special cases
*/
#if !(defined (VMS) || defined (__MINGW32__))
#include <sys/socket.h>
+#include <sys/un.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <sys/ioctl.h>
Socket_Error_Id : constant Exception_Id := Socket_Error'Identity;
Host_Error_Id : constant Exception_Id := Host_Error'Identity;
- type In_Addr_Union (Family : Family_Type) is record
+ 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;
- when Family_Unspec =>
- null;
end case;
end record with Unchecked_Union;
-- 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.
end if;
Socket := Socket_Type (Res);
- Address := Get_Address (Sin);
+ Address := Get_Address (Sin, Len);
end Accept_Socket;
-------------------
is
Res : C.int;
Sin : aliased Sockaddr;
+ Len : C.int;
begin
- Set_Address (Sin'Unchecked_Access, Address);
+ Set_Address (Sin'Unchecked_Access, Address, Len);
- Res := C_Bind
- (C.int (Socket), Sin'Address, C.int (Lengths (Address.Family)));
+ Res := C_Bind (C.int (Socket), Sin'Address, Len);
if Res = Failure then
Raise_Socket_Error (Socket_Errno);
Server : Sock_Addr_Type) return C.int
is
Sin : aliased Sockaddr;
+ Len : C.int;
begin
- Set_Address (Sin'Unchecked_Access, Server);
+ Set_Address (Sin'Unchecked_Access, Server, Len);
- return C_Connect
- (C.int (Socket), Sin'Address, C.int (Lengths (Server.Family)));
+ return C_Connect (C.int (Socket), Sin'Address, Len);
end Connect_Socket;
procedure Connect_Socket
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);
+ Result (J).Addr :=
+ Get_Address (Iter.ai_addr.all, C.int (Iter.ai_addrlen));
if Result (J).Addr.Family = Family_Unspec then
Unsupported;
end if;
Iter := Iter.ai_next;
-
- if Iter = null then
- return Result (1 .. J - 1);
- end if;
end loop Look_For_Supported;
Iter := Iter.ai_next;
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;
+ 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);
+ Set_Address (SA'Unchecked_Access, Addr, Len);
RC := C_Getnameinfo
- (SA'Unchecked_Access, socklen_t (Lengths (Addr.Family)),
+ (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) +
HA.In4 := To_In_Addr (Address);
when Family_Inet6 =>
HA.In6 := To_In6_Addr (Address);
- when Family_Unspec =>
- return (0, 0, (1, " "), (1 .. 0 => (1, " ")),
- (1 .. 0 => No_Inet_Addr));
end case;
Netdb_Lock;
(HA'Address,
(case Address.Family is
when Family_Inet => HA.In4'Size,
- when Family_Inet6 => HA.In6'Size,
- when Family_Unspec => 0) / 8,
+ when Family_Inet6 => HA.In6'Size) / 8,
Families (Address.Family),
Res'Access, Buf'Address, Buflen, Err'Access) /= 0
then
Raise_Socket_Error (Socket_Errno);
end if;
- return Get_Address (Sin);
+ return Get_Address (Sin, Len);
end Get_Peer_Name;
-------------------------
return No_Sock_Addr;
end if;
- return Get_Address (Sin);
+ return Get_Address (Sin, Len);
end Get_Socket_Name;
-----------------------
Size : constant socklen_t :=
(case Value.Family is
when Family_Inet => 4 * Value.Sin_V4'Length,
- when Family_Inet6 => 6 * 5 + 4 * 4,
+ when Family_Inet6 => 6 * 5 + 4 * 4);
-- 1234:1234:1234:1234:1234:1234:123.123.123.123
- when Family_Unspec => 0);
Dst : aliased C.char_array := (1 .. C.size_t (Size) => C.nul);
Ia : aliased In_Addr_Union (Value.Family);
begin
Ia.In6 := To_In6_Addr (Value);
when Family_Inet =>
Ia.In4 := To_In_Addr (Value);
- when Family_Unspec =>
- return "";
end case;
if Inet_Ntop
-----------
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 Ipv6_Brackets (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;
-----------
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 --
--------------------------------
Last := Last_Index (First => Item'First, Count => size_t (Res));
- From := Get_Address (Sin);
+ From := Get_Address (Sin, Len);
end Receive_Socket;
--------------------
begin
if To /= null then
- Set_Address (Sin'Unchecked_Access, To.all);
+ Set_Address (Sin'Unchecked_Access, To.all, Len);
C_To := Sin'Address;
- Len := C.int (Thin_Common.Lengths (To.Family));
else
C_To := System.Null_Address;
--------------------
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),
- when Family_Inet6 => (Family_Inet6, Bytes),
- when Family_Unspec => (Family => Family_Unspec));
+ when Family_Inet6 => (Family_Inet6, Bytes));
---------------
-- Get_Bytes --
function Get_Bytes (Addr : Inet_Addr_Type) return Inet_Addr_Bytes is
(case Addr.Family is
when Family_Inet => Addr.Sin_V4,
- when Family_Inet6 => Addr.Sin_V6,
- when Family_Unspec => (1 .. 0 => 0));
+ when Family_Inet6 => Addr.Sin_V6);
----------
-- Mask --
----------
function Mask
- (Family : Family_Type;
+ (Family : Family_Inet_4_6;
Length : Natural;
Host : Boolean := False) return Inet_Addr_Type
is
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" --
-----------
with Ada.Exceptions;
with Ada.Streams;
+with Ada.Strings.Unbounded;
with Ada.Unchecked_Deallocation;
with Interfaces.C;
-- Return a file descriptor to be used by external subprograms. This is
-- useful for C functions that are not yet interfaced in this package.
- type Family_Type is (Family_Inet, Family_Inet6, Family_Unspec);
+ type Family_Type is (Family_Inet, Family_Inet6, Family_Unix, Family_Unspec);
-- Address family (or protocol family) identifies the communication domain
-- and groups protocols with similar address formats.
-- The order of the enumeration elements should not be changed unilaterally
-- because the IPv6_TCP_Preferred routine rely on it.
+ subtype Family_Inet_4_6 is Family_Type range Family_Inet .. Family_Inet6;
+
type Mode_Type is (Socket_Stream, Socket_Datagram, Socket_Raw);
-- Stream sockets provide connection-oriented byte streams. Datagram
-- sockets support unreliable connectionless message-based communication.
type Inet_Addr_Comp_Type is mod 2 ** 8;
-- Octet for Internet address
- Inet_Addr_Bytes_Length : constant array (Family_Type) of Natural :=
- (Family_Inet => 4, Family_Inet6 => 16, Family_Unspec => 0);
+ Inet_Addr_Bytes_Length : constant array (Family_Inet_4_6) of Natural :=
+ (Family_Inet => 4, Family_Inet6 => 16);
type Inet_Addr_Bytes is array (Natural range <>) of Inet_Addr_Comp_Type;
subtype Inet_Addr_VN_Type is Inet_Addr_Bytes;
-- For backwards compatibility
- type Inet_Addr_Type (Family : Family_Type := Family_Inet) is record
+ type Inet_Addr_Type (Family : Family_Inet_4_6 := Family_Inet) is record
case Family is
when Family_Inet =>
Sin_V4 : Inet_Addr_V4_Type := (others => 0);
when Family_Inet6 =>
Sin_V6 : Inet_Addr_V6_Type := (others => 0);
- when Family_Unspec =>
- null;
-
end case;
end record;
No_Inet_Addr : constant Inet_Addr_Type;
-- Uninitialized inet address
- Unspecified_Addr : constant Inet_Addr_Type;
- -- Unspecified address. Unlike of No_Inet_Addr the constraint is
- -- Family_Unspec for this constant.
-
Broadcast_Inet_Addr : constant Inet_Addr_Type;
-- Broadcast destination address in the current network
-- Functions to handle masks and prefixes
function Mask
- (Family : Family_Type;
+ (Family : Family_Inet_4_6;
Length : Natural;
Host : Boolean := False) return Inet_Addr_Type;
-- Return an address mask of the given family with the given prefix length.
-- same address family).
type Sock_Addr_Type (Family : Family_Type := Family_Inet) is record
- Addr : Inet_Addr_Type (Family);
- Port : Port_Type;
+ case Family is
+ when Family_Unix =>
+ Name : Ada.Strings.Unbounded.Unbounded_String;
+ when Family_Inet_4_6 =>
+ Addr : Inet_Addr_Type (Family);
+ Port : Port_Type;
+ when Family_Unspec =>
+ null;
+ end case;
end record;
pragma No_Component_Reordering (Sock_Addr_Type);
-- Socket addresses fully define a socket connection with protocol family,
-- 8 hextets in hexadecimal format separated by colons.
function Image (Value : Sock_Addr_Type) return String;
- -- Return inet address image and port image separated by a colon
+ -- Return socket address image. Network socket address image will be with
+ -- a port image separated by a colon.
function Inet_Addr (Image : String) return Inet_Addr_Type;
-- Convert address image from numbers-dots-and-colons notation into an
-- inet address.
+ function Unix_Socket_Address (Addr : String) return Sock_Addr_Type;
+ -- Convert unix local socket name to Sock_Addr_Type
+
+ function Network_Socket_Address
+ (Addr : Inet_Addr_Type; Port : Port_Type) return Sock_Addr_Type;
+ -- Create network socket address
+
-- Host entries provide complete information on a given host: the official
-- name, an array of alternative names or aliases and array of network
-- addresses.
private
+ package ASU renames Ada.Strings.Unbounded;
+
type Socket_Type is new Integer;
No_Socket : constant Socket_Type := -1;
(Family_Inet6, (others => 0));
No_Inet_Addr : constant Inet_Addr_Type :=
(Family_Inet, (others => 0));
- Unspecified_Addr : constant Inet_Addr_Type :=
- (Family => Family_Unspec);
Broadcast_Inet_Addr : constant Inet_Addr_Type :=
(Family_Inet, (others => 255));
Loopback_Inet_Addr : constant Inet_Addr_Type :=
procedure Set_Address
(Sin : Sockaddr_Access;
- Address : Sock_Addr_Type)
+ Address : Sock_Addr_Type;
+ Length : out C.int)
is
+ use type C.char;
+
+ function Network_Port return C.unsigned_short is
+ (Short_To_Network (C.unsigned_short (Address.Port))) with Inline;
+
begin
Set_Family (Sin.Sin_Family, Address.Family);
- Sin.Sin_Port := Short_To_Network (C.unsigned_short (Address.Port));
+
+ Length := C.int (Lengths (Address.Family));
case Address.Family is
when Family_Inet =>
+ Sin.Sin_Port := Network_Port;
Sin.Sin_Addr := To_In_Addr (Address.Addr);
+
when Family_Inet6 =>
+ Sin.Sin6_Port := Network_Port;
Sin.Sin6_Addr := To_In6_Addr (Address.Addr);
Sin.Sin6_Scope_Id := 0;
+
+ when Family_Unix =>
+ declare
+ use type C.size_t;
+ Name_Len : constant C.size_t :=
+ C.size_t (ASU.Length (Address.Name));
+ begin
+ Length := Sockaddr_Length_And_Family'Size / System.Storage_Unit
+ + C.int (Name_Len);
+
+ if Name_Len > Sin.Sun_Path'Length then
+ raise Constraint_Error with
+ "Too big address length for UNIX local communication";
+ end if;
+
+ if Name_Len = 0 then
+ Sin.Sun_Path (1) := C.nul;
+
+ else
+ Sin.Sun_Path (1 .. Name_Len) :=
+ C.To_C (ASU.To_String (Address.Name), Append_Nul => False);
+
+ if Sin.Sun_Path (1) /= C.nul
+ and then Name_Len < Sin.Sun_Path'Length
+ then
+ Sin.Sun_Path (Name_Len + 1) := C.nul;
+ Length := Length + 1;
+ end if;
+ end if;
+ end;
+
when Family_Unspec =>
null;
end case;
-- Get_Address --
-----------------
- function Get_Address (Sin : Sockaddr) return Sock_Addr_Type is
- use type C.unsigned_short;
+ function Get_Address
+ (Sin : Sockaddr; Length : C.int) return Sock_Addr_Type
+ is
+ use type C.unsigned_short, C.size_t, C.char, SOSC.OS_Type;
Family : constant C.unsigned_short :=
(if SOSC.Has_Sockaddr_Len = 0 then Sin.Sin_Family.Short_Family
else C.unsigned_short (Sin.Sin_Family.Char_Family));
- AF_INET6_Defined : constant Boolean := SOSC.AF_INET6 > 0;
Result : Sock_Addr_Type
- (if AF_INET6_Defined and then SOSC.AF_INET6 = Family then Family_Inet6
+ (if SOSC.AF_INET6 > 0 and then SOSC.AF_INET6 = Family then Family_Inet6
+ elsif SOSC.AF_UNIX > 0 and then SOSC.AF_UNIX = Family then Family_Unix
elsif SOSC.AF_INET = Family then Family_Inet
else Family_Unspec);
begin
- Result.Port := Port_Type (Network_To_Short (Sin.Sin_Port));
-
case Result.Family is
when Family_Inet =>
+ Result.Port := Port_Type (Network_To_Short (Sin.Sin_Port));
To_Inet_Addr (Sin.Sin_Addr, Result.Addr);
when Family_Inet6 =>
+ Result.Port := Port_Type (Network_To_Short (Sin.Sin6_Port));
To_Inet_Addr (Sin.Sin6_Addr, Result.Addr);
+ when Family_Unix =>
+ if Length > Sin.Sin_Family'Size / System.Storage_Unit then
+ Result.Name := ASU.To_Unbounded_String
+ (C.To_Ada
+ (Sin.Sun_Path
+ (1 .. C.size_t (Length)
+ - Sin.Sin_Family'Size / System.Storage_Unit),
+ Trim_Nul => Sin.Sun_Path (1) /= C.nul
+ or else SOSC.Target_OS = SOSC.Windows));
+ end if;
+
when Family_Unspec =>
- Result.Addr := (Family => Family_Unspec);
+ null;
end case;
return Result;
Families : constant array (Family_Type) of C.int :=
(Family_Unspec => SOSC.AF_UNSPEC,
+ Family_Unix => SOSC.AF_UNIX,
Family_Inet => SOSC.AF_INET,
Family_Inet6 => SOSC.AF_INET6);
Lengths : constant array (Family_Type) of C.unsigned_char :=
(Family_Unspec => 0,
+ Family_Unix => SOSC.SIZEOF_sockaddr_un,
Family_Inet => SOSC.SIZEOF_sockaddr_in,
Family_Inet6 => SOSC.SIZEOF_sockaddr_in6);
when False =>
Short_Family : C.unsigned_short;
end case;
- end record;
- pragma Unchecked_Union (Sockaddr_Length_And_Family);
- pragma Convention (C, Sockaddr_Length_And_Family);
+ end record with Unchecked_Union, Convention => C;
procedure Set_Family
(Length_And_Family : out Sockaddr_Length_And_Family;
type In_Addr is record
S_B1, S_B2, S_B3, S_B4 : C.unsigned_char;
- end record;
- for In_Addr'Alignment use C.int'Alignment;
- pragma Convention (C, In_Addr);
+ end record with Convention => C, Alignment => C.int'Alignment;
-- IPv4 address, represented as a network-order C.int. Note that the
-- underlying operating system may assume that values of this type have
-- C.int alignment, so we need to provide a suitable alignment clause here.
Result : out Inet_Addr_Type);
-- Conversion functions
- type In6_Addr is array (1 .. 16) of C.unsigned_char;
- for In6_Addr'Alignment use C.int'Alignment;
- pragma Convention (C, In6_Addr);
+ type In6_Addr is array (1 .. 16) of C.unsigned_char with Convention => C;
+
+ Unix_Name_Length : constant := 108;
+ -- Maximum length for local unix socket name
function To_In6_Addr (Addr : Inet_Addr_Type) return In6_Addr;
procedure To_Inet_Addr
-- Conversion functions
type Sockaddr (Family : Family_Type := Family_Inet) is record
- Sin_Family : Sockaddr_Length_And_Family;
- -- Address family (and address length on some platforms)
-
- Sin_Port : C.unsigned_short;
- -- Port in network byte order
-
case Family is
when Family_Inet =>
+ Sin_Family : Sockaddr_Length_And_Family;
+ -- Address family (and address length on some platforms)
+
+ Sin_Port : C.unsigned_short;
+ -- Port in network byte order
+
Sin_Addr : In_Addr := (others => 0);
-- IPv4 address
--
-- Note that some platforms require that all unused (reserved) bytes
-- in addresses be initialized to 0 (e.g. VxWorks).
+
when Family_Inet6 =>
+ Sin6_Family : Sockaddr_Length_And_Family;
+ -- Address family (and address length on some platforms)
+
+ Sin6_Port : C.unsigned_short;
+ -- Port in network byte order
+
Sin6_FlowInfo : Interfaces.Unsigned_32 := 0;
Sin6_Addr : In6_Addr := (others => 0);
Sin6_Scope_Id : Interfaces.Unsigned_32 := 0;
+
+ when Family_Unix =>
+ Sun_Family : Sockaddr_Length_And_Family;
+ -- Address family (and address length on some platforms)
+
+ Sun_Path : C.char_array (1 .. Unix_Name_Length);
+
when Family_Unspec =>
null;
end case;
- end record;
- pragma Unchecked_Union (Sockaddr);
- pragma Convention (C, Sockaddr);
+ end record with Convention => C, Unchecked_Union;
-- Internet socket address
type Sockaddr_Access is access all Sockaddr;
procedure Set_Address
(Sin : Sockaddr_Access;
- Address : Sock_Addr_Type);
+ Address : Sock_Addr_Type;
+ Length : out C.int);
-- Initialise all necessary fields in Sin from Address.
-- Set appropriate Family, Port, and either Sin.Sin_Addr or Sin.Sin6_Addr
-- depend on family.
+ -- Set the Length out parameter to the valuable Sockaddr data length.
- function Get_Address (Sin : Sockaddr) return Sock_Addr_Type;
- -- Get Sock_Addr_Type from Sockaddr
+ function Get_Address (Sin : Sockaddr; Length : C.int) return Sock_Addr_Type;
+ -- Get Sock_Addr_Type from Sockaddr and its valuable data Length
------------------
-- Host entries --
#endif
CND(AF_INET6, "IPv6 address family")
+#ifndef AF_UNIX
+# define AF_UNIX -1
+#endif
+CND(AF_UNIX, "Local unix family")
+
#ifndef AF_UNSPEC
# define AF_UNSPEC -1
#else
#endif
CND(SIZEOF_sockaddr_in6, "struct sockaddr_in6")
+/**
+ ** The sockaddr_un structure is not defined in MINGW C headers
+ ** but Windows supports it from build 17063.
+ **/
+#if defined(__MINGW32__)
+struct sockaddr_un {
+ ADDRESS_FAMILY sun_family; /* AF_UNIX */
+ char sun_path[108]; /* Pathname */
+};
+#endif
+#define SIZEOF_sockaddr_un (sizeof (struct sockaddr_un))
+CND(SIZEOF_sockaddr_un, "struct sockaddr_un")
+
#define SIZEOF_fd_set (sizeof (fd_set))
CND(SIZEOF_fd_set, "fd_set")
CND(FD_SETSIZE, "Max fd value")