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,dnswin;
\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
261 result := winforwardlookuplist('',0,error);
\r
265 {windows XP doesn't add v6 IPs
\r
266 if we find no v6 IPs in the list, add one using a hack}
\r
267 for a := biniplist_getcount(result)-1 downto 0 do begin
\r
268 ip := biniplist_get(result,a);
\r
269 if ip.family = AF_INET6 then exit;
\r
273 ip := getlocalipforip(ipstrtobinf('2001:200::'));
\r
274 if (ip.family = AF_INET6) then biniplist_add(result,ip);
\r
289 MAX_HOSTNAME_LEN = 132;
\r
290 MAX_DOMAIN_NAME_LEN = 132;
\r
291 MAX_SCOPE_ID_LEN = 260 ;
\r
292 MAX_ADAPTER_NAME_LENGTH = 260;
\r
293 MAX_ADAPTER_ADDRESS_LENGTH = 8;
\r
294 MAX_ADAPTER_DESCRIPTION_LENGTH = 132;
\r
295 ERROR_BUFFER_OVERFLOW = 111;
\r
296 MIB_IF_TYPE_ETHERNET = 6;
\r
297 MIB_IF_TYPE_TOKENRING = 9;
\r
298 MIB_IF_TYPE_FDDI = 15;
\r
299 MIB_IF_TYPE_PPP = 23;
\r
300 MIB_IF_TYPE_LOOPBACK = 24;
\r
301 MIB_IF_TYPE_SLIP = 28;
\r
305 tip_addr_string=packed record
\r
307 IpAddress : array[0..15] of ansichar;
\r
308 ipmask : array[0..15] of ansichar;
\r
311 pip_addr_string=^tip_addr_string;
\r
312 tFIXED_INFO=packed record
\r
313 HostName : array[0..MAX_HOSTNAME_LEN-1] of ansichar;
\r
314 DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of ansichar;
\r
315 currentdnsserver : pip_addr_string;
\r
316 dnsserverlist : tip_addr_string;
\r
317 nodetype : longint;
\r
318 ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of ansichar;
\r
319 enablerouting : longbool;
\r
320 enableproxy : longbool;
\r
321 enabledns : longbool;
\r
323 pFIXED_INFO=^tFIXED_INFO;
\r
326 iphlpapi : thandle;
\r
327 getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall;
\r
329 function callGetNetworkParams:pFIXED_INFO;
\r
331 fixed_info : pfixed_info;
\r
332 fixed_info_len : longint;
\r
335 if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll');
\r
336 if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams');
\r
337 if not assigned(getnetworkparams) then exit;
\r
338 fixed_info_len := 0;
\r
339 if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit;
\r
340 //fixed_info_len :=sizeof(tfixed_info);
\r
341 getmem(fixed_info,fixed_info_len);
\r
342 if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin
\r
343 freemem(fixed_info);
\r
346 result := fixed_info;
\r
351 function getsystemdnsservers:tbiniplist;
\r
354 fixed_info : pfixed_info;
\r
355 currentdnsserver : pip_addr_string;
\r
365 result := biniplist_new;
\r
368 fixed_info := callgetnetworkparams;
\r
369 if fixed_info = nil then exit;
\r
371 currentdnsserver := @(fixed_info.dnsserverlist);
\r
372 while assigned(currentdnsserver) do begin
\r
373 ip := ipstrtobinf(currentdnsserver.IpAddress);
\r
374 if (ip.family <> 0) then biniplist_add(result,ip);
\r
375 currentdnsserver := currentdnsserver.next;
\r
377 freemem(fixed_info);
\r
380 assignfile(t,'/etc/resolv.conf');
\r
381 {$i-}reset(t);{$i+}
\r
382 if ioresult <> 0 then exit;
\r
384 while not eof(t) do begin
\r
386 if not (copy(s,1,10) = 'nameserver') then continue;
\r
387 s := copy(s,11,500);
\r
388 while s <> '' do begin
\r
389 if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break;
\r
392 if a <> 0 then s := copy(s,1,a-1);
\r
394 if a <> 0 then s := copy(s,1,a-1);
\r
396 ip := ipstrtobinf(s);
\r
397 if (ip.family <> 0) then biniplist_add(result,ip);
\r
404 function gethostname:ansistring;
\r
406 fixed_info : pfixed_info;
\r
409 fixed_info := callgetnetworkparams;
\r
410 if fixed_info = nil then exit;
\r
412 result := fixed_info.hostname;
\r
413 if fixed_info.domainname <> '' then result := result + '.'+fixed_info.domainname;
\r
415 freemem(fixed_info);
\r