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
9 {$include lcoreconfig.inc}
\r
20 {$ifdef cpu386}{$define i386}{$endif}
\r
21 {$ifdef i386}{$define ENDIAN_LITTLE}{$endif}
\r
23 {$include uint32.inc}
\r
26 hexchars:array[0..15] of ansichar='0123456789abcdef';
\r
31 //redeclare these constants so units that use us can use them
\r
32 //without using sockets directly
\r
42 {$define want_Tin6_addr}
\r
45 {$define want_Tin6_addr}
\r
47 {$ifdef want_Tin6_addr}
\r
48 Tin6_addr = packed record
\r
50 0: (u6_addr8 : array[0..15] of byte);
\r
51 1: (u6_addr16 : array[0..7] of Word);
\r
52 2: (u6_addr32 : array[0..3] of uint32);
\r
53 3: (s6_addr8 : array[0..15] of shortint);
\r
54 4: (s6_addr : array[0..15] of shortint);
\r
55 5: (s6_addr16 : array[0..7] of smallint);
\r
56 6: (s6_addr32 : array[0..3] of LongInt);
\r
73 TInetSockAddr = packed Record
\r
77 pad :array [1..8] of byte;
\r
81 TInetSockAddr6 = packed record
\r
84 sin6_flowinfo: uint32;
\r
85 sin6_addr: tin6_addr;
\r
86 sin6_scope_id: uint32;
\r
99 TInetSockAddr6 = packed record
\r
102 sin6_flowinfo: uint32;
\r
103 sin6_addr: tin6_addr;
\r
104 sin6_scope_id: uint32;
\r
108 TinetSockAddrv = packed record
\r
110 0: (InAddr:TInetSockAddr);
\r
112 1: (InAddr6:TInetSockAddr6);
\r
115 Pinetsockaddrv = ^Tinetsockaddrv;
\r
118 tsockaddrin=TInetSockAddr;
\r
123 bin IP list code, by beware
\r
124 while this is really just a string, on the interface side it must be treated
\r
125 as an opaque var which is passed as "var" when it needs to be modified}
\r
127 tbiniplist=tbufferstring;
\r
129 function biniplist_new:tbiniplist;
\r
130 procedure biniplist_add(var l:tbiniplist;ip:tbinip);
\r
131 function biniplist_getcount(const l:tbiniplist):integer;
\r
132 function biniplist_get(const l:tbiniplist;index:integer):tbinip;
\r
133 procedure biniplist_set(var l:tbiniplist;index:integer;ip:tbinip);
\r
134 procedure biniplist_setcount(var l:tbiniplist;newlen:integer);
\r
135 procedure biniplist_free(var l:tbiniplist);
\r
136 procedure biniplist_addlist(var l:tbiniplist;const l2:tbiniplist);
\r
137 function biniplist_tostr(const l:tbiniplist):thostname;
\r
138 function isbiniplist(const l:tbiniplist):boolean;
\r
140 function htons(w:word):word;
\r
141 function htonl(i:uint32):uint32;
\r
143 function ipstrtobin(const s:thostname;var binip:tbinip):boolean;
\r
144 function ipstrtobinf(const s:thostname):tbinip;
\r
145 function ipbintostr(const binip:tbinip):thostname;
\r
147 function ip6bintostr(const bin:tin6_addr):thostname;
\r
148 function ip6strtobin(const s:thostname;var bin:tin6_addr):boolean;
\r
151 function comparebinip(const ip1,ip2:tbinip):boolean;
\r
152 procedure maskbits(var binip:tbinip;bits:integer);
\r
153 function comparebinipmask(ip1,ip2:tbinip;bits:integer):boolean;
\r
155 procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);
\r
158 function longip(s:thostname):longint;
\r
160 function needconverttov4(const ip:tbinip):boolean;
\r
161 procedure converttov4(var ip:tbinip);
\r
162 procedure converttov6(var ip:tbinip);
\r
164 function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;
\r
165 function makeinaddrv(addr:tbinip;port:ansistring;var inaddr:tinetsockaddrv):integer;
\r
166 function inaddrsize(inaddr:tinetsockaddrv):integer;
\r
172 function htons(w:word):word;
\r
174 {$ifdef ENDIAN_LITTLE}
\r
175 result := ((w and $ff00) shr 8) or ((w and $ff) shl 8);
\r
181 function htonl(i:uint32):uint32;
\r
183 {$ifdef ENDIAN_LITTLE}
\r
184 result := (i shr 24) or (i shr 8 and $ff00) or (i shl 8 and $ff0000) or (i shl 24 and $ff000000);
\r
191 function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;
\r
193 result.family := inaddrv.inaddr.family;
\r
194 if result.family = AF_INET then result.ip := inaddrv.inaddr.addr;
\r
196 if result.family = AF_INET6 then result.ip6 := inaddrv.inaddr6.sin6_addr;
\r
200 function makeinaddrv(addr:tbinip;port:ansistring;var inaddr:tinetsockaddrv):integer;
\r
203 { biniptemp := forwardlookup(addr,10);}
\r
204 fillchar(inaddr,sizeof(inaddr),0);
\r
205 //writeln('converted address '+addr+' to binip '+ipbintostr(biniptemp));
\r
206 if addr.family = AF_INET then begin
\r
207 inAddr.InAddr.family:=AF_INET;
\r
208 inAddr.InAddr.port:=htons(strtointdef(port,0));
\r
209 inAddr.InAddr.addr:=addr.ip;
\r
210 result := sizeof(tinetsockaddr);
\r
213 if addr.family = AF_INET6 then begin
\r
214 inAddr.InAddr6.sin6_family:=AF_INET6;
\r
215 inAddr.InAddr6.sin6_port:=htons(strtointdef(port,0));
\r
216 inAddr.InAddr6.sin6_addr:=addr.ip6;
\r
217 result := sizeof(tinetsockaddr6);
\r
222 function inaddrsize(inaddr:tinetsockaddrv):integer;
\r
225 if inaddr.inaddr.family = AF_INET6 then result := sizeof(tinetsockaddr6) else
\r
227 result := sizeof(tinetsockaddr);
\r
231 {converts dotted v4 IP to longint. returns host endian order}
\r
232 function longip(s:thostname):longint;
\r
236 function convertbyte(const s:ansistring):integer;
\r
238 result := strtointdef(s,-1);
\r
239 if result < 0 then begin
\r
243 if result > 255 then begin
\r
248 if (result <> 0) and (s[1] = '0') then begin
\r
253 if not (s[1] in ['0'..'9']) then begin
\r
262 if a = 0 then exit;
\r
263 b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;
\r
265 s := copy(s,a+1,256);
\r
267 if a = 0 then exit;
\r
268 b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;
\r
269 l := l or b shl 16;
\r
270 s := copy(s,a+1,256);
\r
272 if a = 0 then exit;
\r
273 b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;
\r
275 s := copy(s,a+1,256);
\r
276 b := convertbyte(copy(s,1,256));if (b < 0) then exit;
\r
282 function ipstrtobinf;
\r
284 ipstrtobin(s,result);
\r
287 function ipstrtobin(const s:thostname;var binip:tbinip):boolean;
\r
292 if pos(':',s) <> 0 then begin
\r
293 {try ipv6. use builtin routine}
\r
294 result := ip6strtobin(s,binip.ip6);
\r
295 if result then binip.family := AF_INET6;
\r
301 binip.ip := htonl(longip(s));
\r
302 if (binip.ip <> 0) or (s = '0.0.0.0') then begin
\r
304 binip.family := AF_INET;
\r
309 function ipbintostr(const binip:tbinip):thostname;
\r
315 if binip.family = AF_INET6 then begin
\r
316 result := ip6bintostr(binip.ip6);
\r
319 if binip.family = AF_INET then begin
\r
320 a := htonl(binip.ip);
\r
321 result := inttostr(a shr 24)+'.'+inttostr((a shr 16) and $ff)+'.'+inttostr((a shr 8) and $ff)+'.'+inttostr(a and $ff);
\r
326 {------------------------------------------------------------------------------}
\r
331 IPv6 address binary to/from string conversion routines
\r
334 - implementation does not depend on other ipv6 code such as the tin6_addr type,
\r
335 the parameter can also be untyped.
\r
336 - it is host endian neutral - binary format is aways network order
\r
337 - it supports compression of zeroes
\r
338 - it supports ::ffff:192.168.12.34 style addresses
\r
339 - they are made to do the Right Thing, more efficient implementations are possible
\r
342 {fpc has hostaddrtostr6 and strtohostaddr6 but the later isnt implemented yet}
\r
345 function ip6bintostr(const bin:tin6_addr):thostname;
\r
346 {base16 with lowercase output}
\r
347 function makehex(w:word):ansistring;
\r
350 if w >= 4096 then result := result + hexchars[w shr 12];
\r
351 if w >= 256 then result := result + hexchars[w shr 8 and $f];
\r
352 if w >= 16 then result := result + hexchars[w shr 4 and $f];
\r
353 result := result + hexchars[w and $f];
\r
357 a,b,c,addrlen:integer;
\r
358 runbegin,runlength:integer;
\r
359 bytes:array[0..15] of byte absolute bin;
\r
360 words:array[0..7] of word;
\r
361 dwords:array[0..3] of integer absolute words;
\r
363 for a := 0 to 7 do begin
\r
364 words[a] := bytes[a shl 1] shl 8 or bytes[a shl 1 or 1];
\r
366 if (dwords[0] = 0) and (dwords[1] = 0) and (words[4] = 0) and (words[5] = $ffff) then begin
\r
367 {::ffff:/96 exception: v4 IP}
\r
372 {find longest run of zeroes}
\r
375 for a := 0 to addrlen-1 do begin
\r
376 if words[a] = 0 then begin
\r
378 for b := a to addrlen-1 do if words[b] = 0 then begin
\r
381 if (c > runlength) then begin
\r
388 {run length atleast 2 0 words}
\r
389 if (runlength = 1) then begin
\r
395 for a := 0 to runbegin-1 do begin
\r
396 if (a <> 0) then result := result + ':';
\r
397 result := result + makehex(words[a]);
\r
399 if runlength > 0 then result := result + '::';
\r
400 c := runbegin+runlength;
\r
401 for a := c to addrlen-1 do begin
\r
402 if (a > c) then result := result + ':';
\r
403 result := result + makehex(words[a]);
\r
405 if addrlen = 6 then begin
\r
406 result := result + ':'+inttostr(bytes[12])+'.'+inttostr(bytes[13])+'.'+inttostr(bytes[14])+'.'+inttostr(bytes[15]);
\r
410 function ip6strtobin(const s:thostname;var bin:tin6_addr):boolean;
\r
413 fields:array[0..7] of ansistring;
\r
414 fieldcount:integer;
\r
415 emptyfield:integer;
\r
417 words:array[0..7] of word;
\r
418 bytes:array[0..15] of byte absolute bin;
\r
421 for a := 0 to 7 do fields[a] := '';
\r
423 for a := 1 to length(s) do begin
\r
424 if s[a] = ':' then inc(fieldcount) else fields[fieldcount] := fields[fieldcount] + s[a];
\r
425 if fieldcount > 7 then exit;
\r
427 if fieldcount < 2 then exit;
\r
429 {find the empty field (compressed zeroes), not counting the first and last there may be at most 1}
\r
431 for a := 1 to fieldcount-1 do begin
\r
432 if fields[a] = '' then begin
\r
433 if emptyfield = -1 then emptyfield := a else exit;
\r
437 {check if last field is a valid v4 IP}
\r
438 a := longip(fields[fieldcount]);
\r
439 if (a <> 0) or (fields[fieldcount] = '0.0.0.0') then wordcount := 6 else wordcount := 8;
\r
440 {0:1:2:3:4:5:6.6.6.6
\r
442 fillchar(words,sizeof(words),0);
\r
443 if wordcount = 6 then begin
\r
444 if fieldcount > 6 then exit;
\r
445 words[6] := a shr 16;
\r
446 words[7] := a and $ffff;
\r
448 if emptyfield = -1 then begin
\r
449 {no run length: must be an exact number of fields}
\r
450 if wordcount = 6 then begin
\r
451 if fieldcount <> 6 then exit;
\r
453 end else if wordcount = 8 then begin
\r
454 if fieldcount <> 7 then exit;
\r
458 for a := 0 to emptyfield do begin
\r
459 if fields[a] = '' then b := 0 else b := strtointdef('$'+fields[a],-1);
\r
460 if (b < 0) or (b > $ffff) then exit;
\r
463 if wordcount = 6 then dec(fieldcount);
\r
464 for a := wordcount-1 downto wordcount-(fieldcount-emptyfield) do begin
\r
465 b := a+fieldcount-wordcount+1;
\r
466 if fields[b] = '' then b := 0 else b := strtointdef('$'+fields[b],-1);
\r
467 if (b < 0) or (b > $ffff) then exit;
\r
470 for a := 0 to 7 do begin
\r
471 bytes[a shl 1] := words[a] shr 8;
\r
472 bytes[a shl 1 or 1] := words[a] and $ff;
\r
478 function comparebinip(const ip1,ip2:tbinip):boolean;
\r
480 if (ip1.ip <> ip2.ip) then begin
\r
486 if ip1.family = AF_INET6 then begin
\r
487 if (ip1.ip6.s6_addr32[1] <> ip2.ip6.s6_addr32[1])
\r
488 or (ip1.ip6.s6_addr32[2] <> ip2.ip6.s6_addr32[2])
\r
489 or (ip1.ip6.s6_addr32[3] <> ip2.ip6.s6_addr32[3]) then begin
\r
496 result := (ip1.family = ip2.family);
\r
499 procedure maskbits(var binip:tbinip;bits:integer);
\r
501 ipmax={$ifdef ipv6}15{$else}3{$endif};
\r
502 type tarr=array[0..ipmax] of byte;
\r
508 if bits = 0 then b := 0 else b := ((bits-1) div 8)+1;
\r
509 for a := b to ipmax do begin
\r
512 if (bits and 7 <> 0) then begin
\r
513 arr[bits shr 3] := arr[bits div 8] and not ($ff shr (bits and 7))
\r
517 function comparebinipmask;
\r
519 maskbits(ip1,bits);
\r
520 maskbits(ip2,bits);
\r
521 result := comparebinip(ip1,ip2);
\r
524 function needconverttov4(const ip:tbinip):boolean;
\r
527 if ip.family = AF_INET6 then begin
\r
528 if (ip.ip6.u6_addr32[0] = 0) and (ip.ip6.u6_addr32[1] = 0) and
\r
529 (ip.ip6.u6_addr16[4] = 0) and (ip.ip6.u6_addr16[5] = $ffff) then begin
\r
539 {converts a binary IP to v4 if it is a v6 IP in the v4 range}
\r
540 procedure converttov4(var ip:tbinip);
\r
543 if needconverttov4(ip) then begin
\r
544 ip.family := AF_INET;
\r
545 ip.ip := ip.ip6.s6_addr32[3];
\r
551 {converts a binary IP to v6 if it is a v4 IP}
\r
552 procedure converttov6(var ip:tbinip);
\r
555 if ip.family = AF_INET then begin
\r
556 ip.family := AF_INET6;
\r
557 ip.ip6.s6_addr32[3] := ip.ip;
\r
558 ip.ip6.u6_addr32[0] := 0;
\r
559 ip.ip6.u6_addr32[1] := 0;
\r
560 ip.ip6.u6_addr16[4] := 0;
\r
561 ip.ip6.u6_addr16[5] := $ffff;
\r
567 {-----------biniplist stuff--------------------------------------------------}
\r
570 biniplist_prefix: ansistring = 'bipl'#0;
\r
571 //fpc 1.0.x doesn't seem to like use of length function in a constant
\r
573 //biniplist_prefixlen=length(biniplist_prefix);
\r
575 biniplist_prefixlen=5;
\r
577 function biniplist_new:tbiniplist;
\r
579 result := biniplist_prefix;
\r
582 procedure biniplist_add(var l:tbiniplist;ip:tbinip);
\r
586 a := biniplist_getcount(l);
\r
587 biniplist_setcount(l,a+1);
\r
588 biniplist_set(l,a,ip);
\r
591 function biniplist_getcount(const l:tbiniplist):integer;
\r
593 result := (length(l)-biniplist_prefixlen) div sizeof(tbinip);
\r
596 function biniplist_get(const l:tbiniplist;index:integer):tbinip;
\r
598 if (index >= biniplist_getcount(l)) then begin
\r
599 fillchar(result,sizeof(result),0);
\r
602 move(l[index*sizeof(tbinip)+1+biniplist_prefixlen],result,sizeof(result));
\r
605 procedure biniplist_set(var l:tbiniplist;index:integer;ip:tbinip);
\r
608 move(ip,l[index*sizeof(tbinip)+1+biniplist_prefixlen],sizeof(ip));
\r
611 procedure biniplist_setcount(var l:tbiniplist;newlen:integer);
\r
613 setlength(l,(sizeof(tbinip)*newlen)+biniplist_prefixlen);
\r
616 procedure biniplist_free(var l:tbiniplist);
\r
621 procedure biniplist_addlist;
\r
623 l := l + copy(l2,biniplist_prefixlen+1,maxlongint);
\r
626 function biniplist_tostr(const l:tbiniplist):thostname;
\r
631 for a := 0 to biniplist_getcount(l)-1 do begin
\r
632 if result <> '(' then result := result + ', ';
\r
633 result := result + ipbintostr(biniplist_get(l,a));
\r
635 result := result + ')';
\r
638 function isbiniplist(const l:tbiniplist):boolean;
\r
642 for i := 1 to biniplist_prefixlen do begin
\r
643 if biniplist_prefix[i] <> l[i] then begin
\r
651 procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);
\r
656 for a := biniplist_getcount(l2)-1 downto 0 do begin
\r
657 biniptemp := biniplist_get(l2,a);
\r
658 if (biniptemp.family = family) then biniplist_add(l,biniptemp);
\r