I'm trying to auto-detect and report on IPv6 availability for Delphi socket programming.
For "localhost", the following code is returning "1700::" for IPv6 and "2.0.0.0" for IPv4. This address is for simple testing, and should be returning ::1 or 127.0.0.1 for my machine. I'm not getting any socket API errors in the calling code, so I'm trying to figure out what I'm doing wrong when attempting to convert "localhost" to the various IP addresses. I'm using Indy headers.
The type declarations are:
TLSocketAddress = record
case Integer of
AF_INET:
(IPv4: TSockAddrIn);
AF_INET6:
(IPv6: TSockAddrIn6);
end;
TLIpInfo = record
private
function GetFamily: TProtocolFamily;
public
Address : TLSocketAddress;
HostName: string;
Machine: string;
IP6 : string;
IP4 : string;
Port : integer;
Error : integer;
property Family : TProtocolFamily read GetFamily;
end;
and the retrieval routine is:
function GetIpInfo(
const AHostName : string;
const APort : integer
): TLIpInfo;
var
nameRet : PAnsiChar;
ptr : Pointer;
Hints : TAddrInfoW;
AddrInfo : PAddrInfoW;
NextInfo : PAddrInfoW;
RetVal : Integer;
ip,
machine : string;
namelen : integer;
begin
ZeroMemory(#Result, SizeOf(Result));
FillChar(Hints, SizeOf(Hints), 0);
Hints.ai_family := AF_UNSPEC;
AddrInfo := nil;
RetVal := GetAddrInfo(PWideChar(AHostName), nil, #Hints, #AddrInfo);
if RetVal = 0 then
try
Result.HostName := AHostName;
Result.Port := APort;
NextInfo := AddrInfo;
while NextInfo <> nil do
begin
if (NextInfo.ai_family = AF_INET) or (NextInfo.ai_family = AF_INET6) then
begin
if (Result.Machine = '') then
begin
SetLength(machine, NI_MAXHOST);
RetVal := GetNameInfo(NextInfo.ai_addr, NextInfo.ai_addrlen,
PChar(machine), NI_MAXHOST, nil, 0, 0);
if (RetVal <> 0) then
begin
NextInfo := NextInfo.ai_next;
continue;
end;
Result.Machine := PChar(machine);
end;
if NextInfo.ai_family = AF_INET then
begin
ptr := PSockAddrIn(NextInfo.ai_addr);
namelen := INET_ADDRSTRLEN;
SetLength(ip, namelen);
end
else
begin
ptr := PSockAddrIn6(NextInfo.ai_addr);
namelen := INET6_ADDRSTRLEN;
SetLength(ip, namelen);
end;
nameRet := inet_ntop(NextInfo.ai_family, ptr, PChar(ip), namelen);
if (nameRet = nil) then
begin
NextInfo := NextInfo.ai_next;
continue;
end;
if (NextInfo.ai_family = AF_INET6) then
Result.IP6 := PChar(ip)
else
Result.IP4 := PChar(ip);
end;
NextInfo := NextInfo.ai_next;
end;
finally
FreeAddrInfo(AddrInfo);
end;
end;
I know it may be too late for the author, but i struggled today with the same problem and noticed that you pass a pointer to the entire ADDRINFO structure to inet_ntop, however you should only be passing the pointer to the binary address.
So call to intet_ntop for IPv4 addresses should be:
inet_ntop(NextInfo.ai_family, #PSockAddrIn(NextInfo.ai_addr).sin_addr, PChar(ip), namelen)
and for IPv6 should be:
inet_ntop(NextInfo.ai_family, #PSockAddrIn6(NextInfo.ai_addr).sin6_addr, PChar(ip), namelen)
Related
I am trying to "reproduce" netstat for Delphi and came across some questions:
This is my code so far:
program NetstatExample;
{$APPTYPE CONSOLE}
uses
Windows,
Winsock2;
const
TCP_TABLE_OWNER_PID_ALL = 5;
ANY_SIZE = 1;
type
TCP_TABLE_CLASS = Integer;
in6_addr = record
case Integer of
0: (Byte: array [0..15] of u_char);
1: (Word: array[0..7] of u_short);
2: (s6_bytes: array [0..15] of u_char);
3: (s6_addr: array [0..15] of u_char);
4: (s6_words: array[0..7] of u_short);
end;
TIn6Addr = in6_addr;
PIn6Addr = ^in6_addr;
PTMib_TCP6Row = ^TMib_TCP6Row;
TMib_TCP6Row = packed record
LocalAddr : IN6_ADDR ;
dwLocalScopeId : DWORD ;
dwLocalPort : DWORD ;
RemoteAddr : IN6_ADDR ;
dwRemoteScopeId : DWORD ;
dwRemotePort : DWORD ;
dwState : DWORD ;
dwProcessId : DWORD ;
end;
PTMIB_TCP6TABLE = ^TMIB_TCP6TABLE;
TMIB_TCP6TABLE = record
dwNumEntries : DWORD;
Table: array[0..ANY_SIZE - 1] of TMib_TCP6Row;
end;
var
GetExtendedTcpTable : function (pTcpTable: Pointer; dwSize: PDWORD; bOrder: BOOL; lAf: ULONG; TableClass: TCP_TABLE_CLASS; Reserved: ULONG): DWord; stdcall;
iphHandle : HMODULE;
TableSize : DWORD;
TCPTable : PTMIB_TCP6TABLE;
I : Integer;
begin
try
iphHandle := LoadLibrary('iphlpapi.dll');
if iphHandle = 0 then Exit;
GetExtendedTcpTable := GetProcAddress(iphHandle, 'GetExtendedTcpTable');
if #GetExtendedTcpTable = NIL then Exit;
if GetExtendedTcpTable(nil, #TableSize, False, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) <> ERROR_INSUFFICIENT_BUFFER then Exit;
GetMem(TCPTable, TableSize);
try
if GetExtendedTcpTable(TCPTable, #TableSize, False, AF_INET6, TCP_TABLE_OWNER_PID_ALL, 0) <> NO_ERROR then Exit;
for I := 0 to TCPTable^.dwNumEntries - 1 do
begin
// Detect AF_INET6 and/or AF_INET4 family
// Display Remote Address in proper format for each family - XP compatible?!
end;
finally
FreeMem(TCPTable, TableSize);
end;
finally
readln;
end;
end.
Here, I am using AF_INET6 so I can also get the ipv6 connections as well.
Questions are:
How can I safely distinguish ipv4 from ipv6?
How can I display the remote address in the proper format for both families and still make it XP compatible as well? (InetNtop did not yet exist in Windows XP)
Q1: Your GetExtendedTcpTable call returns addresses in the specified address family, ie. if you pass it AF_INET6 it returns IPv6 addresses only, if you pass it AF_INET it returns IPv4 addresses only. So there is no need to distinguish, you just cast the returned table to the right table type, as described in the Remarks section of the documentation (linked above).
Q2: Here's my implementation (I hope it's correct):
function AddrStr(Addr: Cardinal): string;
var
P: PAnsiChar;
begin
P := inet_ntoa(PInAddr(#Addr)^);
SetString(Result, P, StrLen(P));
end;
function Addr6Str(const Addr: IN6_ADDR): string;
var
I: Integer;
begin
Result := '';
for I := 0 to 7 do
begin
if Result <> '' then
Result := Result + ':';
Result := Result + LowerCase(IntToHex(ntohs(Addr.Word[I]), 1));
end;
Result := '[' + Result + ']';
end;
...or have a look at Free Pascal's Sockets unit, NetAddrToStr and NetAddrToStr6.
I'm stuck in my debugging efforts with a call to WlanGetNetworkBssList and would appreciate some pointers. My end objective is to construct a Wifi scanner/profiler tool to help me troubleshoot networking issues on remote sites.
I am using the Windows Native Wifi API (link) and Delphi/Pascal interface found here using Delphi Berlin 10.1 Update 2 under Windows 10 (VCL).
I started with a simple and crude test app (VCL) to get a feel for the API and ran into a problem calling WlanGetNetworkBssList so I created a small console app focused on that problem. The issue is that it works in a console app running in a command prompt but not in my VCL test app. The functions are pretty much copy-paste equivalent and stepping through the code side-by-side shows that the data is identical except for the return data from WlanGetNetworkBssList call (pWlanBssList)
Question: Since the call is to an external DLL what steps can I do to further debug this and understand the difference between the VCL and the console app.
Note: The WlanGetNetworkBssList has two modes of operation where an SSID can be supplied to get the BSSID (MAC of the access point) for that specific SSID. By passing NULL instead of an SSID the API will return the BSSIDs of all APs. Passing NULL works on both the VLC and console app. What breaks is when a specific SSID is requested. After verification, the SSID data structure is identical in both apps but the data buffer returned is invalid with the VCL app. How can this be?
Console app:
program CWifiScan;
{$APPTYPE CONSOLE}
uses
Windows,
System.SysUtils,
nduWlanAPI,
nduWlanTypes;
const
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES = $00000001;
var
hWlan: THandle;
guid : TGUID;
dwSupportedVersion: DWORD = 0;
dwClientVersion: DWORD = 1;
i,j : integer;
pInterfaceInfo : Pndu_WLAN_INTERFACE_INFO;
pInterfaceList : Pndu_WLAN_INTERFACE_INFO_LIST;
pAvailableNetworkList : Pndu_WLAN_AVAILABLE_NETWORK_LIST;
procedure GetBSSIDList(clientHandle : THandle;
interfaceGUID : TGUID;
pSSID : Pndu_DOT11_SSID = nil;
SSID_Type : Tndu_DOT11_BSS_Type = dot11_BSS_type_any;
SecurityEnabled : BOOL = True);
var
//to check if interface is connected
pData : Pndu_WLAN_INTERFACE_STATE;
pdwDataSize : DWORD;
isConnected : Boolean;
//to get list of BSSids from available APs
pWlanBssList : Pndu_WLAN_BSS_LIST;
items : integer;
itemIndex : integer;
SSID : string;
MAC : string;
begin
//check if interface is connected
isConnected := False;
if WlanQueryInterface(clientHandle,
#interfaceGUID,
wlan_intf_opcode_interface_state,
nil,
#pdwDataSize,
#pData,
nil) = ERROR_SUCCESS then
begin
isConnected := (pData^ = Tndu_WLAN_INTERFACE_STATE.wlan_interface_state_connected);
end;
//get the list of BSSids for the provided interface
if isConnected then
begin
if WlanGetNetworkBssList(clientHandle,
#interfaceGUID,
pSSID,
SSID_Type,
SecurityEnabled,
nil,
#pWlanBssList) = ERROR_SUCCESS then
begin
items := pWlanBssList^.dwNumberOfItems;
for itemIndex := 0 to items - 1 do
begin
SSID := String(PAnsiChar(#pWlanBssList^.wlanBssEntries[itemIndex].dot11Ssid.ucSSID));
MAC := Format('%.2x:%.2x:%.2x:%.2x:%.2x:%.2x', [
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[0],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[1],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[2],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[3],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[4],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[5]]);
Writeln('');
Writeln('SSID: ................ '+SSID);
Writeln('Physical Address: .... '+MAC);
end; {for itemIndex}
Writeln(#10+#13+'Done.');
end; {WlanGetNetworkBssList succeeds}
end; {isConnected}
end;
begin
hWlan := 0;
if WlanOpenHandle(2, nil,#dwSupportedVersion, #hWlan)= ERROR_SUCCESS then
begin
if WlanEnumInterfaces(hWlan, nil, #pInterfaceList) = ERROR_SUCCESS then
begin
try
for i := 0 to pInterfaceList^.dwNumberOfItems-1 do
begin
Writeln('Wifi Adapter - '+GUIDToString( pInterfaceList^.InterfaceInfo[i].InterfaceGuid ) );
Writeln('Scanning: .... '+pInterfaceList^.InterfaceInfo[i].strInterfaceDescription);
guid := pInterfaceList^.InterfaceInfo[i].InterfaceGuid;
//Get all BSSids for this interface
GetBSSIDList(hWlan, guid);
if WlanGetAvailableNetworkList(hWlan,
#guid,
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES,
nil,
pAvailableNetworkList) = ERROR_SUCCESS then
begin
try
for j := 0 to pAvailableNetworkList^.dwNumberOfItems - 1 do
begin
//Get BSSid for this specific SSID
GetBSSIDList(hWlan,
guid,
#pAvailableNetworkList^.Network[j].dot11Ssid,
pAvailableNetworkList^.Network[j].dot11BssType,
pAvailableNetworkList^.Network[j].bSecurityEnabled);
end;
finally
if pAvailableNetworkList<>nil then
WlanFreeMemory(pAvailableNetworkList);
end;
end;
end;
finally
if pInterfaceList<>nil then
WlanFreeMemory(pInterfaceList);
end;
end;
WlanCloseHandle(hWlan, nil);
readln;
end;
end.
The relevant parts of the VCL app are:
uses
... nduWlanAPI, nduWlanTypes, nduWinDot11;
function TForm1.GetBSSID(clientHandle: THandle;
interfaceGuid: TGUID;
pSSID: Pndu_DOT11_SSID = nil;
SSID_Type : Tndu_DOT11_BSS_TYPE = dot11_BSS_type_any;
SecurityEnabled: boolean = true): string;
var
//used to determin if the interface is connected
pData : Pndu_WLAN_INTERFACE_STATE;
isConnected : boolean;
//used to extract a list of BSSIDs for a given interface
pWlanBssList : Pndu_WLAN_BSS_LIST;
lastError : DWORD;
pdwDataSize : DWORD;
items,
itemIndex: Integer;
begin
pData := nil;
pdwDataSize := 0;
isConnected := False;
//check if the interface is connected
lastError := WlanQueryInterface(clientHandle,
#interfaceGuid,
wlan_intf_opcode_interface_state,
nil,
#pdwDataSize,
#pData,
nil);
if (lastError = ERROR_SUCCESS) then
begin
//isConnected := (Tndu_WLAN_INTERFACE_STATE(pData^.isState) = Tndu_WLAN_INTERFACE_STATE.wlan_interface_state_connected);
isConnected := (pData^ = Tndu_WLAN_INTERFACE_STATE.wlan_interface_state_connected);
end
else
DisplayError('Error in WlanQueryInterface() function', lastError);
if isConnected then
begin
pWlanBssList := nil;
lastError := WlanGetNetworkBssList(clientHandle,
#interfaceGuid,
pSSID,
SSID_Type,
SecurityEnabled,
nil,
#pWlanBssList);
try
if (lastError = ERROR_SUCCESS) then
begin
items := pWlanBssList^.dwNumberOfItems;
for itemIndex := 0 to items-1 do
begin
Result := (Format('%.2x:%.2x:%.2x:%.2x:%.2x:%.2x', [
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[0],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[1],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[2],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[3],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[4],
pWlanBssList^.wlanBssEntries[itemIndex].dot11Bssid[5]]));
end;
end
else
DisplayError('Error in the WlanGetNetworkBssList() function call', lastError);
finally
if pData<>nil then
WlanFreeMemory(pData);
if pWlanBssList<>nil then
WlanFreeMemory(pWlanBssList);
end;
end;
end;
Which is called as follows:
function TForm1.ScanWifi(): THandle;
const
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES = $00000001;
var
hClient : THandle;
dwVersion : DWORD;
lastError : DWORD;
pInterface : Pndu_WLAN_INTERFACE_INFO_LIST;
i : Integer;
j : Integer;
pAvailableNetworkList: Pndu_WLAN_AVAILABLE_NETWORK_LIST;
interfaceGuid : TGUID;
BSSID : string;
begin
lastError:=WlanOpenHandle(NDU_WLAN_API_VERSION, nil, #dwVersion, #hClient);
if lastError<> ERROR_SUCCESS then
begin
//DisplayError('Error in the WlanOpenHandle() function call', lastError);
Result := 0;
Exit;
end;
//L(Format('Requested WLAN interface version [%d], negotiated version [%d]', [NDU_WLAN_API_VERSION, dwVersion]));
Result := hClient;
try
lastError:=WlanEnumInterfaces(hClient, nil, #pInterface);
try
if lastError<> ERROR_SUCCESS then
begin
//DisplayError('Errorin the WlanEnumInterfaces() function call', lastError);
Exit;
end;
for i := 0 to pInterface^.dwNumberOfItems - 1 do
begin
interfaceGuid:= pInterface^.InterfaceInfo[i].InterfaceGuid;
lastError:=WlanGetAvailableNetworkList(hClient,
#interfaceGuid,
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES,
nil,
pAvailableNetworkList);
try
if lastError<> ERROR_SUCCESS then
begin
//DisplayError('Error WlanGetAvailableNetworkList', lastError);
Exit;
end
else
begin
for j := 0 to pAvailableNetworkList^.dwNumberOfItems - 1 do
Begin
BSSID := GetBssid(hClient,
interfaceGuid,
#pAvailableNetworkList^.Network[j].dot11Ssid,
pAvailableNetworkList^.Network[j].dot11BssType,
pAvailableNetworkList^.Network[j].bSecurityEnabled
);
//FAPList.AddOrSetValue(BSSID,J);
end;
end;
finally
if pAvailableNetworkList <> nil then
WlanFreeMemory(pAvailableNetworkList);
end;
end;
finally
if pInterface <> nil then
WlanFreeMemory(pInterface);
end;
finally
WlanCloseHandle(FhClient, nil);
end;
end;
Comparing the data between the two apps the only difference is the result (pWlanBssList) as seen here (left=console, right=VCL):
Looks like compiler bug in boolean conversion, problem is following line in VCL code
SecurityEnabled: boolean = true
you need to change it to
SecurityEnabled: bool = true
I am using Delphi XE2 Update 4 on Windows XP sp3
I am looking to get max possible information from installed network adapters, specially the broadcast ip.
For that I used this code from Jan Schulz that I got from this article.
Unit:
Unit USock;
Interface
Uses Windows, Winsock;
{ Unit to identify the network interfaces
This code requires at least Win98/ME/2K, 95 OSR 2 or NT service pack #3
as WinSock 2 is used (WS2_32.DLL) }
// Constants found in manual on non-officially documented M$ Winsock functions
Const SIO_GET_INTERFACE_LIST = $4004747F;
IFF_UP = $00000001;
IFF_BROADCAST = $00000002;
IFF_LOOPBACK = $00000004;
IFF_POINTTOPOINT = $00000008;
IFF_MULTICAST = $00000010;
Type SockAddr_Gen = Packed Record
AddressIn : SockAddr_In;
Padding : Packed Array [0..7] of Char;
end;
Interface_Info = Record
iiFlags : u_Long;
iiAddress : SockAddr_Gen;
iiBroadcastAddress : SockAddr_Gen;
iiNetmask : SockAddr_Gen;
end;
tNetworkInterface = Record
ComputerName : String;
AddrIP : String;
SubnetMask : String;
AddrNet : String;
AddrLimitedBroadcast : String;
AddrDirectedBroadcast : String;
IsInterfaceUp : Boolean;
BroadcastSupport : Boolean;
IsLoopback : Boolean;
end;
tNetworkInterfaceList = Array of tNetworkInterface;
Function WSAIoctl (aSocket : TSocket;
aCommand : DWord;
lpInBuffer : PChar;
dwInBufferLen : DWord;
lpOutBuffer : PChar;
dwOutBufferLen : DWord;
lpdwOutBytesReturned : LPDWord;
lpOverLapped : Pointer;
lpOverLappedRoutine : Pointer) : Integer; stdcall; external 'WS2_32.DLL';
Function GetNetworkInterfaces (Var aNetworkInterfaceList : tNetworkInterfaceList): Boolean;
Implementation
Function GetNetworkInterfaces (Var aNetworkInterfaceList : tNetworkInterfaceList): Boolean;
// Returns a complete list the of available network interfaces on a system (IPv4)
// Copyright by Dr. Jan Schulz, 23-26th March 2007
// This version can be used for free and non-profit projects. In any other case get in contact
// Written with information retrieved from MSDN
// www.code10.net
Var aSocket : TSocket;
aWSADataRecord : WSAData;
NoOfInterfaces : Integer;
NoOfBytesReturned : u_Long;
InterfaceFlags : u_Long;
NameLength : DWord;
pAddrIP : SockAddr_In;
pAddrSubnetMask : SockAddr_In;
pAddrBroadcast : Sockaddr_In;
pIPString : PChar;
pSubnetMaskString : PChar;
pLimBroadcastString : PChar;
pNetAddrString : PChar;
pDirBroadcastString : PChar;
DirBroadcastDummy : In_Addr;
NetAddrDummy : In_Addr;
Buffer : Array [0..30] of Interface_Info;
i : Integer;
Begin
Result := False;
SetLength (aNetworkInterfaceList, 0);
// Startup of old the WinSock
// WSAStartup ($0101, aWSADataRecord);
// Startup of WinSock2
WSAStartup(MAKEWORD(2, 0), aWSADataRecord);
// Open a socket
aSocket := Socket (AF_INET, SOCK_STREAM, 0);
// If impossible to open a socket, not worthy to go any further
If (aSocket = INVALID_SOCKET) THen Exit;
Try
If WSAIoCtl (aSocket, SIO_GET_INTERFACE_LIST, NIL, 0,
#Buffer, 1024, #NoOfBytesReturned, NIL, NIL) <> SOCKET_ERROR THen
Begin
NoOfInterfaces := NoOfBytesReturned Div SizeOf (Interface_Info);
SetLength (aNetworkInterfaceList, NoOfInterfaces);
// For each of the identified interfaces get:
For i := 0 to NoOfInterfaces - 1 do
Begin
With aNetworkInterfaceList[i] do
Begin
// Get the name of the machine
NameLength := MAX_COMPUTERNAME_LENGTH + 1;
SetLength (ComputerName, NameLength) ;
If Not Windows.GetComputerName (PChar (Computername), NameLength) THen ComputerName := '';
// Get the IP address
pAddrIP := Buffer[i].iiAddress.AddressIn;
pIPString := inet_ntoa (pAddrIP.Sin_Addr);
AddrIP := pIPString;
// Get the subnet mask
pAddrSubnetMask := Buffer[i].iiNetMask.AddressIn;
pSubnetMaskString := inet_ntoa (pAddrSubnetMask.Sin_Addr);
SubnetMask := pSubnetMaskString;
// Get the limited broadcast address
pAddrBroadcast := Buffer[i].iiBroadCastAddress.AddressIn;
pLimBroadcastString := inet_ntoa (pAddrBroadcast.Sin_Addr);
AddrLimitedBroadcast := pLimBroadcastString;
// Calculate the net and the directed broadcast address
NetAddrDummy.S_addr := Buffer[i].iiAddress.AddressIn.Sin_Addr.S_Addr;
NetAddrDummy.S_addr := NetAddrDummy.S_addr And Buffer[i].iiNetMask.AddressIn.Sin_Addr.S_Addr;
DirBroadcastDummy.S_addr := NetAddrDummy.S_addr Or Not Buffer[i].iiNetMask.AddressIn.Sin_Addr.S_Addr;
pNetAddrString := inet_ntoa ((NetAddrDummy));
AddrNet := pNetAddrString;
pDirBroadcastString := inet_ntoa ((DirBroadcastDummy));
AddrDirectedBroadcast := pDirBroadcastString;
// From the evaluation of the Flags we receive more information
InterfaceFlags := Buffer[i].iiFlags;
// Is the network interface up or down ?
If (InterfaceFlags And IFF_UP) = IFF_UP THen IsInterfaceUp := True
Else IsInterfaceUp := False;
// Does the network interface support limited broadcasts ?
If (InterfaceFlags And IFF_BROADCAST) = IFF_BROADCAST THen BroadcastSupport := True
Else BroadcastSupport := False;
// Is the network interface a loopback interface ?
If (InterfaceFlags And IFF_LOOPBACK) = IFF_LOOPBACK THen IsLoopback := True
Else IsLoopback := False;
end;
end;
end;
Except
Result := False;
end;
// Cleanup the mess
CloseSocket (aSocket);
WSACleanUp;
Result := True;
end;
end.
Example call:
uses USock;
Procedure TForm1.Button1Click(Sender: TObject);
Var i : Integer;
aNetInterfaceList : tNetworkInterfaceList;
Begin
If (GetNetworkInterfaces (aNetInterfaceList)) THen
Begin
Memo1.Clear;
Memo1.Lines.Add (DateTimeToStr (Now)+ ' : ');
For i := 0 to High (aNetInterfaceList) do
Begin
Memo1.Lines.Add ('');
Memo1.Lines.Add ('# : ' + IntToStr(i));
Memo1.Lines.Add ('Name : ' + aNetInterfaceList[i].ComputerName);
Memo1.Lines.Add ('IP-Address : ' + aNetInterfaceList[i].AddrIP);
Memo1.Lines.Add ('Subnet mask : ' + aNetInterfaceList[i].SubnetMask);
Memo1.Lines.Add ('Net address : ' + aNetInterfaceList[i].AddrNet);
Memo1.Lines.Add ('Limited broadcast address : ' + aNetInterfaceList[i].AddrLimitedBroadcast);
Memo1.Lines.Add ('Directed Broadcast address : ' + aNetInterfaceList[i].AddrDirectedBroadcast);
Memo1.Lines.Add ('Interface up : ' + BoolToStr (aNetInterfaceList[i].IsInterfaceUp, True));
Memo1.Lines.Add ('Broadcast supported : ' + BoolToStr (aNetInterfaceList[i].BroadcastSupport, True));
Memo1.Lines.Add ('Loopback interface : ' + BoolToStr (aNetInterfaceList[i].IsLoopback, True));
Memo1.Lines.Add ('');
end;
end;
end;
The code apparently works, however it only return one network interface, the loopback (127.0.0.0), and it should return my network private interface also.
On this part of the code it always return only one interface available:
NoOfInterfaces := NoOfBytesReturned Div SizeOf (Interface_Info);
To work on XE2 I had to change the string used (AnsiString).
I have also tried using Winsock2 unit, and also tried using IdWinSock2 and the API calls from there.
On all cases the APIs worked and returned only the loopback interface.
Using another utility written on delphi I could get a this list, and the local IP 192.168.0.112 got listed, however this source code is not easy to use.
My question is: what is wrong?
Following Remy Lebeau sugestion and help documenting this thread I found this source code in delphi, tested with XP and W7, that brings the information using GetAdaptersInfo().
Credits to Brad Prendergast original post updated by Markus Humm final version
I have added the subnet mask reporting to make clear to newbies like me where the information is stored:
uses IpHlpApi, IpTypes;
procedure RetrieveLocalAdapterInformation(strings: TStrings);
var
pAdapterInfo, pTempAdapterInfo: PIP_ADAPTER_INFO;
AdapterInfo: IP_ADAPTER_INFO;
BufLen: DWORD;
Status: DWORD;
strMAC: String;
i: Integer;
begin
strings.Clear;
BufLen:= sizeof(AdapterInfo);
pAdapterInfo:= #AdapterInfo;
Status:= GetAdaptersInfo(nil, BufLen);
pAdapterInfo:= AllocMem(BufLen);
try
Status:= GetAdaptersInfo(pAdapterInfo, BufLen);
if (Status <> ERROR_SUCCESS) then
begin
case Status of
ERROR_NOT_SUPPORTED:
strings.Add('GetAdaptersInfo is not supported by the operating ' +
'system running on the local computer.');
ERROR_NO_DATA:
strings.Add('No network adapter on the local computer.');
else
strings.Add('GetAdaptersInfo failed with error #' + IntToStr(Status));
end;
Dispose(pAdapterInfo);
Exit;
end;
while (pAdapterInfo <> nil) do
begin
strings.Add('Description: ' + pAdapterInfo^.Description);
strings.Add('Name: ' + pAdapterInfo^.AdapterName);
strMAC := '';
for I := 0 to pAdapterInfo^.AddressLength - 1 do
strMAC := strMAC + '-' + IntToHex(pAdapterInfo^.Address[I], 2);
Delete(strMAC, 1, 1);
strings.Add('MAC address: ' + strMAC);
strings.Add('IP address: ' + pAdapterInfo^.IpAddressList.IpAddress.S);
strings.Add('IP subnet mask: ' + pAdapterInfo^.IpAddressList.IpMask.S);
strings.Add('Gateway: ' + pAdapterInfo^.GatewayList.IpAddress.S);
strings.Add('DHCP enabled: ' + IntTOStr(pAdapterInfo^.DhcpEnabled));
strings.Add('DHCP: ' + pAdapterInfo^.DhcpServer.IpAddress.S);
strings.Add('Have WINS: ' + BoolToStr(pAdapterInfo^.HaveWins,True));
strings.Add('Primary WINS: ' + pAdapterInfo^.PrimaryWinsServer.IpAddress.S);
strings.Add('Secondary WINS: ' + pAdapterInfo^.SecondaryWinsServer.IpAddress.S);
pTempAdapterInfo := pAdapterInfo;
pAdapterInfo:= pAdapterInfo^.Next;
if assigned(pAdapterInfo) then Dispose(pTempAdapterInfo);
end;
finally
Dispose(pAdapterInfo);
end;
end;
Jan Schulz's code works fine when properly converted to unicode-friendly Delphi.
I've done some corrections:
As TOndrej has pointed, char must be converted to AnsiChar when needed. In this case, this only occurs in the Padding field of the SockAddr_Gen record, which was not only screwing the record, but also causing SizeOf(Interface_Info), and subsequently NoOfInterfaces, to return a wrong result. Since it's not really a character, it is better to define it as a byte.
Dropped PChars used to hold results of inet_ntoa calls, and assigning TNetworkInterface string fields directly.
Strings in TNetworkInterface are fine, because they are not passed to any api calls. Also, ComputerName is passed to GetComputerName api, which expects a PWideChar/PChar.
Unit USock;
Interface
Uses Windows, Winsock;
{ Unit to identify the network interfaces
This code requires at least Win98/ME/2K, 95 OSR 2 or NT service pack #3
as WinSock 2 is used (WS2_32.DLL) }
// Constants found in manual on non-officially documented M$ Winsock functions
Const SIO_GET_INTERFACE_LIST = $4004747F;
IFF_UP = $00000001;
IFF_BROADCAST = $00000002;
IFF_LOOPBACK = $00000004;
IFF_POINTTOPOINT = $00000008;
IFF_MULTICAST = $00000010;
Type SockAddr_Gen = Packed Record
AddressIn : SockAddr_In;
Padding : Packed Array [0..7] of Byte;
end;
Interface_Info = Record
iiFlags : u_Long;
iiAddress : SockAddr_Gen;
iiBroadcastAddress : SockAddr_Gen;
iiNetmask : SockAddr_Gen;
end;
tNetworkInterface = Record
ComputerName : String;
AddrIP : String;
SubnetMask : String;
AddrNet : String;
AddrLimitedBroadcast : String;
AddrDirectedBroadcast : String;
IsInterfaceUp : Boolean;
BroadcastSupport : Boolean;
IsLoopback : Boolean;
end;
tNetworkInterfaceList = Array of tNetworkInterface;
Function WSAIoctl (aSocket : TSocket;
aCommand : DWord;
lpInBuffer : Pointer;
dwInBufferLen : DWord;
lpOutBuffer : Pointer;
dwOutBufferLen : DWord;
lpdwOutBytesReturned : LPDWord;
lpOverLapped : Pointer;
lpOverLappedRoutine : Pointer) : Integer; stdcall; external 'WS2_32.DLL';
Function GetNetworkInterfaces (Var aNetworkInterfaceList : tNetworkInterfaceList): Boolean;
implementation
Function GetNetworkInterfaces (Var aNetworkInterfaceList : tNetworkInterfaceList): Boolean;
// Returns a complete list the of available network interfaces on a system (IPv4)
// Copyright by Dr. Jan Schulz, 23-26th March 2007
// This version can be used for free and non-profit projects. In any other case get in contact
// Written with information retrieved from MSDN
// www.code10.net
Var aSocket : TSocket;
aWSADataRecord : WSAData;
NoOfInterfaces : Integer;
NoOfBytesReturned : u_Long;
InterfaceFlags : u_Long;
NameLength : DWord;
pAddrIP : SockAddr_In;
pAddrSubnetMask : SockAddr_In;
pAddrBroadcast : Sockaddr_In;
DirBroadcastDummy : In_Addr;
NetAddrDummy : In_Addr;
Buffer : Array [0..30] of Interface_Info;
i : Integer;
Begin
Result := False;
SetLength (aNetworkInterfaceList, 0);
// Startup of old the WinSock
// WSAStartup ($0101, aWSADataRecord);
// Startup of WinSock2
WSAStartup(MAKEWORD(2, 0), aWSADataRecord);
// Open a socket
aSocket := Socket (AF_INET, SOCK_STREAM, 0);
// If impossible to open a socket, not worthy to go any further
If (aSocket = INVALID_SOCKET) THen Exit;
Try
If WSAIoCtl (aSocket, SIO_GET_INTERFACE_LIST, NIL, 0,
#Buffer, 1024, #NoOfBytesReturned, NIL, NIL) <> SOCKET_ERROR THen
Begin
NoOfInterfaces := NoOfBytesReturned Div SizeOf (Interface_Info);
SetLength (aNetworkInterfaceList, NoOfInterfaces);
// For each of the identified interfaces get:
For i := 0 to NoOfInterfaces - 1 do
Begin
With aNetworkInterfaceList[i] do
Begin
// Get the name of the machine
NameLength := MAX_COMPUTERNAME_LENGTH + 1;
SetLength (ComputerName, NameLength) ;
If Not GetComputerName (PChar (Computername), NameLength) THen ComputerName := '';
// Get the IP address
pAddrIP := Buffer[i].iiAddress.AddressIn;
AddrIP := string(inet_ntoa (pAddrIP.Sin_Addr));
// Get the subnet mask
pAddrSubnetMask := Buffer[i].iiNetMask.AddressIn;
SubnetMask := string(inet_ntoa (pAddrSubnetMask.Sin_Addr));
// Get the limited broadcast address
pAddrBroadcast := Buffer[i].iiBroadCastAddress.AddressIn;
AddrLimitedBroadcast := string(inet_ntoa (pAddrBroadcast.Sin_Addr));
// Calculate the net and the directed broadcast address
NetAddrDummy.S_addr := Buffer[i].iiAddress.AddressIn.Sin_Addr.S_Addr;
NetAddrDummy.S_addr := NetAddrDummy.S_addr And Buffer[i].iiNetMask.AddressIn.Sin_Addr.S_Addr;
DirBroadcastDummy.S_addr := NetAddrDummy.S_addr Or Not Buffer[i].iiNetMask.AddressIn.Sin_Addr.S_Addr;
AddrNet := string(inet_ntoa ((NetAddrDummy)));
AddrDirectedBroadcast := string(inet_ntoa ((DirBroadcastDummy)));
// From the evaluation of the Flags we receive more information
InterfaceFlags := Buffer[i].iiFlags;
// Is the network interface up or down ?
If (InterfaceFlags And IFF_UP) = IFF_UP THen IsInterfaceUp := True
Else IsInterfaceUp := False;
// Does the network interface support limited broadcasts ?
If (InterfaceFlags And IFF_BROADCAST) = IFF_BROADCAST THen BroadcastSupport := True
Else BroadcastSupport := False;
// Is the network interface a loopback interface ?
If (InterfaceFlags And IFF_LOOPBACK) = IFF_LOOPBACK THen IsLoopback := True
Else IsLoopback := False;
end;
end;
end;
Except
//Result := False;
end;
// Cleanup the mess
CloseSocket (aSocket);
WSACleanUp;
Result := True;
end;
end.
For what its worth, if you need the broadcast IP for a specific adapter, you can use SIO_GET_BROADCAST_ADDRESS instead.
With that said, a non-Winsock solution would be to use GetAdaptersInfo() or GetAdaptersAddresses() instead. This way, you don't have to create a SOCKET to get the info, you can enumerate both IPv4 and IPv6 adapters at the same time, as well as other adapters that Winsock does not recognize.
For GetAdaptersInfo(), the IP_ADAPTER_INFO.IpAddressList list contains IPv4 IPs and Subnet masks (on XP+, uni-directional adapters are included in the output, but you can use GetUniDirectionalAdapterInfo() to filter them out).
For GetAdaptersAddresses(), the IP_ADAPTER_ADDRESSES.FirstUnicastAddress list contains both IPv4 and IPv6 IPs, and IPv4 Subnet masks on Vista+. For XP and earlier, you can use GetIpAddrTable() to retrieve IPv4 Subnet masks and match them to the IPv4 IPs from GetAdaptersAddresses().
Once you have an IPv4 IP and Subnet mask, calculating its Broadcast IP is very simple:
BroadcastIP := (IP and SubnetMask) or (not SubnetMask);
As you have changed String declarations to AnsiString, also change Char declarations to AnsiChar.
Update for Delphi 10.2 Tokyo supported.
procedure TForm4.RetrieveLocalAdapterInformation;
var
pAdapterInfo: PIP_ADAPTER_INFO;
AdapterInfo: IP_ADAPTER_INFO;
BufLen: DWORD;
Status: DWORD;
strMAC: String;
i: Integer;
strings: TStrings;
begin
strings:= Tstringlist.create;
strings.Clear;
BufLen:= sizeof(AdapterInfo);
pAdapterInfo:= #AdapterInfo;
Status:= GetAdaptersInfo(nil, BufLen);
pAdapterInfo:= AllocMem(BufLen);
try
Status:= GetAdaptersInfo(pAdapterInfo, BufLen);
if (Status <> ERROR_SUCCESS) then
begin
case Status of
ERROR_NOT_SUPPORTED:
strings.Add('GetAdaptersInfo is not supported by the operating ' +
'system running on the local computer.');
ERROR_NO_DATA:
strings.Add('No network adapter on the local computer.');
else
strings.Add('GetAdaptersInfo failed with error #' + IntToStr(Status));
end;
Dispose(pAdapterInfo);
Exit;
end;
while (pAdapterInfo <> nil) do
begin
memo1.Lines.Add('');
memo1.Lines.Add('Description: ------------------------' + pAdapterInfo^.Description);
memo1.Lines.Add('Name: ' + pAdapterInfo^.AdapterName);
strMAC := '';
for I := 0 to pAdapterInfo^.AddressLength - 1 do
strMAC := strMAC + '-' + IntToHex(pAdapterInfo^.Address[I], 2);
Delete(strMAC, 1, 1);
memo1.Lines.Add('MAC address: ' + strMAC);
memo1.Lines.Add('IP address: ' + pAdapterInfo^.IpAddressList.IpAddress.S);
memo1.Lines.Add('IP subnet mask: ' + pAdapterInfo^.IpAddressList.IpMask.S);
memo1.Lines.Add('Gateway: ' + pAdapterInfo^.GatewayList.IpAddress.S);
memo1.Lines.Add('DHCP enabled: ' + IntTOStr(pAdapterInfo^.DhcpEnabled));
memo1.Lines.Add('DHCP: ' + pAdapterInfo^.DhcpServer.IpAddress.S);
memo1.Lines.Add('Have WINS: ' + BoolToStr(pAdapterInfo^.HaveWins,True));
memo1.Lines.Add('Primary WINS: ' + pAdapterInfo^.PrimaryWinsServer.IpAddress.S);
memo1.Lines.Add('Secondary WINS: ' + pAdapterInfo^.SecondaryWinsServer.IpAddress.S);
pAdapterInfo:= pAdapterInfo^.Next;
end;
finally
Dispose(pAdapterInfo);
strings.free;
end;
end;
procedure TForm4.Button1Click(Sender: TObject);
begin
RetrieveLocalAdapterInformation//
end;
Updated to fix overwrite of pAdapterInfo pointer while looping the linked list
procedure TForm4.RetrieveLocalAdapterInformation;
var
pAdapterInfo: PIP_ADAPTER_INFO; // Linked list of adapters
pAdapter: PIP_ADAPTER_INFO; // Single adapter
BufLen: DWORD;
Status: DWORD;
strMAC: String;
i: Integer;
begin
memo1.Clear;
// Make an initial call to GetAdaptersInfo to get the necessary size
// of the linked list
GetAdaptersInfo(nil, BufLen);
pAdapterInfo := AllocMem(BufLen);
try
Status := GetAdaptersInfo(pAdapterInfo, BufLen);
if (Status <> ERROR_SUCCESS) then
begin
case Status of
ERROR_NOT_SUPPORTED:
memo1.Lines.Add('GetAdaptersInfo is not supported by the operating ' +
'system running on the local computer.');
ERROR_NO_DATA:
memo1.Lines.Add('No network adapter on the local computer.');
else
memo1.Lines.Add('GetAdaptersInfo failed with error #' + IntToStr(Status));
end;
Exit;
end;
pAdapter := pAdapterInfo;
while (pAdapter <> nil) do
begin
memo1.Lines.Add('');
memo1.Lines.Add('Description: ------------------------' + pAdapter^.Description);
memo1.Lines.Add('Name: ' + pAdapter^.AdapterName);
strMAC := '';
for I := 0 to pAdapter^.AddressLength - 1 do
strMAC := strMAC + '-' + IntToHex(pAdapter^.Address[I], 2);
Delete(strMAC, 1, 1);
memo1.Lines.Add('MAC address: ' + strMAC);
memo1.Lines.Add('IP address: ' + pAdapter^.IpAddressList.IpAddress.S);
memo1.Lines.Add('IP subnet mask: ' + pAdapter^.IpAddressList.IpMask.S);
memo1.Lines.Add('Gateway: ' + pAdapter^.GatewayList.IpAddress.S);
memo1.Lines.Add('DHCP enabled: ' + IntToStr(pAdapter^.DhcpEnabled));
memo1.Lines.Add('DHCP: ' + pAdapter^.DhcpServer.IpAddress.S);
memo1.Lines.Add('Have WINS: ' + BoolToStr(pAdapter^.HaveWins,True));
memo1.Lines.Add('Primary WINS: ' + pAdapter^.PrimaryWinsServer.IpAddress.S);
memo1.Lines.Add('Secondary WINS: ' + pAdapter^.SecondaryWinsServer.IpAddress.S);
pAdapter := pAdapter^.Next;
end;
finally
FreeMem(pAdapterInfo);
end;
end
;
We are facing the problem, that different user groups shall be able to read and write files from a common data directory (e.g. c:\ProgramData\xyz).
The data is written from different sources e.g. a service writes files into it and a user shall be able to change it's content later on.
The problem now is that this only works if "everybody" is allowed to read/write/change
files in that directory (und subdirs).
What I want to check in the installer is if all users are allowed to do so aka. check if the "every users" group (or "Jeder" group in german) is in the access list.
I have only basic knowledge about ACL and can change that in the explorer but I would need a few lines of code which push me into the right direction (in Delphi).
many thanks
Mike
I think it's not a Delphi but WinAPI question. Delphi doesn't have any special facilities to make this easier AFAIK.
Getting information from an ACL says you need to do GetSecurityInfo on an open handle, then GetEffectiveRightsFromACL on an ACL you get.
You specify a trustee, which can be by name, but better use SID. Name for "Everyone" can change, but there's a special SID for it which is valid on any PC, google it. Okay, here it is: "(S-1–1–0)". Or you can use CreateWellKnownSid and give it WinWorldSid to get the same SID (more proper but longer way).
All of that from five minutes of googling so watch out for mistakes.
Alright, here's some code.
function ConvertStringSidToSid(StringSid: PWideChar; var Sid: PSID): boolean; stdcall; external advapi32 name 'ConvertStringSidToSidW';
function AclGetEffectiveRights(const path, sid: string): cardinal;
var h: THandle; //handle to our directory
err: integer;
dacl: PACL; //access control list for the object
secdesc: pointer;
tr: TRUSTEE;
bsid: PSid;
begin
Result := 0;
//Open directory
h := CreateFile(PChar(path), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_BACKUP_SEMANTICS, 0);
//we need FILE_FLAG_BACKUP_SEMANTICS to open a directory
if h=INVALID_HANDLE_VALUE then RaiseLastOsError();
try
bsid := nil;
//Query access control list for a directory -- the list you see in the properties box
err := GetSecurityInfo(h, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION,
nil, nil, #dacl, nil, secdesc);
//GetSecurityInfo can return many things but we only need DACL,
//and we are required to also get a security descriptor
if err<>ERROR_SUCCESS then
raise Exception.CreateFmt('Cannot retrieve DACL: error %d',[err]);
try
//Convert string sid to binary sid
if not ConvertStringSidToSid(PChar(sid), bsid) then
RaiseLastOsError();
//Query effective rights for a trustee
BuildTrusteeWithSid(#tr, bsid);
err := GetEffectiveRightsFromAcl(dacl^, tr, Result);
if err<>ERROR_SUCCESS then
raise Exception.CreateFmt('Cannot calculate effective rights: error %d',[err]);
finally
//Documentation says to free some resources this way when we're done with it.
LocalFree(NativeUint(bsid));
LocalFree(NativeUint(secdesc));
end;
finally
CloseHandle(h);
end;
end;
It's used like this:
var rights,test: cardinal;
rights := AclGetEffectiveRights('C:\My\Folder','S-1-1-0');
//List rights you want tested
test := FILE_LIST_DIRECTORY + FILE_ADD_FILE + FILE_ADD_SUBDIRECTORY
+ FILE_READ_EA + FILE_WRITE_EA + FILE_TRAVERSE + FILE_DELETE_CHILD;
Result := (rights and test) = test;
Might not work; gives ACCESS_DENIED on my PC but that's probably because of my complicated domain situation. Anyway that's something for a start.
So.. The above version worked... until Windows Update 1903 hit me and GetEffectiveRightsFromAcl always resulted in an error ERROR_NO_SUCH_DOMAIN (which is obscure since there is no Domain whatsoever involved here).
So I needed to switch to the following procedure:
// ###########################################
// ### translated and extended from https://learn.microsoft.com/de-de/windows/win32/api/aclapi/nf-aclapi-geteffectiverightsfromacla
procedure DisplayAccessMask(Mask : ACCESS_MASK );
begin
{
// This evaluation of the ACCESS_MASK is an example.
// Applications should evaluate the ACCESS_MASK as necessary.
}
if (((Mask and GENERIC_ALL) = GENERIC_ALL)
or ((Mask and FILE_ALL_ACCESS) = FILE_ALL_ACCESS))
then
begin
OutputDebugString( 'Full control');
exit;
end;
if (((Mask and GENERIC_READ) = GENERIC_READ)
or ((Mask and FILE_GENERIC_READ) = FILE_GENERIC_READ))
then
OutputDebugString( 'Read');
if (((Mask and GENERIC_WRITE) = GENERIC_WRITE)
or ((Mask and FILE_GENERIC_WRITE) = FILE_GENERIC_WRITE))
then
OutputDebugString('Write');
if (((Mask and GENERIC_EXECUTE) = GENERIC_EXECUTE)
or ((Mask and FILE_GENERIC_EXECUTE) = FILE_GENERIC_EXECUTE))
then
OutputDebugString('Execute');
end;
function CheckMask( MASK : ACCESS_MASK; refMask : ACCESS_MASK ) : boolean;
var msk : ACCESS_MASK;
begin
msk := 0;
if (((Mask and GENERIC_READ) = GENERIC_READ)
or ((Mask and FILE_GENERIC_READ) = FILE_GENERIC_READ))
then
msk := msk or FILE_GENERIC_READ;
if (((Mask and GENERIC_WRITE) = GENERIC_WRITE)
or ((Mask and FILE_GENERIC_WRITE) = FILE_GENERIC_WRITE))
then
msk := msk or FILE_GENERIC_WRITE;
if (((Mask and GENERIC_EXECUTE) = GENERIC_EXECUTE)
or ((Mask and FILE_GENERIC_EXECUTE) = FILE_GENERIC_EXECUTE))
then
msk := msk or FILE_GENERIC_EXECUTE;
Result := (msk and refMask) = refMask;
end;
function GetAccess(hAuthzClient :AUTHZ_CLIENT_CONTEXT_HANDLE; psd : PSECURITY_DESCRIPTOR) : BOOL;
var AccessRequest : AUTHZ_ACCESS_REQUEST;
AccessReply : AUTHZ_ACCESS_REPLY;
buffer : Array[0..1023] of Byte;
begin
FillChar(AccessRequest, sizeof(AccessRequest), 0);
FillChar(AccessReply, sizeof(AccessReply), 0);
FillChar(buffer, sizeof(buffer), 0);
AccessRequest.DesiredAccess := MAXIMUM_ALLOWED;
AccessRequest.PrincipalSelfSid := nil;
AccessRequest.ObjectTypeList := nil;
AccessRequest.ObjectTypeListLength := 0;
AccessRequest.OptionalArguments := nil;
AccessReply.ResultListLength := 1;
AccessReply.GrantedAccessMask := PACCESS_MASK( LongWord(#Buffer[0]));
AccessReply.Error := PDWORD( LongWord( AccessReply.GrantedAccessMask ) + sizeof(Access_Mask));
Result := AuthzAccessCheck( 0,
hAuthzClient,
#AccessRequest,
0,
psd,
nil,
0,
#AccessReply,
nil);
if Result then
begin
DisplayAccessMask( AccessReply.GrantedAccessMask^ );
Result := CheckMask( AccessReply.GrantedAccessMask^, FILE_GENERIC_WRITE or FILE_GENERIC_READ );
end
else
RaiseLastOSError;
end;
function ConvertStringSidToSid(StringSid: PWideChar; var Sid: PSID): boolean; stdcall; external advapi32 name 'ConvertStringSidToSidW';
function ConvertNameToBinarySid(pAccountName : PCHAR): PSID ;
var pDomainName : PChar;
dwDomainNameSize : DWord;
aSID : PSID;
dwSIDSIZE : DWORD;
sidType : SID_NAME_USE;
begin
pDomainName := nil;
dwDomainNameSize := 0;
aSID := nil;
LookupAccountName( nil, pAccountName, aSID, dwSIDSIZE, pDomainName, dwDomainNameSize, sidType);
aSid := Pointer( LocalAlloc( LPTR, dwSIDSIZE*sizeof(char)) );
pDomainName := Pointer( LocalAlloc(LPTR, dwDomainNameSize*sizeof(char)) );
if not LookupAccountName( nil, pAccountName, aSID, dwSIDSIZE, pDomainName, dwDomainNameSize, sidType) then
begin
LocalFree( Cardinal(aSID) );
Result := nil;
end
else
begin
Result := aSid;
end;
LocalFree( Cardinal(pDomainName) );
end;
function GetEffectiveRightsForSID(hManager :AUTHZ_RESOURCE_MANAGER_HANDLE;
psd : PSECURITY_DESCRIPTOR;
sid : PChar) : BOOL;
var asid : PSID;
bResult : BOOL;
unusedID : LUID;
hAuthzClientContext : AUTHZ_CLIENT_CONTEXT_HANDLE;
begin
Result := False;
asid := nil;
hAuthzClientContext := 0;
FillChar(unusedID, sizeof(unusedID), 0);
if not ConvertStringSidToSid(sid, asid) then
RaiseLastOsError();
// asid := ConvertNameToBinarySid('rabatscher');
if asid = nil then
RaiseLastOSError;
try
if asid <> nil then
begin
bResult := AuthzInitializeContextFromSid( 0, aSid, hManager, nil, unusedId, nil, #hAuthzClientContext );
try
if bResult then
Result := GetAccess(hAuthzClientContext, psd);
finally
if hAuthzClientContext <> 0 then
AuthzFreeContext(hAuthzClientContext);
end;
end;
finally
if asid <> nil then
LocalFree(LongWord(asid));
end;
end;
function UseAuthzSolution( psd : PSECURITY_DESCRIPTOR; const sid : string = 'S-1-1-0') : boolean;
var hManager : AUTHZ_RESOURCE_MANAGER_HANDLE;
bResult : BOOL;
pSid : PChar;
begin
bResult := AuthzInitializeResourceManager(AUTHZ_RM_FLAG_NO_AUDIT,
nil, nil, nil, nil, #hManager);
if bResult then
begin
pSid := PChar(sid);
bResult := GetEffectiveRightsForSID(hManager, psd, psid);
AuthzFreeResourceManager(hManager);
end;
Result := bResult;
end;
function GetSecurityInfo(handle: THandle; ObjectType: SE_OBJECT_TYPE;
SecurityInfo: SECURITY_INFORMATION; ppsidOwner, ppsidGroup: PPSID; ppDacl, ppSacl: PACL;
var pSecurityDescriptor: PSECURITY_DESCRIPTOR): DWORD; stdcall; external 'ADVAPI32.DLL' name 'GetSecurityInfo'; {use localfree to release ppSecurityDescriptor}
function CheckDirectoryAccess( path : string ) : boolean;
var dw : DWORD;
apacl : PACL;
psd : PSECURITY_DESCRIPTOR;
apSID : PSID;
h : THandle;
begin
try
apSID := nil;
//Open directory
h := CreateFile(PChar(path), GENERIC_READ, FILE_SHARE_READ, nil,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL or FILE_FLAG_BACKUP_SEMANTICS, 0);
//we need FILE_FLAG_BACKUP_SEMANTICS to open a directory
if h = INVALID_HANDLE_VALUE then
RaiseLastOsError();
try
//Query access control list for a directory -- the list you see in the properties box
dw := GetSecurityInfo(h, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION or OWNER_SECURITY_INFORMATION or GROUP_SECURITY_INFORMATION,
nil, nil, #apacl, nil, psd);
if dw <> ERROR_SUCCESS then
RaiseLastOSError;
try
Result := UseAuthzSolution(psd);
finally
if apSID <> nil then
LocalFree(NativeUint(apSID));
LocalFree(NativeUint(psd));
end;
finally
CloseHandle(h);
end;
except
on E : Exception do
begin
Result := False;
end;
end;
end;
Note that there are a few changes so the procedure works:
GetSecurityInfo (from the above procedure) needs the parameter DACL_SECURITY_INFORMATION or OWNER_SECURITY_INFORMATION or GROUP_SECURITY_INFORMATION (not only DACL_SECURITY_INFORMATION) otherwise you get an Error 87 in AuthzAccessCheck!
In addition you need to check out the JWA headers from the jedi library.
Hope that helps other people too.
I am currently trying to develop an application for displaying IPv6 traffic. I am confused as to how to extract the site local and the link local addresses of the local machine . Any guidance would be appreciated.
The structure of the IPv6 packet in delphi i am using is :
type
PIPV6HeaderPtr = ^TIPV6Header;
TIPV6Header = packed record
ip6_flow : DWORD; // 4 bits = version #,// 8 bits = Trafic class,// 20 bits = flow label
ip6_len : Word;//Cardinal; // Payload length
//ip6_next : Cardinal; // Next Header
ip6_next : Byte; // Next Header
ip6_hops : Byte; // Hop Limit
h_source : IN6_ADDR;
h_dest : IN6_ADDR;
end;
//
// IPv6 extension header format
//
type
PIPV6EXTPTR = ^TIPV6EXT;
TIPV6EXT = packed record
ip6_next : Byte;
ip6_len : Byte;
ip6_data : array[0..1] of Byte;
End;
type
PIPV6EXT_FRAGPTR = ^TIPV6EXT_FRAG;
TIPV6EXT_FRAG = packed record
ip6_next : Byte;
ip6_reserved : Byte;
ip6_offlg : Word;
ip6_ident : DWORD;
End;
Thanks in advance.
See the following explanation of what site local addresses and link local addresses are and how they work:
IPv6 tutorial – Part 6: Site-local addresses and link-local addresses
You can use the Win32 API GetAdapterAddresses() function to get the local machine's IPv6 addresses.
I have found another method to get the addresses using WSAIoctl . I am adding my code below .
uses WinSock2; // Widely available on Net
function TForm1.GetIPUsingIoctlMethod(var sInt: string): Boolean;
var
s: TSocket;
wsaD: TWSADATA;
NumInterfaces: Integer;
BytesReturned, SetFlags: u_long;
pAddrInet: SOCKADDR_IN;
pAddrString: PCHAR;
PtrA: pointer;
Buffer: Array[0..20] Of INTERFACE_INFO_EX;
i: Integer;
Local_IpList : TStringList ;
addrList : LPSOCKET_ADDRESS_LIST ;
in6 : PSockAddr;
protoInfo : WSAProtocol_Info;
text : Array[1..46] Of Char;
Buffer1 : DWORD;
Str : String;
begin
result := False; // Initialize
sInt := '';
Try
WSAStartup($0101, wsaD); // Start WinSock
// You should normally check
// for errors here :)
{Create a WSA Socket}
//s := WSASocketA(AF_INET6,SOCK_STREAM, IPPROTO_IP, nil, 0, 0);
s:= Socket(AF_INET6,SOCK_STREAM,IPPROTO_IP);
//s := Socket(AF_INET6, SOCK_STREAM, 0); // Open a socket
if (s = INVALID_SOCKET) then exit;
try // Call WSAIoCtl
PtrA := #bytesReturned;
if (WSAIoCtl(s, SIO_ADDRESS_LIST_QUERY, nil, 0, #Buffer, SizeOf(Buffer), PtrA, nil, nil)<> SOCKET_ERROR)
then
begin // If ok, find out how
// many interfaces exist
Result := True;
addrList := LPSOCKET_ADDRESS_LIST(#Buffer);
DebugLog(' i = ' + IntToStr(addrList.iAddressCount),0);
// NumInterfaces := BytesReturned div SizeOf(INTERFACE_INFO);
Local_IpList := TStringList.Create ;
for i := 0 to addrList.iAddressCount - 1 do // For every interface
begin
If ( addrList.Address[i].lpSockaddr.sin_family = AF_Inet6) Then
Begin
in6 := PSockaddr(#addrList.Address[i].lpSockaddr);
Buffer1 := SizeOf(Text);
//protoInfo.
FillChar(Text,SizeOf(Text),#0);
If WSAAddressToString(addrList.Address[i].lpSockaddr,addrList.Address[i].iSockaddrLength,
nil,#text,Buffer1) <> 0 Then
Begin
debuglog('err1 = ' + SysErrorMessage(WSAGetLastError),0);
end;
Str := Text;
DebugLog(' Addr cnt =' + IntToStr(i) + ' = ' + Str,0);
Buffer1 := 0;
FillChar(Text,SizeOf(Text),#0);
//in6.
//DebugLog('ip = ' + );
//IN6_IS_ADDR_LINKLOCAL
end;
end;
end
Else
sInt := SysErrorMessage(WSAGetLastError);
except
end;
//
// Close sockets
//
CloseSocket(s);
WSACleanUp;
Except
End;
end;
debugLog is simply a method to write in a text file.