e7d891c1eade8ac98448bcbc1d3ce5810f92cdad
[lcore.git] / binipstuff.pas
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
5 unit binipstuff;\r
6 \r
7 {$ifdef fpc}\r
8 {$mode delphi}\r
9 {$endif}\r
10 \r
11 interface\r
12 \r
13 {$include lcoreconfig.inc}\r
14 \r
15 uses\r
16 {$ifndef mswindows}\r
17   sockets,\r
18 {$endif}\r
19   pgtypes;\r
20 \r
21 \r
22 {$include pgtypes.inc}\r
23 \r
24 {$include uint32.inc}\r
25 \r
26 const\r
27   hexchars:array[0..15] of ansichar='0123456789abcdef';\r
28   {$ifdef mswindows}\r
29     AF_INET=2;\r
30     AF_INET6=23;\r
31   {$else}\r
32     //redeclare these constants so units that use us can use them\r
33     //without using sockets directly\r
34     AF_INET=AF_INET;\r
35     AF_INET6=AF_INET6;\r
36     //AF_INET6=10;\r
37   {$endif}\r
38 \r
39 type\r
40   {$ifdef ipv6}\r
41     \r
42     {$ifdef mswindows}\r
43       {$define want_Tin6_addr}\r
44     {$endif}\r
45     {$ifdef ver1_0}\r
46       {$define want_Tin6_addr}\r
47     {$endif}\r
48     {$ifdef want_Tin6_addr}\r
49       Tin6_addr = packed record\r
50         case byte of\r
51           0: (u6_addr8  : array[0..15] of byte);\r
52           1: (u6_addr16 : array[0..7] of Word);\r
53           2: (u6_addr32 : array[0..3] of uint32);\r
54           3: (s6_addr8  : array[0..15] of shortint);\r
55           4: (s6_addr   : array[0..15] of shortint);\r
56           5: (s6_addr16 : array[0..7] of smallint);\r
57           6: (s6_addr32 : array[0..3] of LongInt);\r
58       end;\r
59     {$endif}\r
60   {$endif}\r
61 \r
62   tbinip=record\r
63     family:integer;\r
64     {$ifdef ipv6}\r
65       case integer of\r
66         0: (ip:longint);\r
67         1: (ip6:tin6_addr);\r
68     {$else}\r
69       ip:longint;\r
70     {$endif}\r
71   end;\r
72 \r
73   {zipplet 20170204: FPC 3.0.0 changed the definition of TInetSockAddr:\r
74     - http://www.freepascal.org/docs-html/rtl/sockets/tinetsockaddr.html\r
75     - http://www.freepascal.org/docs-html/rtl/sockets/sockaddr_in.html\r
76    Due to this, TInetSockAddr -> TLInetSockAddr4 / TLInetSockAddr6\r
77    Using our own types no matter what OS or compiler version will prevent future problems.\r
78    Adding "4" to non IPv6 record names improves code clarity }\r
79 \r
80   {$ifndef mswindows}\r
81     //zipplet 20170204: Do we still need to support ver1_0? Perhaps a cleanup is in order.\r
82     //For now keep supporting it for compatibility.\r
83     {$ifdef ver1_0}\r
84       cuint16 = word;\r
85       cuint32 = dword;\r
86       sa_family_t = word;\r
87     {$endif}\r
88   {$endif}\r
89 \r
90   TLInetSockAddr4 = packed Record\r
91     family:Word;\r
92     port  :Word;\r
93     addr  :uint32;\r
94     pad   :array [0..7] of byte;   //zipplet 20170204 - originally this was 1..8 for some reason\r
95   end;\r
96   \r
97   {$ifdef ipv6}\r
98     TLInetSockAddr6 = packed record\r
99       sin6_family: word;\r
100       sin6_port: word;\r
101       sin6_flowinfo: uint32;\r
102       sin6_addr: tin6_addr;\r
103       sin6_scope_id: uint32;\r
104     end;\r
105   {$endif}\r
106 \r
107   //zipplet 20170204: I did not rename the unioned record. We might want to rename this to TLinetSockAddrv\r
108   TinetSockAddrv = packed record\r
109     case integer of\r
110       0: (InAddr:TLInetSockAddr4);\r
111       {$ifdef ipv6}\r
112       1: (InAddr6:TLInetSockAddr6);\r
113       {$endif}\r
114   end;\r
115   Pinetsockaddrv = ^Tinetsockaddrv;\r
116 \r
117   type\r
118     tsockaddrin=TLInetSockAddr4;\r
119 \r
120 {\r
121 bin IP list code, by beware\r
122 while this is really just a string, on the interface side it must be treated\r
123 as an opaque var which is passed as "var" when it needs to be modified}\r
124 \r
125   tbiniplist=tbufferstring;\r
126 \r
127 function biniplist_new:tbiniplist;\r
128 procedure biniplist_add(var l:tbiniplist;ip:tbinip);\r
129 function biniplist_getcount(const l:tbiniplist):integer;\r
130 function biniplist_get(const l:tbiniplist;index:integer):tbinip;\r
131 procedure biniplist_set(var l:tbiniplist;index:integer;ip:tbinip);\r
132 procedure biniplist_setcount(var l:tbiniplist;newlen:integer);\r
133 procedure biniplist_free(var l:tbiniplist);\r
134 procedure biniplist_addlist(var l:tbiniplist;const l2:tbiniplist);\r
135 function biniplist_tostr(const l:tbiniplist):thostname;\r
136 function isbiniplist(const l:tbiniplist):boolean;\r
137 \r
138 function htons(w:word):word;\r
139 function htonl(i:uint32):uint32;\r
140 \r
141 function ipstrtobin(const s:thostname;var binip:tbinip):boolean;\r
142 function ipstrtobinf(const s:thostname):tbinip;\r
143 function ipbintostr(const binip:tbinip):thostname;\r
144 {$ifdef ipv6}\r
145 function ip6bintostr(const bin:tin6_addr):thostname;\r
146 function ip6strtobin(const s:thostname;var bin:tin6_addr):boolean;\r
147 {$endif}\r
148 \r
149 function comparebinip(const ip1,ip2:tbinip):boolean;\r
150 procedure maskbits(var binip:tbinip;bits:integer);\r
151 function comparebinipmask(ip1,ip2:tbinip;bits:integer):boolean;\r
152 \r
153 procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);\r
154 \r
155 {deprecated}\r
156 function longip(s:thostname):longint;\r
157 \r
158 function needconverttov4(const ip:tbinip):boolean;\r
159 procedure converttov4(var ip:tbinip);\r
160 procedure converttov6(var ip:tbinip);\r
161 \r
162 function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;\r
163 function makeinaddrv(addr:tbinip;port:ansistring;var inaddr:tinetsockaddrv):integer;\r
164 function inaddrsize(inaddr:tinetsockaddrv):integer;\r
165 \r
166 function getbinipbitlength(const ip:tbinip):integer;\r
167 function getipstrbitlength(const ip:thostname):integer;\r
168 function getfamilybitlength(family:integer):integer;\r
169 \r
170 implementation\r
171 \r
172 uses sysutils;\r
173 \r
174 function htons(w:word):word;\r
175 begin\r
176   {$ifdef ENDIAN_LITTLE}\r
177   result := ((w and $ff00) shr 8) or ((w and $ff) shl 8);\r
178   {$else}\r
179   result := w;\r
180   {$endif}\r
181 end;\r
182 \r
183 function htonl(i:uint32):uint32;\r
184 begin\r
185   {$ifdef ENDIAN_LITTLE}\r
186   result := (i shr 24) or (i shr 8 and $ff00) or (i shl 8 and $ff0000) or (i shl 24 and $ff000000);\r
187   {$else}\r
188   result := i;\r
189   {$endif}\r
190 end;\r
191 \r
192 \r
193 function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;\r
194 begin\r
195   result.family := inaddrv.inaddr.family;\r
196   if result.family = AF_INET then result.ip := inaddrv.inaddr.addr;\r
197   {$ifdef ipv6}\r
198   if result.family = AF_INET6 then result.ip6 := inaddrv.inaddr6.sin6_addr;\r
199   {$endif}\r
200 end;\r
201 \r
202 function makeinaddrv(addr:tbinip;port:ansistring;var inaddr:tinetsockaddrv):integer;\r
203 begin\r
204   result := 0;\r
205 {  biniptemp := forwardlookup(addr,10);}\r
206   fillchar(inaddr,sizeof(inaddr),0);\r
207   //writeln('converted address '+addr+' to binip '+ipbintostr(biniptemp));\r
208   if addr.family = AF_INET then begin\r
209     inAddr.InAddr.family:=AF_INET;\r
210     inAddr.InAddr.port:=htons(strtointdef(port,0));\r
211     inAddr.InAddr.addr:=addr.ip;\r
212     result := sizeof(tlinetsockaddr4);\r
213   end else\r
214   {$ifdef ipv6}\r
215   if addr.family = AF_INET6 then begin\r
216     inAddr.InAddr6.sin6_family:=AF_INET6;\r
217     inAddr.InAddr6.sin6_port:=htons(strtointdef(port,0));\r
218     inAddr.InAddr6.sin6_addr:=addr.ip6;\r
219     result := sizeof(tlinetsockaddr6);\r
220   end;\r
221   {$endif}\r
222 end;\r
223 \r
224 function inaddrsize(inaddr:tinetsockaddrv):integer;\r
225 begin\r
226   {$ifdef ipv6}\r
227   if inaddr.inaddr.family = AF_INET6 then result := sizeof(tlinetsockaddr6) else\r
228   {$endif}\r
229   result := sizeof(tlinetsockaddr4);\r
230 end;\r
231 \r
232 {internal}\r
233 {converts dotted v4 IP to longint. returns host endian order}\r
234 function longip(s:thostname):longint;\r
235 var\r
236   l:longint;\r
237   a,b:integer;\r
238 function convertbyte(const s:ansistring):integer;\r
239 begin\r
240   result := strtointdef(s,-1);\r
241   if result < 0 then begin\r
242     result := -1;\r
243     exit;\r
244   end;\r
245   if result > 255 then begin\r
246     result := -1;\r
247     exit;\r
248   end;\r
249   {01 exception}\r
250   if (result <> 0) and (s[1] = '0') then begin\r
251     result := -1;\r
252     exit;\r
253   end;\r
254   {+1 exception}\r
255   if not (s[1] in ['0'..'9']) then begin\r
256     result := -1;\r
257     exit\r
258   end;\r
259 end;\r
260 \r
261 begin\r
262   result := 0;\r
263   a := pos('.',s);\r
264   if a = 0 then exit;\r
265   b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;\r
266   l := b shl 24;\r
267   s := copy(s,a+1,256);\r
268   a := pos('.',s);\r
269   if a = 0 then exit;\r
270   b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;\r
271   l := l or b shl 16;\r
272   s := copy(s,a+1,256);\r
273   a := pos('.',s);\r
274   if a = 0 then exit;\r
275   b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;\r
276   l := l or b shl 8;\r
277   s := copy(s,a+1,256);\r
278   b := convertbyte(copy(s,1,256));if (b < 0) then exit;\r
279   l := l or b;\r
280   result := l;\r
281 end;\r
282 \r
283 \r
284 function ipstrtobinf;\r
285 begin\r
286   ipstrtobin(s,result);\r
287 end;\r
288 \r
289 function ipstrtobin(const s:thostname;var binip:tbinip):boolean;\r
290 begin\r
291   binip.family := 0;\r
292   result := false;\r
293   {$ifdef ipv6}\r
294   if pos(':',s) <> 0 then begin\r
295     {try ipv6. use builtin routine}\r
296     result := ip6strtobin(s,binip.ip6);\r
297     if result then binip.family := AF_INET6;\r
298     exit;\r
299   end;\r
300   {$endif}\r
301 \r
302   {try v4}\r
303   binip.ip := htonl(longip(s));\r
304   if (binip.ip <> 0) or (s = '0.0.0.0') then begin\r
305     result := true;\r
306     binip.family := AF_INET;\r
307     exit;\r
308   end;\r
309 end;\r
310 \r
311 function ipbintostr(const binip:tbinip):thostname;\r
312 var\r
313   a:integer;\r
314 begin\r
315   result := '';\r
316   {$ifdef ipv6}\r
317   if binip.family = AF_INET6 then begin\r
318     result := ip6bintostr(binip.ip6);\r
319   end else\r
320   {$endif}\r
321   if binip.family = AF_INET then begin\r
322     a := htonl(binip.ip);\r
323     result := inttostr(a shr 24)+'.'+inttostr((a shr 16) and $ff)+'.'+inttostr((a shr 8) and $ff)+'.'+inttostr(a and $ff);\r
324   end;\r
325 end;\r
326 \r
327 \r
328 {------------------------------------------------------------------------------}\r
329 \r
330 {$ifdef ipv6}\r
331 \r
332 {\r
333 IPv6 address binary to/from string conversion routines\r
334 written by beware\r
335 \r
336 - implementation does not depend on other ipv6 code such as the tin6_addr type,\r
337   the parameter can also be untyped.\r
338 - it is host endian neutral - binary format is always network order\r
339 - it supports compression of zeroes\r
340 - it supports ::ffff:192.168.12.34 style addresses\r
341 - they are made to do the Right Thing, more efficient implementations are possible\r
342 }\r
343 \r
344 {fpc has hostaddrtostr6 and strtohostaddr6 but the later isnt implemented yet}\r
345 \r
346 \r
347 function ip6bintostr(const bin:tin6_addr):thostname;\r
348 {base16 with lowercase output}\r
349 function makehex(w:word):ansistring;\r
350 begin\r
351   result := '';\r
352   if w >= 4096 then result := result + hexchars[w shr 12];\r
353   if w >= 256 then result := result + hexchars[w shr 8 and $f];\r
354   if w >= 16 then result := result + hexchars[w shr 4 and $f];\r
355   result := result + hexchars[w and $f];\r
356 end;\r
357 \r
358 var\r
359   a,b,c,addrlen:integer;\r
360   runbegin,runlength:integer;\r
361   bytes:array[0..15] of byte absolute bin;\r
362   words:array[0..7] of word;\r
363   dwords:array[0..3] of integer absolute words;\r
364 begin\r
365   for a := 0 to 7 do begin\r
366     words[a] := bytes[a shl 1] shl 8 or bytes[a shl 1 or 1];\r
367   end;\r
368   if (dwords[0] = 0) and (dwords[1] = 0) and (words[4] = 0) and (words[5] = $ffff) then begin\r
369     {::ffff:/96 exception: v4 IP}\r
370     addrlen := 6;\r
371   end else begin\r
372     addrlen := 8;\r
373   end;\r
374   {find longest run of zeroes}\r
375   runbegin := 0;\r
376   runlength := 0;\r
377   for a := 0 to addrlen-1 do begin\r
378     if words[a] = 0 then begin\r
379       c := 0;\r
380       for b := a to addrlen-1 do if words[b] = 0 then begin\r
381         inc(c);\r
382       end else break;\r
383       if (c > runlength) then begin\r
384         runlength := c;\r
385         runbegin := a;\r
386       end;\r
387     end;\r
388   end;\r
389 \r
390   {run length at least 2 0 words}\r
391   if (runlength = 1) then begin\r
392     runlength := 0;\r
393     runbegin := 0;\r
394   end;\r
395 \r
396   result := '';\r
397   for a := 0 to runbegin-1 do begin\r
398     if (a <> 0) then result := result + ':';\r
399     result := result + makehex(words[a]);\r
400   end;\r
401   if runlength > 0 then result := result + '::';\r
402   c := runbegin+runlength;\r
403   for a := c to addrlen-1 do begin\r
404     if (a > c) then result := result + ':';\r
405     result := result + makehex(words[a]);\r
406   end;\r
407   if addrlen = 6 then begin\r
408     result := result + ':'+inttostr(bytes[12])+'.'+inttostr(bytes[13])+'.'+inttostr(bytes[14])+'.'+inttostr(bytes[15]);\r
409   end;\r
410 end;\r
411 \r
412 function ip6strtobin(const s:thostname;var bin:tin6_addr):boolean;\r
413 var\r
414   a,b:integer;\r
415   fields:array[0..7] of ansistring;\r
416   fieldcount:integer;\r
417   emptyfield:integer;\r
418   wordcount:integer;\r
419   words:array[0..7] of word;\r
420   bytes:array[0..15] of byte absolute bin;\r
421 begin\r
422   result := false;\r
423   for a := 0 to 7 do fields[a] := '';\r
424   fieldcount := 0;\r
425   for a := 1 to length(s) do begin\r
426     if s[a] = ':' then inc(fieldcount) else fields[fieldcount] := fields[fieldcount] + s[a];\r
427     if fieldcount > 7 then exit;\r
428   end;\r
429   if fieldcount < 2 then exit;\r
430 \r
431   {find the empty field (compressed zeroes), not counting the first and last there may be at most 1}\r
432   emptyfield := -1;\r
433   for a := 1 to fieldcount-1 do begin\r
434     if fields[a] = '' then begin\r
435       if emptyfield = -1 then emptyfield := a else exit;\r
436     end;\r
437   end;\r
438 \r
439   {check if last field is a valid v4 IP}\r
440   a := longip(fields[fieldcount]);\r
441   if (a <> 0) or (fields[fieldcount] = '0.0.0.0') then wordcount := 6 else wordcount := 8;\r
442   {0:1:2:3:4:5:6.6.6.6\r
443    0:1:2:3:4:5:6:7}\r
444   fillchar(words,sizeof(words),0);\r
445   if wordcount = 6 then begin\r
446     if fieldcount > 6 then exit;\r
447     words[6] := a shr 16;\r
448     words[7] := a and $ffff;\r
449   end;\r
450   if emptyfield = -1 then begin\r
451     {no run length: must be an exact number of fields}\r
452     if wordcount = 6 then begin\r
453       if fieldcount <> 6 then exit;\r
454       emptyfield := 5;\r
455     end else if wordcount = 8 then begin\r
456       if fieldcount <> 7 then exit;\r
457       emptyfield := 7;\r
458     end else exit;\r
459   end;\r
460   for a := 0 to emptyfield do begin\r
461     if fields[a] = '' then b := 0 else b := strtointdef('$'+fields[a],-1);\r
462     if (b < 0) or (b > $ffff) then exit;\r
463     words[a] := b;\r
464   end;\r
465   if wordcount = 6 then dec(fieldcount);\r
466   for a := wordcount-1 downto wordcount-(fieldcount-emptyfield) do begin\r
467     b := a+fieldcount-wordcount+1;\r
468     if fields[b] = '' then b := 0 else b := strtointdef('$'+fields[b],-1);\r
469     if (b < 0) or (b > $ffff) then exit;\r
470     words[a] := b;\r
471   end;\r
472   for a := 0 to 7 do begin\r
473     bytes[a shl 1] := words[a] shr 8;\r
474     bytes[a shl 1 or 1] := words[a] and $ff;\r
475   end;\r
476   result := true;\r
477 end;\r
478 {$endif}\r
479 \r
480 function comparebinip(const ip1,ip2:tbinip):boolean;\r
481 begin\r
482   if (ip1.ip <> ip2.ip) then begin\r
483     result := false;\r
484     exit;\r
485   end;\r
486 \r
487   {$ifdef ipv6}\r
488   if ip1.family = AF_INET6 then begin\r
489     if (ip1.ip6.s6_addr32[1] <> ip2.ip6.s6_addr32[1])\r
490     or (ip1.ip6.s6_addr32[2] <> ip2.ip6.s6_addr32[2])\r
491     or (ip1.ip6.s6_addr32[3] <> ip2.ip6.s6_addr32[3]) then begin\r
492       result := false;\r
493       exit;\r
494     end;\r
495   end;\r
496   {$endif}\r
497 \r
498   result := (ip1.family = ip2.family);\r
499 end;\r
500 \r
501 procedure maskbits(var binip:tbinip;bits:integer);\r
502 const\r
503   ipmax={$ifdef ipv6}15{$else}3{$endif};\r
504 type tarr=array[0..ipmax] of byte;\r
505 var\r
506   arr:^tarr;\r
507   a,b:integer;\r
508 begin\r
509   arr := @binip.ip;\r
510   if bits = 0 then b := 0 else b := ((bits-1) div 8)+1;\r
511   for a := b to ipmax do begin\r
512     arr[a] := 0;\r
513   end;\r
514   if (bits and 7 <> 0) then begin\r
515     arr[bits shr 3] := arr[bits div 8] and not ($ff shr (bits and 7))\r
516   end;\r
517 end;\r
518 \r
519 function comparebinipmask;\r
520 begin\r
521   maskbits(ip1,bits);\r
522   maskbits(ip2,bits);\r
523   result := comparebinip(ip1,ip2);\r
524 end;\r
525 \r
526 function needconverttov4(const ip:tbinip):boolean;\r
527 begin\r
528   {$ifdef ipv6}\r
529   if ip.family = AF_INET6 then begin\r
530     if (ip.ip6.u6_addr32[0] = 0) and (ip.ip6.u6_addr32[1] = 0) and\r
531     (ip.ip6.u6_addr16[4] = 0) and (ip.ip6.u6_addr16[5] = $ffff) then begin\r
532       result := true;\r
533       exit;\r
534     end;\r
535   end;\r
536   {$endif}\r
537 \r
538   result := false;\r
539 end;\r
540 \r
541 {converts a binary IP to v4 if it is a v6 IP in the v4 range}\r
542 procedure converttov4(var ip:tbinip);\r
543 begin\r
544   {$ifdef ipv6}\r
545   if needconverttov4(ip) then begin\r
546     ip.family := AF_INET;\r
547     ip.ip := ip.ip6.s6_addr32[3];\r
548   end;\r
549   {$endif}\r
550 end;\r
551 \r
552 \r
553 {converts a binary IP to v6 if it is a v4 IP}\r
554 procedure converttov6(var ip:tbinip);\r
555 begin\r
556   {$ifdef ipv6}\r
557     if ip.family = AF_INET then begin\r
558       ip.family := AF_INET6;\r
559       ip.ip6.s6_addr32[3] := ip.ip;\r
560       ip.ip6.u6_addr32[0] := 0;\r
561       ip.ip6.u6_addr32[1] := 0;\r
562       ip.ip6.u6_addr16[4] := 0;\r
563       ip.ip6.u6_addr16[5] := $ffff;\r
564     end;\r
565   {$endif}\r
566 end;\r
567 \r
568 \r
569 {-----------biniplist stuff--------------------------------------------------}\r
570 \r
571 const\r
572   biniplist_prefix: ansistring = 'bipl'#0;\r
573   //fpc 1.0.x doesn't seem to like use of length function in a constant \r
574   //definition\r
575   //biniplist_prefixlen=length(biniplist_prefix);\r
576 \r
577   biniplist_prefixlen=5;\r
578   \r
579 function biniplist_new:tbiniplist;\r
580 begin\r
581   result := biniplist_prefix;\r
582 end;\r
583 \r
584 procedure biniplist_add(var l:tbiniplist;ip:tbinip);\r
585 var\r
586   a:integer;\r
587 begin\r
588   a := biniplist_getcount(l);\r
589   biniplist_setcount(l,a+1);\r
590   biniplist_set(l,a,ip);\r
591 end;\r
592 \r
593 function biniplist_getcount(const l:tbiniplist):integer;\r
594 begin\r
595   result := (length(l)-biniplist_prefixlen) div sizeof(tbinip);\r
596 end;\r
597 \r
598 function biniplist_get(const l:tbiniplist;index:integer):tbinip;\r
599 begin\r
600   if (index >= biniplist_getcount(l)) then begin\r
601     fillchar(result,sizeof(result),0);\r
602     exit;\r
603   end;\r
604   move(l[index*sizeof(tbinip)+1+biniplist_prefixlen],result,sizeof(result));\r
605 end;\r
606 \r
607 procedure biniplist_set(var l:tbiniplist;index:integer;ip:tbinip);\r
608 begin\r
609   uniquestring(l);\r
610   move(ip,l[index*sizeof(tbinip)+1+biniplist_prefixlen],sizeof(ip));\r
611 end;\r
612 \r
613 procedure biniplist_setcount(var l:tbiniplist;newlen:integer);\r
614 begin\r
615   setlength(l,(sizeof(tbinip)*newlen)+biniplist_prefixlen);\r
616 end;\r
617 \r
618 procedure biniplist_free(var l:tbiniplist);\r
619 begin\r
620   l := '';\r
621 end;\r
622 \r
623 procedure biniplist_addlist;\r
624 begin\r
625   l := l + copy(l2,biniplist_prefixlen+1,maxlongint);\r
626 end;\r
627 \r
628 function biniplist_tostr(const l:tbiniplist):thostname;\r
629 var\r
630   a:integer;\r
631 begin\r
632   result := '(';\r
633   for a := 0 to biniplist_getcount(l)-1 do begin\r
634     if result <> '(' then result := result + ', ';\r
635     result := result + ipbintostr(biniplist_get(l,a));\r
636   end;\r
637   result := result + ')';\r
638 end;\r
639 \r
640 function isbiniplist(const l:tbiniplist):boolean;\r
641 var\r
642   i : integer;\r
643 begin\r
644   for i := 1 to biniplist_prefixlen do begin\r
645     if biniplist_prefix[i] <> l[i] then begin\r
646       result := false;\r
647       exit;\r
648     end;\r
649   end;\r
650   result := true;\r
651 end;\r
652 \r
653 procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);\r
654 var\r
655   a:integer;\r
656   biniptemp:tbinip;\r
657 begin\r
658   for a := biniplist_getcount(l2)-1 downto 0 do begin\r
659     biniptemp := biniplist_get(l2,a);\r
660     if (biniptemp.family = family) then biniplist_add(l,biniptemp);\r
661   end;\r
662 end;\r
663 \r
664 function getfamilybitlength(family:integer):integer;\r
665 begin\r
666   {$ifdef ipv6}\r
667   if family = AF_INET6 then result := 128 else\r
668   {$endif}\r
669   if family = AF_INET then result := 32\r
670   else result := 0;\r
671 end;\r
672 \r
673 function getbinipbitlength(const ip:tbinip):integer;\r
674 begin\r
675   result := getfamilybitlength(ip.family);\r
676 end;\r
677 \r
678 function getipstrbitlength(const ip:thostname):integer;\r
679 var\r
680   biniptemp:tbinip;\r
681 begin\r
682   ipstrtobin(ip,biniptemp);\r
683   result := getbinipbitlength(biniptemp);\r
684 end;\r
685 \r
686 end.\r