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 IP addresses assigned to local interfaces.
8 both IPv4 and IPv6, or one address family in isolation.
9 works on both windows and linux.
13 - localhost IPs (127.0.0.1, ::1) may be returned, the app must not expect them to be in or not in.
14 (typically, they're returned on linux and not on windows)
16 - normal behavior is to return all v6 IPs, including link local (fe80::).
17 an app that doesn't want link local IPs has to filter them out.
18 windows XP returns only one, global scope, v6 IP, due to shortcomings.
28 {$include lcoreconfig.inc}
30 function getlocalips:tbiniplist;
31 function getv4localips:tbiniplist;
33 function getv6localips:tbiniplist;
41 baseunix,sockets,sysutils;
43 function getv6localips:tbiniplist;
50 result := biniplist_new;
52 assignfile(t,'/proc/net/if_inet6');
54 if ioresult <> 0 then exit; {none found, return empty list}
55 while not eof(t) do begin
58 for a := 0 to 7 do begin
59 if (s2 <> '') then s2 := s2 + ':';
60 s2 := s2 + copy(s,(a shl 2)+1,4);
63 if ip.family <> 0 then biniplist_add(result,ip);
68 function getv4localips:tbiniplist;
79 ifr_ifrn:array [0..IF_NAMESIZE-1] of char;
83 tifrecarr=array[0..999] of tifrec;
92 result := biniplist_new;
94 {must create a socket for this}
95 s := fpsocket(AF_INET,SOCK_DGRAM,0);
96 if (s < 0) then raise exception.create('getv4localips unable to create socket');
98 fillchar(ifc,sizeof(ifc),0);
100 {get size of IP record list}
101 if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then raise exception.create('getv4localips ioctl failed 1');
103 {allocate it, with extra room in case there's more interfaces added (as recommended)}
104 getmem(ifr,ifc.ifc_len shl 1);
108 if (fpioctl(s,SIOCGIFCONF,@ifc) < 0) then raise exception.create('getv4localips ioctl failed 2');
110 fillchar(ad,sizeof(ad),0);
112 for a := (ifc.ifc_len div sizeof (tifrec))-1 downto 0 do begin
113 ad := @ifr[a].ifru_addr;
114 ip := inaddrvtobinip(ad^);
115 biniplist_add(result,ip);
122 function getlocalips:tbiniplist;
124 result := getv4localips;
126 biniplist_addlist(result,getv6localips);
133 sysutils,winsock,dnssync;
135 {the following code's purpose is to determine what IP windows would come from, to reach an IP
136 it can be abused to find if there's any global v6 IPs on a local interface}
138 SIO_ROUTING_INTERFACE_QUERY = $c8000014;
139 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';
141 function getlocalipforip(const ip:tbinip):tbinip;
145 inaddrv,inaddrv2:tinetsockaddrv;
146 srcx:winsock.tsockaddr absolute inaddrv2;
148 makeinaddrv(ip,'0',inaddrv);
149 handle := Socket(inaddrv.inaddr.family,SOCK_DGRAM,IPPROTO_UDP);
150 if (handle < 0) then begin
151 {this happens on XP without an IPv6 stack
152 i can either fail with an exception, or with a "null result". an exception is annoying in the IDE}
153 {fillchar(result,sizeof(result),0);
155 raise exception.create('getlocalipforip: can''t create socket');
157 if WSAIoctl(handle, SIO_ROUTING_INTERFACE_QUERY, inaddrv, sizeof(inaddrv), inaddrv2, sizeof(inaddrv2), a, nil, nil) <> 0
158 then raise exception.create('getlocalipforip failed with error: '+inttostr(wsagetlasterror));
159 result := inaddrvtobinip(inaddrv2);
164 function getv4localips:tbiniplist;
170 result := biniplist_new;
172 templist := getlocalips;
173 for a := biniplist_getcount(templist)-1 downto 0 do begin
174 biniptemp := biniplist_get(templist,a);
175 if biniptemp.family = AF_INET then biniplist_add(result,biniptemp);
180 function getv6localips:tbiniplist;
186 result := biniplist_new;
188 templist := getlocalips;
189 for a := biniplist_getcount(templist)-1 downto 0 do begin
190 biniptemp := biniplist_get(templist,a);
191 if biniptemp.family = AF_INET6 then biniplist_add(result,biniptemp);
196 function getlocalips:tbiniplist;
201 result := forwardlookuplist('',0);
205 {windows XP doesn't add v6 IPs
206 if we find no v6 IPs in the list, add one using a hack}
207 for a := biniplist_getcount(result)-1 downto 0 do begin
208 ip := biniplist_get(result,a);
209 if ip.family = AF_INET6 then exit;
213 ip := getlocalipforip(ipstrtobinf('2001:200::'));
214 if (ip.family = AF_INET6) then biniplist_add(result,ip);