You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

binipstuff.pas 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632
  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. ----------------------------------------------------------------------------- }
  5. unit binipstuff;
  6. interface
  7. {$include lcoreconfig.inc}
  8. {$ifndef win32}
  9. {$ifdef ipv6}
  10. uses sockets;
  11. {$endif}
  12. {$endif}
  13. {$ifdef fpc}
  14. {$mode delphi}
  15. {$endif}
  16. {$ifdef cpu386}{$define i386}{$endif}
  17. {$ifdef i386}{$define ENDIAN_LITTLE}{$endif}
  18. {$include uint32.inc}
  19. const
  20. hexchars:array[0..15] of char='0123456789abcdef';
  21. AF_INET=2;
  22. {$ifdef win32}
  23. AF_INET6=23;
  24. {$else}
  25. AF_INET6=10;
  26. {$endif}
  27. type
  28. {$ifdef ipv6}
  29. {$ifdef win32}
  30. {$define want_Tin6_addr}
  31. {$endif}
  32. {$ifdef ver1_0}
  33. {$define want_Tin6_addr}
  34. {$endif}
  35. {$ifdef want_Tin6_addr}
  36. Tin6_addr = packed record
  37. case byte of
  38. 0: (u6_addr8 : array[0..15] of byte);
  39. 1: (u6_addr16 : array[0..7] of Word);
  40. 2: (u6_addr32 : array[0..3] of uint32);
  41. 3: (s6_addr8 : array[0..15] of shortint);
  42. 4: (s6_addr : array[0..15] of shortint);
  43. 5: (s6_addr16 : array[0..7] of smallint);
  44. 6: (s6_addr32 : array[0..3] of LongInt);
  45. end;
  46. {$endif}
  47. {$endif}
  48. tbinip=record
  49. family:integer;
  50. {$ifdef ipv6}
  51. case integer of
  52. 0: (ip:longint);
  53. 1: (ip6:tin6_addr);
  54. {$else}
  55. ip:longint;
  56. {$endif}
  57. end;
  58. {$ifdef win32}
  59. TInetSockAddr = packed Record
  60. family:Word;
  61. port :Word;
  62. addr :uint32;
  63. pad :array [1..8] of byte;
  64. end;
  65. {$ifdef ipv6}
  66. TInetSockAddr6 = packed record
  67. sin6_family: word;
  68. sin6_port: word;
  69. sin6_flowinfo: uint32;
  70. sin6_addr: tin6_addr;
  71. sin6_scope_id: uint32;
  72. end;
  73. {$endif}
  74. {$endif}
  75. {$ifdef ipv6}
  76. {$ifdef ver1_0}
  77. cuint16=word;
  78. cuint32=dword;
  79. sa_family_t=word;
  80. TInetSockAddr6 = packed record
  81. sin6_family: word;
  82. sin6_port: word;
  83. sin6_flowinfo: uint32;
  84. sin6_addr: tin6_addr;
  85. sin6_scope_id: uint32;
  86. end;
  87. {$endif}
  88. {$endif}
  89. TinetSockAddrv = packed record
  90. case integer of
  91. 0: (InAddr:TInetSockAddr);
  92. {$ifdef ipv6}
  93. 1: (InAddr6:TInetSockAddr6);
  94. {$endif}
  95. end;
  96. Pinetsockaddrv = ^Tinetsockaddrv;
  97. type
  98. tsockaddrin=TInetSockAddr;
  99. {
  100. bin IP list code, by beware
  101. while this is really just a string, on the interface side it must be treated
  102. as an opaque var which is passed as "var" when it needs to be modified}
  103. tbiniplist=string;
  104. function biniplist_new:tbiniplist;
  105. procedure biniplist_add(var l:tbiniplist;ip:tbinip);
  106. function biniplist_getcount(const l:tbiniplist):integer;
  107. function biniplist_get(const l:tbiniplist;index:integer):tbinip;
  108. procedure biniplist_set(var l:tbiniplist;index:integer;ip:tbinip);
  109. procedure biniplist_setcount(var l:tbiniplist;newlen:integer);
  110. procedure biniplist_free(var l:tbiniplist);
  111. procedure biniplist_addlist(var l:tbiniplist;const l2:tbiniplist);
  112. function biniplist_tostr(const l:tbiniplist):string;
  113. function isbiniplist(const l:tbiniplist):boolean;
  114. function htons(w:word):word;
  115. function htonl(i:uint32):uint32;
  116. function ipstrtobin(const s:string;var binip:tbinip):boolean;
  117. function ipstrtobinf(const s:string):tbinip;
  118. function ipbintostr(const binip:tbinip):string;
  119. {$ifdef ipv6}
  120. function ip6bintostr(const bin:tin6_addr):string;
  121. function ip6strtobin(const s:string;var bin:tin6_addr):boolean;
  122. {$endif}
  123. function comparebinip(const ip1,ip2:tbinip):boolean;
  124. procedure maskbits(var binip:tbinip;bits:integer);
  125. function comparebinipmask(ip1,ip2:tbinip;bits:integer):boolean;
  126. procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);
  127. {deprecated}
  128. function longip(s:string):longint;
  129. function needconverttov4(const ip:tbinip):boolean;
  130. procedure converttov4(var ip:tbinip);
  131. function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;
  132. function makeinaddrv(addr:tbinip;port:string;var inaddr:tinetsockaddrv):integer;
  133. function inaddrsize(inaddr:tinetsockaddrv):integer;
  134. implementation
  135. uses sysutils;
  136. function htons(w:word):word;
  137. begin
  138. {$ifdef ENDIAN_LITTLE}
  139. result := ((w and $ff00) shr 8) or ((w and $ff) shl 8);
  140. {$else}
  141. result := w;
  142. {$endif}
  143. end;
  144. function htonl(i:uint32):uint32;
  145. begin
  146. {$ifdef ENDIAN_LITTLE}
  147. result := (i shr 24) or (i shr 8 and $ff00) or (i shl 8 and $ff0000) or (i shl 24 and $ff000000);
  148. {$else}
  149. result := i;
  150. {$endif}
  151. end;
  152. function inaddrvtobinip(inaddrv:tinetsockaddrv):tbinip;
  153. begin
  154. result.family := inaddrv.inaddr.family;
  155. if result.family = AF_INET then result.ip := inaddrv.inaddr.addr;
  156. {$ifdef ipv6}
  157. if result.family = AF_INET6 then result.ip6 := inaddrv.inaddr6.sin6_addr;
  158. {$endif}
  159. end;
  160. function makeinaddrv(addr:tbinip;port:string;var inaddr:tinetsockaddrv):integer;
  161. begin
  162. result := 0;
  163. { biniptemp := forwardlookup(addr,10);}
  164. fillchar(inaddr,sizeof(inaddr),0);
  165. //writeln('converted address '+addr+' to binip '+ipbintostr(biniptemp));
  166. if addr.family = AF_INET then begin
  167. inAddr.InAddr.family:=AF_INET;
  168. inAddr.InAddr.port:=htons(strtointdef(port,0));
  169. inAddr.InAddr.addr:=addr.ip;
  170. result := sizeof(tinetsockaddr);
  171. end else
  172. {$ifdef ipv6}
  173. if addr.family = AF_INET6 then begin
  174. inAddr.InAddr6.sin6_family:=AF_INET6;
  175. inAddr.InAddr6.sin6_port:=htons(strtointdef(port,0));
  176. inAddr.InAddr6.sin6_addr:=addr.ip6;
  177. result := sizeof(tinetsockaddr6);
  178. end;
  179. {$endif}
  180. end;
  181. function inaddrsize(inaddr:tinetsockaddrv):integer;
  182. begin
  183. {$ifdef ipv6}
  184. if inaddr.inaddr.family = AF_INET6 then result := sizeof(tinetsockaddr6) else
  185. {$endif}
  186. result := sizeof(tinetsockaddr);
  187. end;
  188. {internal}
  189. {converts dotted v4 IP to longint. returns host endian order}
  190. function longip(s:string):longint;
  191. var
  192. l:longint;
  193. a,b:integer;
  194. function convertbyte(const s:string):integer;
  195. begin
  196. result := strtointdef(s,-1);
  197. if result < 0 then begin
  198. result := -1;
  199. exit;
  200. end;
  201. if result > 255 then begin
  202. result := -1;
  203. exit;
  204. end;
  205. {01 exception}
  206. if (result <> 0) and (s[1] = '0') then begin
  207. result := -1;
  208. exit;
  209. end;
  210. {+1 exception}
  211. if not (s[1] in ['0'..'9']) then begin
  212. result := -1;
  213. exit
  214. end;
  215. end;
  216. begin
  217. result := 0;
  218. a := pos('.',s);
  219. if a = 0 then exit;
  220. b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;
  221. l := b shl 24;
  222. s := copy(s,a+1,256);
  223. a := pos('.',s);
  224. if a = 0 then exit;
  225. b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;
  226. l := l or b shl 16;
  227. s := copy(s,a+1,256);
  228. a := pos('.',s);
  229. if a = 0 then exit;
  230. b := convertbyte(copy(s,1,a-1));if (b < 0) then exit;
  231. l := l or b shl 8;
  232. s := copy(s,a+1,256);
  233. b := convertbyte(copy(s,1,256));if (b < 0) then exit;
  234. l := l or b;
  235. result := l;
  236. end;
  237. function ipstrtobinf;
  238. begin
  239. ipstrtobin(s,result);
  240. end;
  241. function ipstrtobin(const s:string;var binip:tbinip):boolean;
  242. begin
  243. binip.family := 0;
  244. result := false;
  245. {$ifdef ipv6}
  246. if pos(':',s) <> 0 then begin
  247. {try ipv6. use builtin routine}
  248. result := ip6strtobin(s,binip.ip6);
  249. if result then binip.family := AF_INET6;
  250. exit;
  251. end;
  252. {$endif}
  253. {try v4}
  254. binip.ip := htonl(longip(s));
  255. if (binip.ip <> 0) or (s = '0.0.0.0') then begin
  256. result := true;
  257. binip.family := AF_INET;
  258. exit;
  259. end;
  260. end;
  261. function ipbintostr(const binip:tbinip):string;
  262. var
  263. a:integer;
  264. begin
  265. result := '';
  266. {$ifdef ipv6}
  267. if binip.family = AF_INET6 then begin
  268. result := ip6bintostr(binip.ip6);
  269. end else
  270. {$endif}
  271. if binip.family = AF_INET then begin
  272. a := htonl(binip.ip);
  273. result := inttostr(a shr 24)+'.'+inttostr((a shr 16) and $ff)+'.'+inttostr((a shr 8) and $ff)+'.'+inttostr(a and $ff);
  274. end;
  275. end;
  276. {------------------------------------------------------------------------------}
  277. {$ifdef ipv6}
  278. {
  279. IPv6 address binary to/from string conversion routines
  280. written by beware (steendijk at xs4all dot nl)
  281. - implementation does not depend on other ipv6 code such as the tin6_addr type,
  282. the parameter can also be untyped.
  283. - it is host endian neutral - binary format is aways network order
  284. - it supports compression of zeroes
  285. - it supports ::ffff:192.168.12.34 style addresses
  286. - they are made to do the Right Thing, more efficient implementations are possible
  287. }
  288. {fpc has hostaddrtostr6 and strtohostaddr6 but the later isnt implemented yet}
  289. function ip6bintostr(const bin:tin6_addr):string;
  290. {base16 with lowercase output}
  291. function makehex(w:word):string;
  292. begin
  293. result := '';
  294. if w >= 4096 then result := result + hexchars[w shr 12];
  295. if w >= 256 then result := result + hexchars[w shr 8 and $f];
  296. if w >= 16 then result := result + hexchars[w shr 4 and $f];
  297. result := result + hexchars[w and $f];
  298. end;
  299. var
  300. a,b,c,addrlen:integer;
  301. runbegin,runlength:integer;
  302. bytes:array[0..15] of byte absolute bin;
  303. words:array[0..7] of word;
  304. dwords:array[0..3] of integer absolute words;
  305. begin
  306. for a := 0 to 7 do begin
  307. words[a] := bytes[a shl 1] shl 8 or bytes[a shl 1 or 1];
  308. end;
  309. if (dwords[0] = 0) and (dwords[1] = 0) and (words[4] = 0) and (words[5] = $ffff) then begin
  310. {::ffff:/96 exception: v4 IP}
  311. addrlen := 6;
  312. end else begin
  313. addrlen := 8;
  314. end;
  315. {find longest run of zeroes}
  316. runbegin := 0;
  317. runlength := 0;
  318. for a := 0 to addrlen-1 do begin
  319. if words[a] = 0 then begin
  320. c := 0;
  321. for b := a to addrlen-1 do if words[b] = 0 then begin
  322. inc(c);
  323. end else break;
  324. if (c > runlength) then begin
  325. runlength := c;
  326. runbegin := a;
  327. end;
  328. end;
  329. end;
  330. result := '';
  331. for a := 0 to runbegin-1 do begin
  332. if (a <> 0) then result := result + ':';
  333. result := result + makehex(words[a]);
  334. end;
  335. if runlength > 0 then result := result + '::';
  336. c := runbegin+runlength;
  337. for a := c to addrlen-1 do begin
  338. if (a > c) then result := result + ':';
  339. result := result + makehex(words[a]);
  340. end;
  341. if addrlen = 6 then begin
  342. result := result + ':'+inttostr(bytes[12])+'.'+inttostr(bytes[13])+'.'+inttostr(bytes[14])+'.'+inttostr(bytes[15]);
  343. end;
  344. end;
  345. function ip6strtobin(const s:string;var bin:tin6_addr):boolean;
  346. var
  347. a,b:integer;
  348. fields:array[0..7] of string;
  349. fieldcount:integer;
  350. emptyfield:integer;
  351. wordcount:integer;
  352. words:array[0..7] of word;
  353. bytes:array[0..15] of byte absolute bin;
  354. begin
  355. result := false;
  356. for a := 0 to 7 do fields[a] := '';
  357. fieldcount := 0;
  358. for a := 1 to length(s) do begin
  359. if s[a] = ':' then inc(fieldcount) else fields[fieldcount] := fields[fieldcount] + s[a];
  360. if fieldcount > 7 then exit;
  361. end;
  362. if fieldcount < 2 then exit;
  363. {find the empty field (compressed zeroes), not counting the first and last there may be at most 1}
  364. emptyfield := -1;
  365. for a := 1 to fieldcount-1 do begin
  366. if fields[a] = '' then begin
  367. if emptyfield = -1 then emptyfield := a else exit;
  368. end;
  369. end;
  370. {check if last field is a valid v4 IP}
  371. a := longip(fields[fieldcount]);
  372. if (a <> 0) or (fields[fieldcount] = '0.0.0.0') then wordcount := 6 else wordcount := 8;
  373. {0:1:2:3:4:5:6.6.6.6
  374. 0:1:2:3:4:5:6:7}
  375. fillchar(words,sizeof(words),0);
  376. if wordcount = 6 then begin
  377. if fieldcount > 6 then exit;
  378. words[6] := a shr 16;
  379. words[7] := a and $ffff;
  380. end;
  381. if emptyfield = -1 then begin
  382. {no run length: must be an exact number of fields}
  383. if wordcount = 6 then begin
  384. if fieldcount <> 6 then exit;
  385. emptyfield := 5;
  386. end else if wordcount = 8 then begin
  387. if fieldcount <> 7 then exit;
  388. emptyfield := 7;
  389. end else exit;
  390. end;
  391. for a := 0 to emptyfield do begin
  392. if fields[a] = '' then b := 0 else b := strtointdef('$'+fields[a],-1);
  393. if (b < 0) or (b > $ffff) then exit;
  394. words[a] := b;
  395. end;
  396. if wordcount = 6 then dec(fieldcount);
  397. for a := wordcount-1 downto wordcount-(fieldcount-emptyfield) do begin
  398. b := a+fieldcount-wordcount+1;
  399. if fields[b] = '' then b := 0 else b := strtointdef('$'+fields[b],-1);
  400. if (b < 0) or (b > $ffff) then exit;
  401. words[a] := b;
  402. end;
  403. for a := 0 to 7 do begin
  404. bytes[a shl 1] := words[a] shr 8;
  405. bytes[a shl 1 or 1] := words[a] and $ff;
  406. end;
  407. result := true;
  408. end;
  409. {$endif}
  410. function comparebinip(const ip1,ip2:tbinip):boolean;
  411. begin
  412. if (ip1.ip <> ip2.ip) then begin
  413. result := false;
  414. exit;
  415. end;
  416. {$ifdef ipv6}
  417. if ip1.family = AF_INET6 then begin
  418. if (ip1.ip6.s6_addr32[1] <> ip2.ip6.s6_addr32[1])
  419. or (ip1.ip6.s6_addr32[2] <> ip2.ip6.s6_addr32[2])
  420. or (ip1.ip6.s6_addr32[3] <> ip2.ip6.s6_addr32[3]) then begin
  421. result := false;
  422. exit;
  423. end;
  424. end;
  425. {$endif}
  426. result := (ip1.family = ip2.family);
  427. end;
  428. procedure maskbits(var binip:tbinip;bits:integer);
  429. const
  430. ipmax={$ifdef ipv6}15{$else}3{$endif};
  431. type tarr=array[0..ipmax] of byte;
  432. var
  433. arr:^tarr;
  434. a,b:integer;
  435. begin
  436. arr := @binip.ip;
  437. if bits = 0 then b := 0 else b := ((bits-1) div 8)+1;
  438. for a := b to ipmax do begin
  439. arr[a] := 0;
  440. end;
  441. if (bits and 7 <> 0) then begin
  442. arr[bits shr 3] := arr[bits div 8] and not ($ff shr (bits and 7))
  443. end;
  444. end;
  445. function comparebinipmask;
  446. begin
  447. maskbits(ip1,bits);
  448. maskbits(ip2,bits);
  449. result := comparebinip(ip1,ip2);
  450. end;
  451. function needconverttov4(const ip:tbinip):boolean;
  452. begin
  453. {$ifdef ipv6}
  454. if ip.family = AF_INET6 then begin
  455. if (ip.ip6.u6_addr32[0] = 0) and (ip.ip6.u6_addr32[1] = 0) and
  456. (ip.ip6.u6_addr16[4] = 0) and (ip.ip6.u6_addr16[5] = $ffff) then begin
  457. result := true;
  458. exit;
  459. end;
  460. end;
  461. {$endif}
  462. result := false;
  463. end;
  464. {converts a binary IP to v4 if it is a v6 IP in the v4 range}
  465. procedure converttov4(var ip:tbinip);
  466. begin
  467. {$ifdef ipv6}
  468. if needconverttov4(ip) then begin
  469. ip.family := AF_INET;
  470. ip.ip := ip.ip6.s6_addr32[3];
  471. end;
  472. {$endif}
  473. end;
  474. {-----------biniplist stuff--------------------------------------------------}
  475. const
  476. biniplist_prefix='bipl'#0;
  477. //fpc 1.0.x doesn't seem to like use of length function in a constant
  478. //definition
  479. //biniplist_prefixlen=length(biniplist_prefix);
  480. biniplist_prefixlen=5;
  481. function biniplist_new:tbiniplist;
  482. begin
  483. result := biniplist_prefix;
  484. end;
  485. procedure biniplist_add(var l:tbiniplist;ip:tbinip);
  486. var
  487. a:integer;
  488. begin
  489. a := biniplist_getcount(l);
  490. biniplist_setcount(l,a+1);
  491. biniplist_set(l,a,ip);
  492. end;
  493. function biniplist_getcount(const l:tbiniplist):integer;
  494. begin
  495. result := (length(l)-biniplist_prefixlen) div sizeof(tbinip);
  496. end;
  497. function biniplist_get(const l:tbiniplist;index:integer):tbinip;
  498. begin
  499. if (index >= biniplist_getcount(l)) then begin
  500. fillchar(result,sizeof(result),0);
  501. exit;
  502. end;
  503. move(l[index*sizeof(tbinip)+1+biniplist_prefixlen],result,sizeof(result));
  504. end;
  505. procedure biniplist_set(var l:tbiniplist;index:integer;ip:tbinip);
  506. begin
  507. uniquestring(l);
  508. move(ip,l[index*sizeof(tbinip)+1+biniplist_prefixlen],sizeof(ip));
  509. end;
  510. procedure biniplist_setcount(var l:tbiniplist;newlen:integer);
  511. begin
  512. setlength(l,(sizeof(tbinip)*newlen)+biniplist_prefixlen);
  513. end;
  514. procedure biniplist_free(var l:tbiniplist);
  515. begin
  516. l := '';
  517. end;
  518. procedure biniplist_addlist;
  519. begin
  520. l := l + copy(l2,biniplist_prefixlen+1,maxlongint);
  521. end;
  522. function biniplist_tostr(const l:tbiniplist):string;
  523. var
  524. a:integer;
  525. begin
  526. result := '(';
  527. for a := 0 to biniplist_getcount(l)-1 do begin
  528. if result <> '(' then result := result + ', ';
  529. result := result + ipbintostr(biniplist_get(l,a));
  530. end;
  531. result := result + ')';
  532. end;
  533. function isbiniplist(const l:tbiniplist):boolean;
  534. var
  535. i : integer;
  536. begin
  537. for i := 1 to biniplist_prefixlen do begin
  538. if biniplist_prefix[i] <> l[i] then begin
  539. result := false;
  540. exit;
  541. end;
  542. end;
  543. result := true;
  544. end;
  545. procedure addipsoffamily(var l:tbiniplist;const l2:tbiniplist;family:integer);
  546. var
  547. a:integer;
  548. biniptemp:tbinip;
  549. begin
  550. for a := biniplist_getcount(l2)-1 downto 0 do begin
  551. biniptemp := biniplist_get(l2,a);
  552. if (biniptemp.family = family) then biniplist_add(l,biniptemp);
  553. end;
  554. end;
  555. end.