1{ 2 This file is part of the Free Pascal run time library. 3 Copyright (c) 1999-2000 by the Free Pascal development team 4 5 See the file COPYING.FPC, included in this distribution, 6 for details about the copyright. 7 8 This program is distributed in the hope that it will be useful, 9 but WITHOUT ANY WARRANTY; without even the implied warranty of 10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 11 12 **********************************************************************} 13 14{****************************************************************************** 15 Text File Writeln/ReadLn Support 16******************************************************************************} 17 18 19Procedure OpenSock(var F:Text); 20begin 21 if textrec(f).handle=UnusedHandle then 22 textrec(f).mode:=fmclosed 23 else 24 case textrec(f).userdata[1] of 25 S_OUT : textrec(f).mode:=fmoutput; 26 S_IN : textrec(f).mode:=fminput; 27 else 28 textrec(f).mode:=fmclosed; 29 end; 30end; 31 32 33 34procedure iosock(var f:text); 35 36var r:sizeint; 37 def_error:word; 38 39begin 40 with textrec(f) do 41 begin 42 case mode of 43 fmoutput: 44 begin 45 repeat 46{$ifdef use_readwrite} 47 r:=fpwrite(handle,bufptr^,bufpos); 48{$else} 49 r:=fpsend(handle,bufptr,bufpos,0); 50{$endif} 51 until (r<>-1) or (SocketError <> EsockEINTR); 52 bufend:=r; 53 def_error:=101; {File write error.} 54 end; 55 fminput: 56 begin 57 repeat 58{$ifdef use_readwrite} 59 r:=fpread(handle,bufptr^,bufsize); 60{$else} 61 r:=fprecv(handle,bufptr,bufsize,0); 62{$endif} 63 until (r<>-1) or (SocketError <> EsockEINTR); 64 bufend:=r; 65 def_error:=100; {File read error.} 66 end; 67 end; 68 if r=-1 then 69 case SocketError of 70 EsockEBADF: 71{ EsysENOTSOCK:} {Why is this constant not defined? (DM)} 72 inoutres:=6; {Invalid file handle.} 73 EsockEFAULT: 74 inoutres:=217; 75 EsockEINVAL: 76 inoutres:=218; 77 else 78 inoutres:=def_error; 79 end; 80 bufpos:=0; 81 end; 82end; 83 84 85 86Procedure FlushSock(var F:Text); 87begin 88 if (textrec(f).mode=fmoutput) and (textrec(f).bufpos<>0) then 89 begin 90 IOSock(f); 91 textrec(f).bufpos:=0; 92 end; 93end; 94 95 96 97Procedure CloseSock(var F:text); 98begin 99 { Nothing special has to be done here } 100end; 101 102 103 104Procedure Sock2Text(Sock:Longint;Var SockIn,SockOut:Text); 105{ 106 Set up two Pascal Text file descriptors for reading and writing) 107} 108begin 109{ First the reading part.} 110 Assign(SockIn,'.'); 111 Textrec(SockIn).Handle:=Sock; 112 Textrec(Sockin).userdata[1]:=S_IN; 113 TextRec(SockIn).OpenFunc:=@OpenSock; 114 TextRec(SockIn).InOutFunc:=@IOSock; 115 TextRec(SockIn).FlushFunc:=@FlushSock; 116 TextRec(SockIn).CloseFunc:=@CloseSock; 117 TextRec(SockIn).Mode := fmInput; 118 Case DefaultTextLineBreakStyle Of 119 tlbsLF: TextRec(sockin).LineEnd := #10; 120 tlbsCRLF: TextRec(sockin).LineEnd := #13#10; 121 tlbsCR: TextRec(sockin).LineEnd := #13; 122 End; 123{ Now the writing part. } 124 Assign(SockOut,'.'); 125 Textrec(SockOut).Handle:=Sock; 126 Textrec(SockOut).userdata[1]:=S_OUT; 127 TextRec(SockOut).OpenFunc:=@OpenSock; 128 TextRec(SockOut).InOutFunc:=@IOSock; 129 TextRec(SockOut).FlushFunc:=@FlushSock; 130 TextRec(SockOut).CloseFunc:=@CloseSock; 131 TextRec(SockOut).Mode := fmOutput; 132 133 Case DefaultTextLineBreakStyle Of 134 tlbsLF: TextRec(sockout).LineEnd := #10; 135 tlbsCRLF: TextRec(sockout).LineEnd := #13#10; 136 tlbsCR: TextRec(sockout).LineEnd := #13; 137 End; 138end; 139 140 141{****************************************************************************** 142 Untyped File 143******************************************************************************} 144 145Procedure Sock2File(Sock:Longint;Var SockIn,SockOut:File); 146begin 147{Input} 148 Assign(SockIn,'.'); 149 FileRec(SockIn).Handle:=Sock; 150 FileRec(SockIn).RecSize:=1; 151 FileRec(Sockin).userdata[1]:=S_IN; 152 FileRec(SockIn).Mode := fmInput; 153 154{Output} 155 Assign(SockOut,'.'); 156 FileRec(SockOut).Handle:=Sock; 157 FileRec(SockOut).RecSize:=1; 158 FileRec(SockOut).userdata[1]:=S_OUT; 159 FileRec(SockOut).Mode := fmOutput; 160end; 161 162{****************************************************************************** 163 InetSock 164******************************************************************************} 165 166Function DoAccept(Sock:longint;Var addr:TInetSockAddr):longint; 167 168Var AddrLen : Longint; 169 170begin 171 AddrLEn:=SizeOf(Addr); 172 repeat 173 DoAccept:=fpaccept(Sock,@Addr,@AddrLen); 174 until (DoAccept<>-1) or (SocketError <> EsockEINTR); 175end; 176 177Function DoConnect(Sock:longint;const addr: TInetSockAddr): Boolean; 178 179var 180 res: longint; 181begin 182 repeat 183 res:=fpconnect(Sock,@Addr,SizeOF(TInetSockAddr)); 184 until (res<>-1) or (SocketError <> EsockEINTR); 185 DoConnect:= res = 0; 186end; 187 188{$warnings off} 189 190Function Connect(Sock:longint;const addr: TInetSockAddr;var SockIn,SockOut:text):Boolean; 191 192begin 193 Connect:=DoConnect(Sock,addr); 194 If Connect then 195 Sock2Text(Sock,SockIn,SockOut); 196end; 197 198Function Connect(Sock:longint;const addr:TInetSockAddr;var SockIn,SockOut:file):Boolean; 199 200begin 201 Connect:=DoConnect(Sock,addr); 202 If Connect then 203 Sock2File(Sock,SockIn,SockOut); 204end; 205 206Function Accept(Sock:longint;var addr:TInetSockAddr;var SockIn,SockOut:text):Boolean; 207var 208 s : longint; 209begin 210 S:=DoAccept(Sock,addr); 211 if S>0 then 212 begin 213 Sock2Text(S,SockIn,SockOut); 214 Accept:=true; 215 end 216 else 217 Accept:=false; 218end; 219 220Function Accept(Sock:longint;var addr:TInetSockAddr;var SockIn,SockOut:File):Boolean; 221var 222 s : longint; 223begin 224 S:=DoAccept(Sock,addr); 225 if S>0 then 226 begin 227 Sock2File(S,SockIn,SockOut); 228 Accept:=true; 229 end 230 else 231 Accept:=false; 232end; 233 234{$warnings on} 235 236type thostaddr= packed array[1..4] of byte; 237 238function htonl( host : cardinal):cardinal; inline;overload; 239begin 240{$ifdef FPC_BIG_ENDIAN} 241 htonl:=host; 242{$else} 243 htonl:=SwapEndian(host); 244{$endif} 245end; 246 247Function NToHl (Net : cardinal) : cardinal; inline;overload; 248begin 249{$ifdef FPC_BIG_ENDIAN} 250 ntohl:=net; 251{$else} 252 ntohl:=SwapEndian(net); 253{$endif} 254end; 255 256function htons( host : word):word; inline; 257 258begin 259{$ifdef FPC_BIG_ENDIAN} 260 htons:=host; 261{$else} 262 htons:=SwapEndian(host); 263{$endif} 264end; 265 266Function NToHs (Net : word):word; inline; 267 268begin 269{$ifdef FPC_BIG_ENDIAN} 270 ntohs:=net; 271{$else} 272 ntohs:=SwapEndian(net); 273{$endif} 274end; 275 276Type array4int = array[1..4] of byte; 277 278function NetAddrToStr (Entry : in_addr) : AnsiString; 279 280Var Dummy : Ansistring; 281 i,j : Longint; 282 283begin 284 NetAddrToStr:=''; 285 j:=entry.s_addr; 286 For I:=1 to 4 do 287 begin 288 Str(array4int(j)[i],Dummy); 289 NetAddrToStr:=NetAddrToStr+Dummy; 290 If I<4 Then 291 NetAddrToStr:=NetAddrToStr+'.'; 292 end; 293end; 294 295function HostAddrToStr (Entry : in_addr) : AnsiString; 296 297Var x: in_addr; 298 299begin 300 x.s_addr:=htonl(entry.s_addr); 301 HostAddrToStr:=NetAddrToStr(x); 302end; 303 304function StrToHostAddr(IP : AnsiString) : in_addr ; 305var 306 ip4: in_addr; 307begin 308 ip4.s_addr := 0; 309 StrToHostAddr.s_addr:=0; //:=NoAddress; 310 if TryStrToHostAddr(IP, ip4) then 311 StrToHostAddr := ip4; 312end; 313 314function TryStrToHostAddr(IP: AnsiString; out ip4: in_addr): Boolean; 315 316Const 317 AllowedChars = ['.','0'..'9']; 318 319Var 320 Dummy : AnsiString; 321 I,j,k : Longint; 322 Temp : in_addr; 323 324begin 325 TryStrToHostAddr := False; 326 ip4.s_addr:=0; //:=NoAddress; 327 for I:=1 to Length(IP) do 328 if Not (IP[I] in AllowedChars) then 329 exit; 330 For I:=1 to 4 do 331 begin 332 If I<4 Then 333 begin 334 J:=Pos('.',IP); 335 If J=0 then 336 exit; 337 Dummy:=Copy(IP,1,J-1); 338 Delete (IP,1,J); 339 end 340 else 341 Dummy:=IP; 342 if Length(Dummy)>3 then 343 exit; 344 Val (Dummy,k,J); 345 If J<>0 then 346 exit; 347 array4int(temp.s_addr)[i]:=byte(k); 348 { check if after wrapping to a byte, our number is 349 still the same. if not, it can't be part of an IP. } 350 If array4int(temp.s_addr)[i]<>k then 351 exit; 352 end; 353 ip4.s_addr:=ntohl(Temp.s_addr); 354 TryStrToHostAddr := True; 355end; 356 357function StrToNetAddr(IP : AnsiString) : in_addr; 358 359begin 360 StrToNetAddr.s_addr:=htonl(StrToHostAddr(IP).s_addr); 361end; 362 363Function HostToNet (Host : in_addr):in_addr; 364 365begin 366 HostToNet.s_addr:=htonl(host.s_addr); 367end; 368 369Function NetToHost (Net : in_addr) : in_addr; 370 371begin 372 NetToHost.s_addr:=ntohl(net.s_addr); 373end; 374 375Function HostToNet (Host : Longint) : Longint; 376 377begin 378 HostToNet:=htonl(host); 379end; 380 381Function NetToHost (Net : Longint) : Longint; 382 383begin 384 NetToHost:=ntohl(net); 385end; 386 387Function ShortHostToNet (Host : Word) : Word; 388 389begin 390 ShortHostToNet:=htons(host); 391end; 392 393Function ShortNetToHost (Net : Word) : Word; 394 395begin 396 ShortNEtToHost:=ntohs(net); 397end; 398 399const digittab : shortstring = ('0123456789ABCDEF'); 400 401function lclinttohex (i:longint;digits:longint): ansistring; 402 403begin 404 SetLength(lclinttohex,4); 405 lclinttohex[4]:=digittab[1+(i and 15)]; 406 lclinttohex[3]:=digittab[1+((i shr 4) and 15)]; 407 lclinttohex[2]:=digittab[1+((i shr 8) and 15)]; 408 lclinttohex[1]:=digittab[1+((i shr 12) and 15)];; 409end; 410 411function HostAddrToStr6 (Entry : TIn6_Addr) :ansiString; 412var 413 i: byte; 414 zr1,zr2: set of byte; 415 zc1,zc2: byte; 416 have_skipped: boolean; 417begin 418 zr1 := []; 419 zr2 := []; 420 zc1 := 0; 421 zc2 := 0; 422 for i := 0 to 7 do begin 423 if Entry.u6_addr16[i] = 0 then begin 424 include(zr2, i); 425 inc(zc2); 426 end else begin 427 if zc1 < zc2 then begin 428 zc1 := zc2; 429 zr1 := zr2; 430 zc2 := 0; zr2 := []; 431 end; 432 end; 433 end; 434 if zc1 < zc2 then begin 435 zc1 := zc2; 436 zr1 := zr2; 437 end; 438 SetLength(HostAddrToStr6, 8*5-1); 439 SetLength(HostAddrToStr6, 0); 440 have_skipped := false; 441 for i := 0 to 7 do begin 442 if not (i in zr1) then begin 443 if have_skipped then begin 444 if HostAddrToStr6 = '' 445 then HostAddrToStr6 := '::' 446 else HostAddrToStr6 := HostAddrToStr6 + ':'; 447 have_skipped := false; 448 end; 449 // FIXME: is that shortnettohost really proper there? I wouldn't be too sure... 450 HostAddrToStr6 := HostAddrToStr6 +lclIntToHex(ShortNetToHost(Entry.u6_addr16[i]), 1) + ':'; 451 end else begin 452 have_skipped := true; 453 end; 454 end; 455 if have_skipped then 456 if HostAddrToStr6 = '' 457 then HostAddrToStr6 := '::' 458 else HostAddrToStr6 := HostAddrToStr6 + ':'; 459 460 if HostAddrToStr6 = '' then HostAddrToStr6 := '::'; 461 if not (7 in zr1) then 462 SetLength(HostAddrToStr6, Length(HostAddrToStr6)-1); 463end; 464 465function StrToHostAddr6(IP: AnsiString): in6_addr; 466var 467 i6: in6_addr; 468begin 469 i6.u6_addr32[0] := 0; 470 i6.u6_addr32[1] := 0; 471 i6.u6_addr32[2] := 0; 472 i6.u6_addr32[3] := 0; 473 StrToHostAddr6.u6_addr32[0] := 0; 474 StrToHostAddr6.u6_addr32[1] := 0; 475 StrToHostAddr6.u6_addr32[2] := 0; 476 StrToHostAddr6.u6_addr32[3] := 0; 477 478 if TryStrToHostAddr6(IP, i6) then 479 StrToHostAddr6 := i6; 480end; 481 482function TryStrToHostAddr6(IP: AnsiString; out ip6: in6_addr): Boolean; 483type 484 TCharClass = (cHexDigit, cColon, cDot, cUnknown, cEndStr); 485 TParserMode = (pmIPv6, pmIPv4); 486 487 TCharRec = record 488 ch: AnsiChar; 489 ctype: TCharClass; 490 end; 491 492 TToken = record 493 s: ShortString; 494 tt: TCharClass; 495 end; 496 497 function get_char_class(ch: AnsiChar): TCharClass; 498 begin 499 get_char_class := cUnknown; 500 case ch of 501 'A' .. 'F', 'a' .. 'f', '0' .. '9': get_char_class := cHexDigit; 502 ':': get_char_class := cColon; 503 '.': get_char_class := cDot; 504 else 505 get_char_class := cUnknown; 506 end; 507 end; 508 509 function is_eos(idx: Cardinal): Boolean; 510 begin 511 is_eos := (idx < 1) or (idx > Length(IP)); 512 end; 513 514 function next_char(idx: Cardinal): TCharRec; 515 begin 516 next_char.ctype := cUnknown; 517 if is_eos(idx) then 518 begin 519 next_char.ch := '-'; 520 next_char.ctype := cEndStr; 521 end 522 else 523 begin 524 next_char.ch := IP[idx]; 525 next_char.ctype := get_char_class(next_char.ch); 526 end; 527 end; 528 529 function next_token(var idx: Cardinal): TToken; 530 var 531 rch: TCharRec; 532 prv: TCharClass; 533 begin 534 next_token.s := ''; 535 next_token.tt := cUnknown; 536 rch := next_char(idx); 537 prv := rch.ctype; 538 next_token.tt := rch.ctype; 539 while (rch.ctype <> cEndStr) and (rch.ctype = prv) do 540 begin 541 next_token.s := next_token.s + rch.ch; 542 Inc(idx); 543 rch := next_char(idx); 544 end; 545 end; 546 547 function convert_hextet(const s: ShortString; var res: Word): Boolean; 548 var 549 tmpval,valcode: Word; 550 begin 551 convert_hextet := False; 552 if Length(s) > 4 then exit; 553 Val('0x'+s,tmpval,valcode); 554 if valcode <> 0 then exit; 555 res := htons(tmpval); 556 convert_hextet := True; 557 end; 558 559 function convert_octet(const s: ShortString; var res: Byte): Boolean; 560 var 561 tmpval: Word; 562 valcode: Word; 563 begin 564 convert_octet := False; 565 if Length(s) > 3 then exit; 566 Val(s,tmpval,valcode); 567 if valcode <> 0 then exit; 568 if tmpval > 255 then exit; 569 res := tmpval; 570 convert_octet := True; 571 end; 572 573var 574 tkn, ptkn: TToken; 575 idx: Cardinal; 576 hextet_arr: array[0 .. 7] of Word = (0,0,0,0,0,0,0,0); 577 hextet_idx, octet_idx,coll_start_idx: byte; 578 octet_arr: array[0 .. 3] of byte = (0,0,0,0); 579 coll_zero_seen: Boolean = False; 580 parser_mode: TParserMode = pmIPv6; 581 tmpval: Word = 0; 582 tmpByte: Byte = 0; 583 584begin 585 TryStrToHostAddr6 := False; 586 ip6.u6_addr32[0] := 0; 587 ip6.u6_addr32[1] := 0; 588 ip6.u6_addr32[2] := 0; 589 ip6.u6_addr32[3] := 0; 590 591 if (Length(IP) > 45) or (Length(IP) < 2) then exit; 592 593 hextet_idx := 0; 594 coll_start_idx := 0; 595 octet_idx := 0; 596 idx := 1; 597 598 ptkn.s := ''; 599 ptkn.tt := cUnknown; 600 601 tkn := next_token(idx); 602 while (tkn.tt <> cEndStr) do 603 begin 604 case tkn.tt of 605 cHexDigit: 606 begin 607 case parser_mode of 608 pmIPv6: 609 begin 610 if (hextet_idx <= 7) and (convert_hextet(tkn.s, tmpval)) then 611 begin 612 hextet_arr[hextet_idx] := tmpval; 613 Inc(hextet_idx); 614 end 615 else 616 exit; // too many hextets, or invalid hextet. 617 end; 618 pmIPv4: 619 begin 620 if (octet_idx <= 3) and (convert_octet(tkn.s, tmpByte)) then 621 begin 622 octet_arr[octet_idx] := tmpByte; 623 Inc(octet_idx); 624 end 625 else 626 exit; // too many octets, or invalid octet. 627 end; 628 end; 629 end; 630 631 cColon: 632 begin 633 if (parser_mode = pmIPv4) or (Length(tkn.s) > 2) then exit; 634 if Length(tkn.s) = 2 then 635 begin 636 // if we saw a collapsed sequence before, or if we've already 637 // seen 8 hextets. 638 if coll_zero_seen or (hextet_idx > 7) then exit; 639 coll_zero_seen := True; 640 coll_start_idx := hextet_idx; 641 Inc(hextet_idx); 642 end 643 else if Length(tkn.s) = 1 then 644 begin 645 // is this single colon the first token? if so, address is invalid. 646 // if the prev token is cUnknown, then this must be the first token. 647 if ptkn.tt = cUnknown then exit; 648 end; 649 end; 650 651 cDot: 652 begin 653 if Length(tkn.s) > 1 then exit; 654 655 // By the time we see the first dot, the first octet of the IPv4 656 // address has already been processed as an IPv6 hextet. we have 657 // to backtrack to remove that value from hextet_arr 658 // and reprocess the value as ipv4. 659 if parser_mode = pmIPv6 then 660 begin 661 if ptkn.tt = cHexDigit then 662 begin 663 Dec(hextet_idx); 664 hextet_arr[hextet_idx] := 0; 665 666 if (octet_idx <= 3) and (convert_octet(ptkn.s, tmpByte)) then 667 begin 668 octet_arr[octet_idx] := tmpByte; 669 Inc(octet_idx); 670 end 671 else 672 exit; // too many octets, or invalid octet. 673 end 674 else // dot preceded by something other than digit 675 exit; 676 parser_mode := pmIPv4; 677 end; 678 end; 679 680 cUnknown: 681 exit; 682 end; 683 ptkn := tkn; 684 tkn := next_token(idx); 685 end; 686 687 // if we finished on a . or :, the address is invalid. 688 if (ptkn.tt = cDot) or ((ptkn.tt = cColon) and (Length(ptkn.s) = 1)) then 689 exit; 690 691 // if there's an ipv4 addr, add its octets onto the end 692 // of the ipv6 hextet array. we have to convert the bytes to 693 // words. 694 if (parser_mode = pmIPv4) then 695 begin 696 if (octet_idx = 4) and (hextet_idx <= 6) then 697 begin 698 tmpval := (octet_arr[0] shl 8) + (octet_arr[1]); 699 hextet_arr[hextet_idx] := htons(tmpval); 700 Inc(hextet_idx); 701 tmpval := (octet_arr[2] shl 8) + (octet_arr[3]); 702 hextet_arr[hextet_idx] := htons(tmpval); 703 Inc(hextet_idx); 704 end 705 else 706 exit; // invalid no of ipv4 octets, or not enough room for them. 707 end; 708 709 // finish line is in sight. if we have a collapsed-zeroes sequence 710 // then we must fill that in now. 711 if coll_zero_seen then 712 begin 713 for tmpByte := 0 to coll_start_idx do 714 ip6.u6_addr16[tmpByte] := hextet_arr[tmpByte]; 715 716 // hextet_idx-1 points to the final byte we processed, in the hextet_arr 717 // array. starting there, reading back to coll_start_idx, we copy these 718 // words to the end of the Result array, with word hextet_idx-1 going at 719 // the end of the Result array, hextet_idx-2 going to the end - 1 of Result, 720 // and so on. 721 // NOTE: optimization note -- a memmove/memcpy equivalent could help here. 722 tmpByte := hextet_idx-1; 723 idx := 7; 724 while tmpByte > coll_start_idx do 725 begin 726 ip6.u6_addr16[idx] := hextet_arr[tmpByte]; 727 Dec(tmpByte); 728 Dec(idx); 729 end; 730 end 731 else 732 begin 733 // no collapsed zeroes. we must have exactly 8 words then, or we're short. 734 // NOTE: optimization note: memmove/memcpy equivalent could help here. 735 if hextet_idx < 8 then exit; 736 for tmpByte := 0 to 7 do 737 ip6.u6_addr16[tmpByte] := hextet_arr[tmpByte]; 738 end; 739 TryStrToHostAddr6 := True; 740end; 741 742function NetAddrToStr6 (Entry : TIn6_Addr) : ansiString; 743begin 744 netaddrtostr6 := HostAddrToStr6((Entry)); 745end; 746 747function StrToNetAddr6(IP : ansiString) : TIn6_Addr; 748begin 749 StrToNetAddr6 := StrToHostAddr6(IP); 750end; 751 752 753