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