1 { Copyright (C) 2005 Bas Steendijk and Peter Green
2 For conditions of distribution and use, see copyright notice in zlib_license.txt
3 which is included in the package
4 ----------------------------------------------------------------------------- }
7 unit to get various local system config
10 - get IP addresses assigned to local interfaces.
11 both IPv4 and IPv6, or one address family in isolation.
12 works on both windows and linux.
16 - localhost IPs (127.0.0.1, ::1) may be returned, the app must not expect them to be in or not in.
17 (typically, they're returned on linux and not on windows)
19 - normal behavior is to return all v6 IPs, including link local (fe80::).
20 an app that doesn't want link local IPs has to filter them out.
21 windows XP returns only one, global scope, v6 IP, due to shortcomings.
25 - get system DNS servers
37 {$include lcoreconfig.inc}
39 function getlocalips:tbiniplist;
40 function getv4localips:tbiniplist;
42 function getv6localips:tbiniplist;
45 function getsystemdnsservers:tbiniplist;
46 function getsystemhostname:ansistring;
53 baseunix,sockets,sysutils;
55 function getv6localips:tbiniplist;
62 result := biniplist_new;
64 assignfile(t,'/proc/net/if_inet6');
66 if ioresult <> 0 then exit; {none found, return empty list}
67 while not eof(t) do begin
70 for a := 0 to 7 do begin
71 if (s2 <> '') then s2 := s2 + ':';
72 s2 := s2 + copy(s,(a shl 2)+1,4);
75 if ip.family <> 0 then biniplist_add(result,ip);
80 function getv4localips:tbiniplist;
91 ifr_ifrn:array [0..IF_NAMESIZE-1] of char;
95 tifrecarr=array[0..999] of tifrec;
104 result := biniplist_new;
106 {must create a socket for this}
107 s := fpsocket(AF_INET,SOCK_DGRAM,0);
108 if (s < 0) then raise exception.create('getv4localips unable to create socket');
110 fillchar(ifc,sizeof(ifc),0);
112 {get size of IP record list}
113 if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then raise exception.create('getv4localips ioctl failed 1');
115 {allocate it, with extra room in case there's more interfaces added (as recommended)}
116 getmem(ifr,ifc.ifc_len shl 1);
120 if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then raise exception.create('getv4localips ioctl failed 2');
122 fillchar(ad,sizeof(ad),0);
124 for a := (ifc.ifc_len div sizeof (tifrec))-1 downto 0 do begin
125 ad := @ifr[a].ifru_addr;
126 ip := inaddrvtobinip(ad^);
127 biniplist_add(result,ip);
134 function getlocalips:tbiniplist;
136 result := getv4localips;
138 biniplist_addlist(result,getv6localips);
145 sysutils,windows,winsock,dnssync;
147 {the following code's purpose is to determine what IP windows would come from, to reach an IP
148 it can be abused to find if there's any global v6 IPs on a local interface}
150 SIO_ROUTING_INTERFACE_QUERY = $c8000014;
151 function WSAIoctl(s: TSocket; code:integer; const Buf; len: Integer; var output; outlen:integer; var outreturned: Integer; overlapped:pointer; completion: pointer): Integer; stdcall; external 'ws2_32.dll' name 'WSAIoctl';
153 function getlocalipforip(const ip:tbinip):tbinip;
157 inaddrv,inaddrv2:tinetsockaddrv;
158 srcx:winsock.tsockaddr absolute inaddrv2;
160 makeinaddrv(ip,'0',inaddrv);
161 handle := Socket(inaddrv.inaddr.family,SOCK_DGRAM,IPPROTO_UDP);
162 if (handle < 0) then begin
163 {this happens on XP without an IPv6 stack
164 i can either fail with an exception, or with a "null result". an exception is annoying in the IDE}
165 {fillchar(result,sizeof(result),0);
167 raise exception.create('getlocalipforip: can''t create socket');
169 if WSAIoctl(handle, SIO_ROUTING_INTERFACE_QUERY, inaddrv, sizeof(inaddrv), inaddrv2, sizeof(inaddrv2), a, nil, nil) <> 0
170 then raise exception.create('getlocalipforip failed with error: '+inttostr(wsagetlasterror));
171 result := inaddrvtobinip(inaddrv2);
176 function getv4localips:tbiniplist;
182 result := biniplist_new;
184 templist := getlocalips;
185 for a := biniplist_getcount(templist)-1 downto 0 do begin
186 biniptemp := biniplist_get(templist,a);
187 if biniptemp.family = AF_INET then biniplist_add(result,biniptemp);
192 function getv6localips:tbiniplist;
198 result := biniplist_new;
200 templist := getlocalips;
201 for a := biniplist_getcount(templist)-1 downto 0 do begin
202 biniptemp := biniplist_get(templist,a);
203 if biniptemp.family = AF_INET6 then biniplist_add(result,biniptemp);
208 function getlocalips:tbiniplist;
213 result := forwardlookuplist('',0);
217 {windows XP doesn't add v6 IPs
218 if we find no v6 IPs in the list, add one using a hack}
219 for a := biniplist_getcount(result)-1 downto 0 do begin
220 ip := biniplist_get(result,a);
221 if ip.family = AF_INET6 then exit;
225 ip := getlocalipforip(ipstrtobinf('2001:200::'));
226 if (ip.family = AF_INET6) then biniplist_add(result,ip);
241 MAX_HOSTNAME_LEN = 132;
242 MAX_DOMAIN_NAME_LEN = 132;
243 MAX_SCOPE_ID_LEN = 260 ;
244 MAX_ADAPTER_NAME_LENGTH = 260;
245 MAX_ADAPTER_ADDRESS_LENGTH = 8;
246 MAX_ADAPTER_DESCRIPTION_LENGTH = 132;
247 ERROR_BUFFER_OVERFLOW = 111;
248 MIB_IF_TYPE_ETHERNET = 6;
249 MIB_IF_TYPE_TOKENRING = 9;
250 MIB_IF_TYPE_FDDI = 15;
251 MIB_IF_TYPE_PPP = 23;
252 MIB_IF_TYPE_LOOPBACK = 24;
253 MIB_IF_TYPE_SLIP = 28;
257 tip_addr_string=packed record
259 IpAddress : array[0..15] of ansichar;
260 ipmask : array[0..15] of ansichar;
263 pip_addr_string=^tip_addr_string;
264 tFIXED_INFO=packed record
265 HostName : array[0..MAX_HOSTNAME_LEN-1] of ansichar;
266 DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of ansichar;
267 currentdnsserver : pip_addr_string;
268 dnsserverlist : tip_addr_string;
270 ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of ansichar;
271 enablerouting : longbool;
272 enableproxy : longbool;
273 enabledns : longbool;
275 pFIXED_INFO=^tFIXED_INFO;
279 getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall;
281 function callGetNetworkParams:pFIXED_INFO;
283 fixed_info : pfixed_info;
284 fixed_info_len : longint;
287 if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll');
288 if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams');
289 if not assigned(getnetworkparams) then exit;
291 if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit;
292 //fixed_info_len :=sizeof(tfixed_info);
293 getmem(fixed_info,fixed_info_len);
294 if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin
298 result := fixed_info;
303 function getsystemdnsservers:tbiniplist;
306 fixed_info : pfixed_info;
307 currentdnsserver : pip_addr_string;
317 result := biniplist_new;
320 fixed_info := callgetnetworkparams;
321 if fixed_info = nil then exit;
323 currentdnsserver := @(fixed_info.dnsserverlist);
324 while assigned(currentdnsserver) do begin
325 ip := ipstrtobinf(currentdnsserver.IpAddress);
326 if (ip.family <> 0) then biniplist_add(result,ip);
327 currentdnsserver := currentdnsserver.next;
332 assignfile(t,'/etc/resolv.conf');
334 if ioresult <> 0 then exit;
336 while not eof(t) do begin
338 if not (copy(s,1,10) = 'nameserver') then continue;
340 while s <> '' do begin
341 if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break;
344 if a <> 0 then s := copy(s,1,a-1);
346 if a <> 0 then s := copy(s,1,a-1);
348 ip := ipstrtobinf(s);
349 if (ip.family <> 0) then biniplist_add(result,ip);
356 function getsystemhostname:ansistring;
359 fixed_info : pfixed_info;
366 fixed_info := callgetnetworkparams;
367 if fixed_info = nil then exit;
369 result := fixed_info.hostname;
370 if fixed_info.domainname <> '' then result := result + '.'+fixed_info.domainname;
375 assignfile(t,'/etc/hostname');
377 if ioresult <> 0 then exit;