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
44 uses binipstuff,pgtypes;
\r
46 {$include lcoreconfig.inc}
\r
48 function getlocalips:tbiniplist;
\r
49 function getv4localips:tbiniplist;
\r
51 function getv6localips:tbiniplist;
\r
54 function getsystemdnsservers:tbiniplist;
\r
57 function gethostname:ansistring;
\r
65 baseunix,sockets,sysutils;
\r
68 function getlocalips_internal(wantfamily:integer):tbiniplist;
\r
72 {$ifdef linux}SIOCGIFCONF=$8912;{$endif}
\r
73 {$ifdef bsd}{$ifdef cpu386}SIOCGIFCONF=$C0086924;{$endif}{$endif}
\r
75 {amd64: mac OS X: $C00C6924; freeBSD: $c0106924}
\r
77 tifconf=packed record
\r
82 tifrec=packed record
\r
83 ifr_ifrn:array [0..IF_NAMESIZE-1] of char;
\r
84 ifru_addr:TSockAddr;
\r
90 ifr,ifr2,ifrmax:^tifrec;
\r
91 lastlen,len:integer;
\r
95 result := biniplist_new;
\r
97 {must create a socket for this}
\r
98 s := fpsocket(AF_INET,SOCK_DGRAM,0);
\r
99 if (s < 0) then raise exception.create('getv4localips unable to create socket');
\r
101 fillchar(ifc,sizeof(ifc),0);
\r
106 len := 2*sizeof(tifrec);
\r
109 reallocmem(ifr,len);
\r
110 ifc.ifc_len := len;
\r
111 ifc.ifcu_rec := ifr;
\r
112 {get IP record list}
\r
113 if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then begin
\r
114 raise exception.create('getv4localips ioctl failed');
\r
116 if (lastlen = ifc.ifc_len) then break;
\r
117 lastlen := ifc.ifc_len;
\r
122 ifrmax := pointer(taddrint(ifr) + ifc.ifc_len);
\r
123 while (ifr2 < ifrmax) do begin
\r
124 lastlen := taddrint(ifrmax) - taddrint(ifr2);
\r
125 if (lastlen < sizeof(tifrec)) then break; {not enough left}
\r
127 ad := @ifr2.ifru_addr;
\r
130 len := ad.inaddr.len + IF_NAMESIZE;
\r
131 if (len < sizeof(tifrec)) then
\r
133 len := sizeof(tifrec);
\r
135 if (len < sizeof(tifrec)) then break; {not enough left}
\r
137 ip := inaddrvtobinip(ad^);
\r
138 if (ip.family <> 0) and ((ip.family = wantfamily) or (wantfamily = 0)) then biniplist_add(result,ip);
\r
139 inc(taddrint(ifr2),len);
\r
147 function getv6localips:tbiniplist;
\r
154 result := biniplist_new;
\r
156 assignfile(t,'/proc/net/if_inet6');
\r
157 {$i-}reset(t);{$i+}
\r
158 if ioresult <> 0 then begin
\r
159 {not on linux, try if this OS uses the other way to return v6 addresses}
\r
160 result := getlocalips_internal(AF_INET6);
\r
163 while not eof(t) do begin
\r
166 for a := 0 to 7 do begin
\r
167 if (s2 <> '') then s2 := s2 + ':';
\r
168 s2 := s2 + copy(s,(a shl 2)+1,4);
\r
171 if ip.family <> 0 then biniplist_add(result,ip);
\r
177 function getv4localips:tbiniplist;
\r
179 result := getlocalips_internal(AF_INET);
\r
182 function getlocalips:tbiniplist;
\r
184 result := getv4localips;
\r
186 biniplist_addlist(result,getv6localips);
\r
193 sysutils,windows,winsock,dnswin;
\r
195 {the following code's purpose is to determine what IP windows would come from, to reach an IP
\r
196 it can be abused to find if there's any global v6 IPs on a local interface}
\r
198 SIO_ROUTING_INTERFACE_QUERY = $c8000014;
\r
199 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
201 function getlocalipforip(const ip:tbinip):tbinip;
\r
205 inaddrv,inaddrv2:tinetsockaddrv;
\r
206 srcx:winsock.tsockaddr absolute inaddrv2;
\r
208 makeinaddrv(ip,'0',inaddrv);
\r
209 handle := Socket(inaddrv.inaddr.family,SOCK_DGRAM,IPPROTO_UDP);
\r
210 if (handle < 0) then begin
\r
211 {this happens on XP without an IPv6 stack
\r
212 i can either fail with an exception, or with a "null result". an exception is annoying in the IDE}
\r
213 {fillchar(result,sizeof(result),0);
\r
215 raise exception.create('getlocalipforip: can''t create socket');
\r
217 if WSAIoctl(handle, SIO_ROUTING_INTERFACE_QUERY, inaddrv, sizeof(inaddrv), inaddrv2, sizeof(inaddrv2), a, nil, nil) <> 0
\r
218 then raise exception.create('getlocalipforip failed with error: '+inttostr(wsagetlasterror));
\r
219 result := inaddrvtobinip(inaddrv2);
\r
220 closesocket(handle);
\r
224 function getv4localips:tbiniplist;
\r
226 templist:tbiniplist;
\r
230 result := biniplist_new;
\r
232 templist := getlocalips;
\r
233 for a := biniplist_getcount(templist)-1 downto 0 do begin
\r
234 biniptemp := biniplist_get(templist,a);
\r
235 if biniptemp.family = AF_INET then biniplist_add(result,biniptemp);
\r
240 function getv6localips:tbiniplist;
\r
242 templist:tbiniplist;
\r
246 result := biniplist_new;
\r
248 templist := getlocalips;
\r
249 for a := biniplist_getcount(templist)-1 downto 0 do begin
\r
250 biniptemp := biniplist_get(templist,a);
\r
251 if biniptemp.family = AF_INET6 then biniplist_add(result,biniptemp);
\r
256 function getlocalips:tbiniplist;
\r
260 usewindnstemp:boolean;
\r
263 result := winforwardlookuplist('',0,error);
\r
267 {windows XP doesn't add v6 IPs
\r
268 if we find no v6 IPs in the list, add one using a hack}
\r
269 for a := biniplist_getcount(result)-1 downto 0 do begin
\r
270 ip := biniplist_get(result,a);
\r
271 if ip.family = AF_INET6 then exit;
\r
275 ip := getlocalipforip(ipstrtobinf('2001:200::'));
\r
276 if (ip.family = AF_INET6) then biniplist_add(result,ip);
\r
291 MAX_HOSTNAME_LEN = 132;
\r
292 MAX_DOMAIN_NAME_LEN = 132;
\r
293 MAX_SCOPE_ID_LEN = 260 ;
\r
294 MAX_ADAPTER_NAME_LENGTH = 260;
\r
295 MAX_ADAPTER_ADDRESS_LENGTH = 8;
\r
296 MAX_ADAPTER_DESCRIPTION_LENGTH = 132;
\r
297 ERROR_BUFFER_OVERFLOW = 111;
\r
298 MIB_IF_TYPE_ETHERNET = 6;
\r
299 MIB_IF_TYPE_TOKENRING = 9;
\r
300 MIB_IF_TYPE_FDDI = 15;
\r
301 MIB_IF_TYPE_PPP = 23;
\r
302 MIB_IF_TYPE_LOOPBACK = 24;
\r
303 MIB_IF_TYPE_SLIP = 28;
\r
307 tip_addr_string=packed record
\r
309 IpAddress : array[0..15] of ansichar;
\r
310 ipmask : array[0..15] of ansichar;
\r
313 pip_addr_string=^tip_addr_string;
\r
314 tFIXED_INFO=packed record
\r
315 HostName : array[0..MAX_HOSTNAME_LEN-1] of ansichar;
\r
316 DomainName : array[0..MAX_DOMAIN_NAME_LEN-1] of ansichar;
\r
317 currentdnsserver : pip_addr_string;
\r
318 dnsserverlist : tip_addr_string;
\r
319 nodetype : longint;
\r
320 ScopeId : array[0..MAX_SCOPE_ID_LEN + 4] of ansichar;
\r
321 enablerouting : longbool;
\r
322 enableproxy : longbool;
\r
323 enabledns : longbool;
\r
325 pFIXED_INFO=^tFIXED_INFO;
\r
328 iphlpapi : thandle;
\r
329 getnetworkparams : function(pFixedInfo : PFIXED_INFO;OutBufLen : plongint) : longint;stdcall;
\r
331 function callGetNetworkParams:pFIXED_INFO;
\r
333 fixed_info : pfixed_info;
\r
334 fixed_info_len : longint;
\r
337 if iphlpapi=0 then iphlpapi := loadlibrary('iphlpapi.dll');
\r
338 if not assigned(getnetworkparams) then @getnetworkparams := getprocaddress(iphlpapi,'GetNetworkParams');
\r
339 if not assigned(getnetworkparams) then exit;
\r
340 fixed_info_len := 0;
\r
341 if GetNetworkParams(nil,@fixed_info_len)<>ERROR_BUFFER_OVERFLOW then exit;
\r
342 //fixed_info_len :=sizeof(tfixed_info);
\r
343 getmem(fixed_info,fixed_info_len);
\r
344 if GetNetworkParams(fixed_info,@fixed_info_len)<>0 then begin
\r
345 freemem(fixed_info);
\r
348 result := fixed_info;
\r
353 function getsystemdnsservers:tbiniplist;
\r
356 fixed_info : pfixed_info;
\r
357 currentdnsserver : pip_addr_string;
\r
367 result := biniplist_new;
\r
370 fixed_info := callgetnetworkparams;
\r
371 if fixed_info = nil then exit;
\r
373 currentdnsserver := @(fixed_info.dnsserverlist);
\r
374 while assigned(currentdnsserver) do begin
\r
375 ip := ipstrtobinf(currentdnsserver.IpAddress);
\r
376 if (ip.family <> 0) then biniplist_add(result,ip);
\r
377 currentdnsserver := currentdnsserver.next;
\r
379 freemem(fixed_info);
\r
382 assignfile(t,'/etc/resolv.conf');
\r
383 {$i-}reset(t);{$i+}
\r
384 if ioresult <> 0 then exit;
\r
386 while not eof(t) do begin
\r
388 if not (copy(s,1,10) = 'nameserver') then continue;
\r
389 s := copy(s,11,500);
\r
390 while s <> '' do begin
\r
391 if (s[1] = #32) or (s[1] = #9) then s := copy(s,2,500) else break;
\r
394 if a <> 0 then s := copy(s,1,a-1);
\r
396 if a <> 0 then s := copy(s,1,a-1);
\r
398 ip := ipstrtobinf(s);
\r
399 if (ip.family <> 0) then biniplist_add(result,ip);
\r
406 function gethostname:ansistring;
\r
408 fixed_info : pfixed_info;
\r
411 fixed_info := callgetnetworkparams;
\r
412 if fixed_info = nil then exit;
\r
414 result := fixed_info.hostname;
\r
415 if fixed_info.domainname <> '' then result := result + '.'+fixed_info.domainname;
\r
417 freemem(fixed_info);
\r