unit network_interfaces;
{$mode ObjFPC}{$H+}
interface
uses
Classes, SysUtils,Generics.collections,Generics.defaults,variants
{$IFDEF UNIX}
,BaseUnix,Sockets,streamex
{$ENDIF}
{$IFDEF WINDOWS}
,Windows, Winsock,Registry,ComObj,ActiveX
{$ENDIF}
;
type
TNetworkInterfaceRecord=record
Name:String;
Index:Integer;
IPv4:String;
MacAddress:String;
end;
TANetworkInterfaceRecord=array of TNetworkInterfaceRecord;
{ TNetworkInterfaces }
TNetworkInterfaces=class(TObject)
private
FInterfaces:TANetworkInterfaceRecord;
function GetInterfaces:TANetworkInterfaceRecord;
{$IFDEF UNIX}
function GetIPV4Address( IfName:String):String;
{$ENDIF}
{$IFDEF WINDOWS}
procedure SetWMIQuery(out SWbemLocator: olevariant;out WMIService: olevariant);
function GetIPV4Address(Index:UInt32):TStringArray;
{$ENDIF}
public
constructor Create;
property Interfaces:TANetworkInterfaceRecord read FInterfaces;
end;
{ TNetworkInterfaceComparer }
TNetworkInterfaceComparer=class(TInterfacedObject,specialize IComparer<TNetworkInterfaceRecord>)
public
function Compare(constref NIC1,NIC2: TNetworkInterfaceRecord): Integer;
end;
implementation
{$IFDEF UNIX}
const
NETDEV_FILE='/proc/net/dev';
NETDEV_INFO_DIR='/sys/class/net/';
SIOCGIFADDR = $8915;
IFNAMSIZ = 16; // Adjust if necessary
type
TIfReq = packed record
ifr_name: array[0..IFNAMSIZ-1] of char;
ifr_addr: sockaddr_in;
end;
TIPAddress = array[0..3] of Byte;
function inet_ntoa( IPv4:cuint32):String;
var
AParam:cuint32;
IPAddress:TIPAddress absolute AParam;
begin
AParam:=IPV4;
Result:=IntToStr(IPAddress[0]) + '.' + IntToStr(IPAddress[1]) + '.' +
IntToStr(IPAddress[2]) + '.' + IntToStr(IPAddress[3]);
end;
{$ENDIF}
{$IFDEF WINDOWS}
const
wbemFlagForwardOnly = $00000020;
{$ENDIF}
{ TNetworkInterfaceComparer }
function TNetworkInterfaceComparer.Compare(constref NIC1,
NIC2: TNetworkInterfaceRecord): Integer;
begin
if NIC1.Index<NIC2.Index then begin
Result:=-1;
end
else if NIC1.Index=NIC2.Index then begin
Result:=0;
end
else begin
Result:=1;
end;
end;
{ TNetworkInterfaces }
function TNetworkInterfaces.GetInterfaces: TANetworkInterfaceRecord;
var
Comparer:specialize IComparer<TNetworkInterfaceRecord>;
i,j:Integer;
{$IFDEF UNIX}
NetDev:TFileStream;
Reader:TStreamReader;
Line:String;
DevDir:String;
IfIndex:String;
IfName:String;
{$ENDIF}
{$IFDEF WINDOWS}
SWbemLocator: olevariant;
WMIService: olevariant;
QueryStr:String;
WbemObjectSet: olevariant;
WbemObject: olevariant;
oEnum: IEnumvariant;
iValue: longword;
IPV4Array:TStringArray;
{$ENDIF}
begin
// Ensure empty result
Result:=TANetworkInterfaceRecord.Create;
{$IFDEF UNIX}
// Get Active interfaces from /proc/net/dev
Reader:=NIL;
NetDev:=TFileStream.Create(NETDEV_FILE,fmOpenRead);
try
Reader:=TStreamReader.Create(NetDev);
while not Reader.Eof do begin
Line:=Reader.ReadLine;
// Interface lines have :
i:=Pos(':',Line);
if i>0 then begin
// Get interface name
ifName:=Trim(Copy(Line,1,i-1));
// Save it to the result
i:=Length(Result);SetLength(Result,i+1);
Result[i].Name:=IfName;
end;
end;
// Read the info from /sys/class/net
for i:=0 to Length(Result)-1 do begin
DevDir:=IncludeTrailingPathDelimiter(NETDEV_INFO_DIR+Result[i].Name);
// Read Index
NetDev.Free;NetDev:=NIL;
NetDev:=TFileStream.Create(DevDir+'ifindex',fmOpenRead);
Reader.Free;Reader:=NIL;
Reader:=TStreamReader.Create(NetDev);
//
IfIndex:=Reader.ReadLine;
Result[i].Index:=StrToInt(IfIndex);
// Read MacAddress
NetDev.Free;NetDev:=NIL;
NetDev:=TFileStream.Create(DevDir+'address',fmOpenRead);
Reader.Free;Reader:=NIL;
Reader:=TStreamReader.Create(NetDev);
Result[i].MacAddress:=Reader.ReadLine;
// Read IP address
Result[i].IPv4:=Self.GetIPV4Address(Result[i].Name);
end;
finally
Reader.Free;
NetDev.Free;
end;
{$ENDIF}
{$IFDEF WINDOWS}
Self.SetWMIQuery(SWbemLocator,WMIService);
// QueryStr:='SELECT * FROM Win32_NetworkAdapter WHERE Manufacturer != ''Microsoft'' AND NOT PNPDeviceID LIKE ''ROOT\\%''';
QueryStr:='SELECT * FROM Win32_NetworkAdapter WHERE Manufacturer != ''Microsoft''';
WbemObjectSet := WMIService.ExecQuery( QueryStr,'WQL', wbemFlagForwardOnly);
oEnum := IUnknown(WbemObjectSet._NewEnum) as IEnumVariant;
while oEnum.Next(1, WbemObject, iValue) = 0 do begin
i:=Length(Result);SetLength(Result,i+1);
// WINDOWS XP doesn't have InterfaceObject
if Win32MajorVersion>5 then begin
if not VarIsNull(WbemObject.InterfaceIndex) then begin
Result[i].Index:=WbemObject.InterfaceIndex;
end;
end
else begin
// WINDOWS XP use DeviceID
if not VarIsNull(WbemObject.Index) then begin
Result[i].Index:=WbemObject.DeviceID;
end;
end;
if not VarIsNull(WbemObject.Name) then begin
Result[i].Name:=WbemObject.Name;
end;
if not VarIsNull(WbemObject.MACAddress) then begin
Result[i].MacAddress := WbemObject.MACAddress;
end;
if not VarIsNull(WbemObject.Description) then begin
// Get IPv4, one adapter can have more than 1 address
IPV4Array :=Self.GetIPV4Address(WbemObject.DeviceID);
if Length(IPV4Array)>0 then begin
Result[i].IPv4:=IPV4Array[0];
// if there are more than 1 ip, make additional entry
if Length(IPV4Array) > 1 then begin
for j:=1 to Length(IPV4Array) - 1 do begin
i:=Length(Result);SetLength(Result,i+1);
// Copy other properties
Result[i].Index:=Result[i-1].Index;
Result[i].Name:=Result[i-1].Name;
Result[i].MacAddress:=Result[i-1].MacAddress;
Result[i].IPv4:=IPV4Array[j];
end;
end;
end;
end;
end;
{$ENDIF}
// Sort base on index
Comparer:=TNetworkInterfaceComparer.Create;
specialize TArrayHelper<TNetworkInterfaceRecord>.Sort(Result,Comparer);
end;
{$IFDEF WINDOWS}
procedure TNetworkInterfaces.SetWMIQuery(out SWbemLocator: olevariant; out
WMIService: olevariant);
const
WbemUser = '';
WbemPassword = '';
WbemComputer = 'localhost';
begin
SWbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
WMIService := SWbemLocator.ConnectServer(WbemComputer, 'root\CIMV2',
WbemUser, WbemPassword);
end;
function OccurrencesOfChar(const S: string; const C: char): integer;
var
i: integer;
begin
Result := 0;
for i := 1 to Length(S) do begin
if S[i] = C then begin
Inc(Result);
end;
end;
end;
function TNetworkInterfaces.GetIPV4Address(Index: UInt32): TStringArray;
var
SWbemLocator: olevariant;
WMIService: olevariant;
WbemObjectSet: olevariant;
WbemObject: olevariant;
oEnum: IEnumvariant;
iValue: longword;
i,j: integer;
begin
Result:=TStringArray.Create;
Self.SetWMIQuery(SWbemLocator,WMIService);
WbemObjectSet := WMIService.ExecQuery(
'SELECT * FROM Win32_NetworkAdapterConfiguration WHERE Index= ''' +
IntToStr(Index) + '''', 'WQL', wbemFlagForwardOnly);
oEnum := IUnknown(WbemObjectSet._NewEnum) as IEnumVariant;
//
while oEnum.Next(1, WbemObject, iValue) = 0 do begin
if not VarIsClear(WbemObject.IPAddress) and not VarIsNull(WbemObject.IPAddress) then begin
for i := VarArrayLowBound(WbemObject.IPAddress, 1)
to VarArrayHighBound(WbemObject.IPAddress, 1)
do begin
// IPV4
if OccurrencesOfChar(WbemObject.IPAddress[i], '.') = 3 then begin
j:=Length(Result);SetLength(Result,j+1);
Result[j]:=WbemObject.IPAddress[i];
end;
end;
end;
end;
end;
{$ENDIF}
{$IFDEF UNIX}
function TNetworkInterfaces.GetIPV4Address(IfName: String): String;
var
fd: TSocket;
ifr: TIfReq;
begin
fd := fpsocket(AF_INET, SOCK_DGRAM, 0);
if fd < 0 then
ExitCode := 1;
ifr.ifr_addr.sin_family := AF_INET;
StrPCopy(ifr.ifr_name, IfName);
if Fpioctl(fd, SIOCGIFADDR, @ifr) < 0 then
ExitCode := 1;
Result:=inet_ntoa(ifr.ifr_addr.sin_addr.s_addr);
CloseSocket(fd);
end;
{$ENDIF}
constructor TNetworkInterfaces.Create;
begin
Self.FInterfaces:=Self.GetInterfaces;
end;
end.