1 { Copyright (C) 2005 Bas Steendijk and Peter Green
\r
2 For conditions of distribution and use, see copyright notice in zlib_license.txt
\r
3 which is included in the package
\r
4 ----------------------------------------------------------------------------- }
\r
7 unit to get various local system config
\r
10 - get IP addresses assigned to local interfaces.
\r
11 both IPv4 and IPv6, or one address family in isolation.
\r
12 works on both windows and linux.
\r
19 - mac OS X (probably works on freeBSD too)
\r
23 - localhost IPs (127.0.0.1, ::1) may be returned, the app must not expect them to be in or not in.
\r
24 (typically, they're returned on linux and not on windows)
\r
26 - normal behavior is to return all v6 IPs, including link local (fe80::).
\r
27 an app that doesn't want link local IPs has to filter them out.
\r
28 windows XP returns only one, global scope, v6 IP, due to shortcomings.
\r
32 - get system DNS servers
\r
34 - get system hostname (if not on windows, use freepascal's "unix")
\r
42 uses binipstuff,pgtypes;
\r
44 {$include lcoreconfig.inc}
\r
46 function getlocalips:tbiniplist;
\r
47 function getv4localips:tbiniplist;
\r
49 function getv6localips:tbiniplist;
\r
52 function getsystemdnsservers:tbiniplist;
\r
55 function gethostname:ansistring;
\r
63 baseunix,sockets,sysutils;
\r
66 function getlocalips_internal(wantfamily:integer):tbiniplist;
\r
70 {$ifdef linux}SIOCGIFCONF=$8912;{$endif}
\r
71 {$ifdef bsd}{$ifdef cpu386}SIOCGIFCONF=$C0086924;{$endif}{$endif}
\r
73 {amd64: mac OS X: $C00C6924; freeBSD: $c0106924}
\r
75 tifconf=packed record
\r
80 tifrec=packed record
\r
81 ifr_ifrn:array [0..IF_NAMESIZE-1] of char;
\r
82 ifru_addr:TSockAddr;
\r
88 ifr,ifr2,ifrmax:^tifrec;
\r
89 lastlen,len:integer;
\r
93 result := biniplist_new;
\r
95 {must create a socket for this}
\r
96 s := fpsocket(AF_INET,SOCK_DGRAM,0);
\r
97 if (s < 0) then raise exception.create('getv4localips unable to create socket');
\r
99 fillchar(ifc,sizeof(ifc),0);
\r
104 len := 2*sizeof(tifrec);
\r
107 reallocmem(ifr,len);
\r
108 ifc.ifc_len := len;
\r
109 ifc.ifcu_rec := ifr;
\r
110 {get IP record list}
\r
111 if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then begin
\r
112 raise exception.create('getv4localips ioctl failed');
\r
114 if (lastlen = ifc.ifc_len) then break;
\r
115 lastlen := ifc.ifc_len;
\r
120 ifrmax := pointer(taddrint(ifr) + ifc.ifc_len);
\r
121 while (ifr2 < ifrmax) do begin
\r
122 lastlen := taddrint(ifrmax) - taddrint(ifr2);
\r
123 if (lastlen < sizeof(tifrec)) then break; {not enough left}
\r
125 ad := @ifr2.ifru_addr;
\r
128 len := ad.inaddr.len + IF_NAMESIZE;
\r
129 if (len < sizeof(tifrec)) then
\r
131 len := sizeof(tifrec);
\r
133 if (len < sizeof(tifrec)) then break; {not enough left}
\r
135 ip := inaddrvtobinip(ad^);
\r
136 if (ip.family <> 0) and ((ip.family = wantfamily) or (wantfamily = 0)) then biniplist_add(result,ip);
\r
137 inc(taddrint(ifr2),len);
\r
145 function getv6localips:tbiniplist;
\r
152 result := biniplist_new;
\r
154 assignfile(t,'/proc/net/if_inet6');
\r
155 {$i-}reset(t);{$i+}
\r
156 if ioresult <> 0 then begin
\r
157 {not on linux, try if this OS uses the other way to return v6 addresses}
\r
158 result := getlocalips_internal(AF_INET6);
\r
161 while not eof(t) do begin
\r
164 for a := 0 to 7 do begin
\r
165 if (s2 <> '') then s2 := s2 + ':';
\r
166 s2 := s2 + copy(s,(a shl 2)+1,4);
\r
169 if ip.family <> 0 then biniplist_add(result,ip);
\r
175 function getv4localips:tbiniplist;
\r
177 result := getlocalips_internal(AF_INET);
\r
180 function getlocalips:tbiniplist;
\r
182 result := getv4localips;
\r
184 biniplist_addlist(result,getv6localips);
\r
191 sysutils,windows,winsock,dnssync,dnscore;
\r
193 {the following code's purpose is to determine what IP windows would come from, to reach an IP
\r
194 it can be abused to find if there's any global v6 IPs on a local interface}
\r
196 SIO_ROUTING_INTERFACE_QUERY = $c8000014;
\r
197 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';
\r
199 function getlocalipforip(const ip:tbinip):tbinip;
\r
203 inaddrv,inaddrv2:tinetsockaddrv;
\r
204 srcx:winsock.tsockaddr absolute inaddrv2;
\r
206 makeinaddrv(ip,'0',inaddrv);
\r
207 handle := Socket(inaddrv.inaddr.family,SOCK_DGRAM,IPPROTO_UDP);
\r
208 if (handle < 0) then begin
\r
209 {this happens on XP without an IPv6 stack
\r
210 i can either fail with an exception, or with a "null result". an exception is annoying in the IDE}
\r
211 {fillchar(result,sizeof(result),0);
\r
213 raise exception.create('getlocalipforip: can''t create socket');
\r
215 if WSAIoctl(handle, SIO_ROUTING_INTERFACE_QUERY, inaddrv, sizeof(inaddrv), inaddrv2, sizeof(inaddrv2), a, nil, nil) <> 0
\r
216 then raise exception.create('getlocalipforip failed with error: '+inttostr(wsagetlasterror));
\r
217 result := inaddrvtobinip(inaddrv2);
\r
218 closesocket(handle);
\r
222 function getv4localips:tbiniplist;
\r
224 templist:tbiniplist;
\r
228 result := biniplist_new;
\r
230 templist := getlocalips;
\r
231 for a := biniplist_getcount(templist)-1 downto 0 do begin
\r
232 biniptemp := biniplist_get(templist,a);
\r
233 if biniptemp.family = AF_INET then biniplist_add(result,biniptemp);
\r
238 function getv6localips:tbiniplist;
\r
240 templist:tbiniplist;
\r
244 result := biniplist_new;
\r
246 templist := getlocalips;
\r
247 for a := biniplist_getcount(templist)-1 downto 0 do begin
\r
248 biniptemp := biniplist_get(templist,a);
\r
249 if biniptemp.family = AF_INET6 then biniplist_add(result,biniptemp);
\r
254 function getlocalips:tbiniplist;
\r
258 usewindnstemp:boolean;
\r
260 {this lookup must always be done with the windows API lookup
\r
261 setting usewindns to false on windows will fail with infinite recursion}
\r
262 usewindnstemp := usewindns;
\r
264 result := forwardlookuplist('',0);
\r
265 usewindns := usewindnstemp;
\r
269 {windows XP doesn't add v6 IPs
\r
270 if we find no v6 IPs in the list, add one using a hack}
\r
271 for a := biniplist_getcount(result)-1 downto 0 do begin
\r
272 ip := biniplist_get(result,a);
\r
273 if ip.family = AF_INET6 then exit;
\r
277 ip := getlocalipforip(ipstrtobinf('2001:200::'));
\r
278 if (ip.family = AF_INET6) then biniplist_add(result,ip);
\r
293 MAX_HOSTNAME_LEN = 132;
\r
294 MAX_DOMAIN_NAME_LEN = 132;
\r
295 MAX_SCOPE_ID_LEN = 260 ;
\r
296 MAX_ADAPTER_NAME_LENGTH = 260;
\r
297 MAX_ADAPTER_ADDRESS_LENGTH = 8;
\r
298 MAX_ADAPTER_DESCRIPTION_LENGTH = 132;
\r
299 ERROR_BUFFER_OVERFLOW = 111;
\r
300 MIB_IF_TYPE_ETHERNET = 6;
\r
301 MIB_IF_TYPE_TOKENRING = 9;
\r
302 MIB_IF_TYPE_FDDI = 15;
\r
303 MIB_IF_TYPE_PPP = 23;
\r
304 MIB_IF_TYPE_LOOPBACK = 24;
\r
305 MIB_IF_TYPE_SLIP = 28;
\r
309 tip_addr_string=packed record
\r
311 IpAddress : array[0..15] of ansichar;
\r
312 ipmask : array[0..15] of ansichar;
\r
315 pip_addr_string=^tip_addr_string;
\r
316 tFIXED_INFO=packed record
\r
317 HostName : array[0..MAX_HOSTNAME_LEN-1] of ansichar;
\r
318 DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of ansichar;
\r
319 currentdnsserver : pip_addr_string;
\r
320 dnsserverlist : tip_addr_string;
\r
321 nodetype : longint;
\r
322 ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of ansichar;
\r
323 enablerouting : longbool;
\r
324 enableproxy : longbool;
\r
325 enabledns : longbool;
\r
327 pFIXED_INFO=^tFIXED_INFO;
\r
330 iphlpapi : thandle;
\r
331 getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall;
\r
333 function callGetNetworkParams:pFIXED_INFO;
\r
335 fixed_info : pfixed_info;
\r
336 fixed_info_len : longint;
\r
339 if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll');
\r
340 if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams');
\r
341 if not assigned(getnetworkparams) then exit;
\r
342 fixed_info_len := 0;
\r
343 if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit;
\r
344 //fixed_info_len :=sizeof(tfixed_info);
\r
345 getmem(fixed_info,fixed_info_len);
\r
346 if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin
\r
347 freemem(fixed_info);
\r
350 result := fixed_info;
\r
355 function getsystemdnsservers:tbiniplist;
\r
358 fixed_info : pfixed_info;
\r
359 currentdnsserver : pip_addr_string;
\r
369 result := biniplist_new;
\r
372 fixed_info := callgetnetworkparams;
\r
373 if fixed_info = nil then exit;
\r
375 currentdnsserver := @(fixed_info.dnsserverlist);
\r
376 while assigned(currentdnsserver) do begin
\r
377 ip := ipstrtobinf(currentdnsserver.IpAddress);
\r
378 if (ip.family <> 0) then biniplist_add(result,ip);
\r
379 currentdnsserver := currentdnsserver.next;
\r
381 freemem(fixed_info);
\r
384 assignfile(t,'/etc/resolv.conf');
\r
385 {$i-}reset(t);{$i+}
\r
386 if ioresult <> 0 then exit;
\r
388 while not eof(t) do begin
\r
390 if not (copy(s,1,10) = 'nameserver') then continue;
\r
391 s := copy(s,11,500);
\r
392 while s <> '' do begin
\r
393 if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break;
\r
396 if a <> 0 then s := copy(s,1,a-1);
\r
398 if a <> 0 then s := copy(s,1,a-1);
\r
400 ip := ipstrtobinf(s);
\r
401 if (ip.family <> 0) then biniplist_add(result,ip);
\r
408 function gethostname:ansistring;
\r
410 fixed_info : pfixed_info;
\r
413 fixed_info := callgetnetworkparams;
\r
414 if fixed_info = nil then exit;
\r
416 result := fixed_info.hostname;
\r
417 if fixed_info.domainname <> '' then result := result + '.'+fixed_info.domainname;
\r
419 freemem(fixed_info);
\r