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