unit Sparkle.Win.Socket;

{$I Sparkle.Inc}

interface

{$IFDEF MSWINDOWS}

uses
  Winapi.Winsock;

type
  TIn6Addr = packed record
    case Integer of
      0: (s6_addr: array[0..15] of u_char);
  end;

  TSockAddrIn6 = packed record
    sin6_family   : Smallint;
    sin6_port     : u_short;
    sin6_flowinfo : u_long;
    sin6_addr     : TIn6Addr;
    sin6_scope_id : u_long;
  end;
  PSockAddrIn6 = ^TSockAddrIn6;

const
  NI_MAXHOST = 1025;
  NI_MAXSERV = 32;
  NI_NOFQDN = $01;
  NI_NUMERICHOST = $02;
  NI_NAMEREQD	= $04;
  NI_NUMERICSERV = $08;
  NI_DGRAM = $10;

  AF_INET6 = 23;

var
  getnameinfo: function(sa: PSockAddr; salen: u_int; host: PWideChar; hostlen: u_int;
    serv: PWideChar; servlen: u_int; flags: Integer): integer; stdcall;
  inet_pton: function(af: Integer; const src: PWideChar; dst: Pointer): Integer; stdcall;

function InitSocketLibrary: boolean;

function SockAddrToString(Addr: PSOCKADDR): string;
function GetSockAddrPort(Addr: PSOCKADDR): integer;
function BuildSockAddr4(const IP: string; Port: integer; var SockAddr: TSockAddrIn): boolean;

{$ENDIF}

implementation

{$IFDEF MSWINDOWS}

uses
  Winapi.Windows, System.SysUtils;

function SockAddrToString(Addr: PSOCKADDR): string;
var
  host: array[0..NI_MAXHOST] of Char;
  serv: array[0..NI_MAXSERV] of Char;
  salen: integer;
begin
  case Addr.sa_family of
    AF_INET: salen := SizeOf(TSockAddrIn);
    AF_INET6: salen := SizeOf(TSockAddrIn6);
  else
    salen := 0;
  end;
  if getnameinfo(Addr, salen, host, NI_MAXHOST, serv, NI_MAXSERV,
    NI_NUMERICHOST or NI_NUMERICSERV) = 0 then
    Result := host
  else
    Result := '';
end;

function GetSockAddrPort(Addr: PSOCKADDR): integer;
begin
  case Addr.sa_family of
    AF_INET: Result := PSockAddrIn(Addr).sin_port;
    AF_INET6: Result := PSockAddrIn6(Addr).sin6_port;
  else
    result := 0;
  end;
  Result := ntohs(Result);
end;

function BuildSockAddr4(const IP: string; Port: integer; var SockAddr: TSockAddrIn): boolean;
begin
  Result := inet_pton(AF_INET, PChar(IP), @SockAddr.sin_addr) > 0;
  if Result then
  begin
    SockAddr.sin_family := AF_INET;
    SockAddr.sin_port := htons(Port);
  end;
end;

var
  _WSAData: TWSAData;

const
  SocketDllName = 'ws2_32.dll';

var
  LibraryHandle: THandle;

function LoadSocketLibrary: boolean;

  function LoadProc(const ProcName: string): Pointer;
  begin
    Result := GetProcAddress(LibraryHandle, PChar(ProcName));
    Assert(Assigned(Result), SocketDllName + ' - Could not find method: ' + ProcName);
  end;

begin
  if LibraryHandle <> 0 then
    Exit(True);

  Result := False;
  LibraryHandle := LoadLibrary(PChar(SocketDllName));
  if (LibraryHandle <> 0) then
  begin
    Result := True;
    getnameinfo := LoadProc('GetNameInfoW');
    inet_pton := LoadProc('InetPtonW');
  end;
end;

function InitSocketLibrary: boolean;
begin
  Result := LoadSocketLibrary;
  if Result then
    WSAStartup($202, _WSAData);
end;

Initialization
  LibraryHandle := 0;

finalization
  if LibraryHandle <> 0 then
  begin
    WSACleanUp;
    FreeLibrary(LibraryHandle);
  end;

{$ENDIF}

end.
