1{ lCommon
2
3  CopyRight (C) 2004-2008 Ales Katona
4
5  This library is Free software; you can rediStribute it and/or modify it
6  under the terms of the GNU Library General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or (at your
8  option) any later version.
9
10  This program is diStributed in the hope that it will be useful, but WITHOUT
11  ANY WARRANTY; withOut even the implied warranty of MERCHANTABILITY or
12  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
13  for more details.
14
15  You should have received a Copy of the GNU Library General Public License
16  along with This library; if not, Write to the Free Software Foundation,
17  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
19  This license has been modified. See File LICENSE.ADDON for more inFormation.
20  Should you find these sources without a LICENSE File, please contact
21  me at ales@chello.sk
22}
23
24unit lCommon;
25
26{$mode objfpc}{$H+}
27{$inline on}
28
29interface
30
31uses
32  {$i sys/osunits.inc}
33
34const
35  {$IFDEF WINDOWS}
36  SOL_SOCKET = $ffff;
37  LMSG = 0;
38  SOCKET_ERROR = WinSock2.SOCKET_ERROR;
39  SHUT_RDWR = SD_BOTH;
40  SHUT_WR = SD_SEND;
41  {$ENDIF}
42
43  {$IFDEF OS2}
44  SOL_SOCKET = WinSock.SOL_SOCKET;
45  LMSG = 0;
46  SOCKET_ERROR = WinSock.SOCKET_ERROR;
47  {$ENDIF}
48
49  {$IFDEF NETWARE}
50  SOL_SOCKET = WinSock.SOL_SOCKET;
51  LMSG = 0;
52  SOCKET_ERROR = WinSock.SOCKET_ERROR;
53  {$ENDIF}
54
55  {$IFDEF UNIX}
56  INVALID_SOCKET = -1;
57  SOCKET_ERROR = -1;
58    {$IFDEF LINUX} // TODO: fix this crap, some don't even have MSG_NOSIGNAL
59    LMSG = MSG_NOSIGNAL;
60    {$ELSE}
61      {$IFDEF FREEBSD}
62        LMSG = $20000; // FPC BUG in 2.0.4-, freeBSD value
63      {$ELSE}
64        LMSG = 0;
65      {$ENDIF}
66    {$ENDIF}
67
68    {$IFDEF DARWIN}
69    SO_NOSIGPIPE = $1022; // for fpc 2.0.4
70    {$ENDIF}
71  {$ENDIF}
72  { Default Values }
73  LDEFAULT_BACKLOG = 5;
74  BUFFER_SIZE = 262144;
75  { Net types }
76  LAF_INET      =  AF_INET;
77  LAF_INET6     = AF_INET6;
78  { Address constants }
79  LADDR_ANY = '0.0.0.0';
80  LADDR_BR  = '255.255.255.255';
81  LADDR_LO  = '127.0.0.1';
82  LADDR6_ANY = '::0';
83  LADDR6_LO  = '::1';
84  { ICMP }
85  LICMP_ECHOREPLY     = 0;
86  LICMP_UNREACH       = 3;
87  LICMP_ECHO          = 8;
88  LICMP_TIME_EXCEEDED = 11;
89  { Protocols }
90  LPROTO_IP     =     0;
91  LPROTO_ICMP   =     1;
92  LPROTO_IGMP   =     2;
93  LPROTO_TCP    =     6;
94  LPROTO_UDP    =    17;
95  LPROTO_IPV6   =    41;
96  LPROTO_ICMPV6 =    58;
97  LPROTO_RAW    =   255;
98  LPROTO_MAX    =   256;
99
100type
101
102  { TLSocketAddress }
103
104  TLSocketAddress = record
105    case Integer of
106      LAF_INET  : (IPv4: TInetSockAddr);
107      LAF_INET6 : (IPv6: TInetSockAddr6);
108  end;
109
110  { Base functions }
111  {$IFNDEF UNIX}
112  function fpSelect(const nfds: Integer; const readfds, writefds, exceptfds: PFDSet;
113                    const timeout: PTimeVal): Integer; inline;
114  function fpFD_ISSET(const Socket: Integer; var FDSet: TFDSet): Integer; inline;
115  procedure fpFD_SET(const Socket: Integer; var FDSet: TFDSet); inline;
116  procedure fpFD_ZERO(var FDSet: TFDSet); inline;
117  {$ENDIF}
118  { DNS }
119  function GetHostName(const Address: string): string;
120  function GetHostIP(const Name: string): string;
121  function GetHostName6(const Address: string): string;
122  function GetHostIP6(const Name: string): string;
123
124  function LStrError(const Ernum: Longint; const UseUTF8: Boolean = False): string;
125  function LSocketError: Longint;
126
127  function SetBlocking(const aHandle: Integer; const aValue: Boolean): Boolean;
128//  function SetNoDelay(const aHandle: Integer; const aValue: Boolean): Boolean;
129
130  function IsBlockError(const anError: Integer): Boolean; inline;
131  function IsNonFatalError(const anError: Integer): Boolean; inline;
132  function IsPipeError(const anError: Integer): Boolean; inline;
133
134  function TZSeconds: Integer; inline;
135
136  function StrToHostAddr(const IP: string): Cardinal; inline;
137  function HostAddrToStr(const Entry: Cardinal): string; inline;
138  function StrToNetAddr(const IP: string): Cardinal; inline;
139  function NetAddrToStr(const Entry: Cardinal): string; inline;
140
141  procedure FillAddressInfo(var aAddrInfo: TLSocketAddress; const aFamily: sa_family_t;
142                            const Address: string; const aPort: Word);
143
144implementation
145
146uses
147  StrUtils
148
149{$IFNDEF UNIX}
150
151{$IFDEF WINDOWS}
152  , Windows, lws2tcpip;
153
154{$IFDEF WINCE}
155
156function LStrError(const Ernum: Longint; const UseUTF8: Boolean = False): string;
157const
158  MAX_ERROR = 1024;
159var
160  Tmp: string;
161  TmpW: widestring;
162begin
163  Result := '[' + IntToStr(Ernum) + '] ';
164    SetLength(TmpW, MAX_ERROR);
165    SetLength(TmpW, FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM or
166                                   FORMAT_MESSAGE_IGNORE_INSERTS or
167                                   FORMAT_MESSAGE_ARGUMENT_ARRAY,
168                                   nil, Ernum, 0, @TmpW[1], MAX_ERROR, nil));
169    Tmp := UTF8Encode(TmpW);
170  if Length(Tmp) > 2 then
171    Delete(Tmp, Length(Tmp)-1, 2);
172  Result := Tmp;
173end;
174
175{$ELSE} // any other windows
176
177function LStrError(const Ernum: Longint; const UseUTF8: Boolean = False): string;
178const
179  MAX_ERROR = 1024;
180var
181  Tmp: string;
182  TmpW: widestring;
183begin
184  Result := ' [' + IntToStr(Ernum) + ']: ';
185  if USEUtf8 then begin
186    SetLength(TmpW, MAX_ERROR);
187    SetLength(TmpW, FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM or
188                                   FORMAT_MESSAGE_IGNORE_INSERTS or
189                                   FORMAT_MESSAGE_ARGUMENT_ARRAY,
190                                   nil, Ernum, 0, @TmpW[1], MAX_ERROR, nil));
191    Tmp := UTF8Encode(TmpW);
192  end else begin
193    SetLength(Tmp, MAX_ERROR);
194    SetLength(Tmp, FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or
195                                 FORMAT_MESSAGE_IGNORE_INSERTS or
196                                 FORMAT_MESSAGE_ARGUMENT_ARRAY,
197                                 nil, Ernum, 0, @Tmp[1], MAX_ERROR, nil));
198  end;
199  if Length(Tmp) > 2 then
200    Delete(Tmp, Length(Tmp)-1, 2);
201  Result := Result + Tmp;
202end;
203
204{$ENDIF}
205
206function TZSeconds: integer; inline;
207var
208  lInfo: Windows.TIME_ZONE_INFORMATION;
209begin
210  { lInfo.Bias is in minutes }
211  if Windows.GetTimeZoneInformation(@lInfo) <> $FFFFFFFF then
212    Result := lInfo.Bias * 60
213  else
214    Result := 0;
215end;
216
217{$ELSE}
218  ; // uses
219
220function LStrError(const Ernum: Longint; const UseUTF8: Boolean = False): string;
221begin
222  Result := IntToStr(Ernum); // TODO: fix for non-windows winsock users
223end;
224
225function TZSeconds: integer; inline;
226begin
227  Result := 0; // todo: fix for non-windows non unix
228end;
229
230{$ENDIF}
231
232function LSocketError: Longint;
233begin
234  Result := WSAGetLastError;
235end;
236
237function CleanError(const Ernum: Longint): Byte;
238begin
239  Result := Byte(Ernum - 10000);
240end;
241
242function fpSelect(const nfds: Integer; const readfds, writefds, exceptfds: PFDSet;
243                  const timeout: PTimeVal): Longint; inline;
244begin
245  Result := Select(nfds, readfds, writefds, exceptfds, timeout);
246end;
247
248function fpFD_ISSET(const Socket: Longint; var FDSet: TFDSet): Integer; inline;
249begin
250  Result := 0;
251  if FD_ISSET(Socket, FDSet) then
252    Result := 1;
253end;
254
255procedure fpFD_SET(const Socket: Longint; var FDSet: TFDSet); inline;
256begin
257  FD_SET(Socket, FDSet);
258end;
259
260procedure fpFD_ZERO(var FDSet: TFDSet); inline;
261begin
262  FD_ZERO(FDSet);
263end;
264
265function GetHostName(const Address: string): string;
266var
267  HE: PHostEnt;
268  Addr: DWord;
269begin
270  Result := '';
271  HE := nil;
272  Addr := inet_addr(PChar(Address));
273  HE := gethostbyaddr(@Addr, SizeOf(Addr), AF_INET);
274  if Assigned(HE) then
275    Result := HE^.h_name;
276end;
277
278function GetHostIP(const Name: string): string;
279var
280  HE: PHostEnt;
281  P: PDWord;
282begin
283  Result := '';
284  HE := nil;
285  HE := gethostbyname(PChar(Name));
286  if Assigned(HE) then begin
287    P := Pointer(HE^.h_addr_list[0]);
288    Result := NetAddrToStr(P^);
289  end;
290end;
291
292function GetHostName6(const Address: string): string;
293var
294  H: TAddrInfo;
295  R: PAddrInfo;
296  n: Integer;
297begin
298  Result := '';
299  ZeroMemory(@H, SizeOf(H));
300  H.ai_flags := AI_NUMERICHOST;
301  H.ai_family := AF_INET6;
302  H.ai_protocol := PF_INET6;
303  H.ai_socktype := SOCK_STREAM;
304
305  n := getaddrinfo(pChar(Address), nil, @H, R);
306  if n <> 0 then
307    Exit;
308  Result := R^.ai_canonname;
309  freeaddrinfo(R);
310end;
311
312function GetHostIP6(const Name: string): string;
313var
314  H: TAddrInfo;
315  R: PAddrInfo;
316  n: Integer;
317begin
318  Result := '';
319  ZeroMemory(@H, SizeOf(H));
320  H.ai_family := AF_INET6;
321  H.ai_protocol := PF_INET6;
322  H.ai_socktype := SOCK_STREAM;
323
324  n := getaddrinfo(pChar(Name), nil, @H, R);
325  if n <> 0 then
326    Exit;
327  Result := NetAddrToStr6(sockets.in6_addr(R^.ai_addr^));
328  freeaddrinfo(R);
329end;
330
331function SetBlocking(const aHandle: Integer; const aValue: Boolean): Boolean;
332const
333  BlockAr: array[Boolean] of DWord = (1, 0);
334var
335  opt: DWord;
336begin
337  opt := BlockAr[aValue];
338  if ioctlsocket(aHandle, Longint(FIONBIO), opt) = SOCKET_ERROR then
339    Exit(False);
340  Result := True;
341end;
342
343function IsBlockError(const anError: Integer): Boolean; inline;
344begin
345  Result := anError = WSAEWOULDBLOCK;
346end;
347
348function IsNonFatalError(const anError: Integer): Boolean; inline;
349begin
350  Result := (anError = WSAEINVAL) or (anError = WSAEFAULT)
351         or (anError = WSAEOPNOTSUPP) or (anError = WSAEMSGSIZE)
352         or (anError = WSAEADDRNOTAVAIL) or (anError = WSAEAFNOSUPPORT)
353         or (anError = WSAEDESTADDRREQ);
354end;
355
356function IsPipeError(const anError: Integer): Boolean; inline;
357begin
358  {$WARNING check these ambiguous errors}
359  Result := anError = WSAECONNRESET;
360end;
361
362{$ELSE}
363
364// unix
365
366  ,Errors, UnixUtil;
367
368function LStrError(const Ernum: Longint; const UseUTF8: Boolean = False): string;
369begin
370  Result := ' [' + IntToStr(Ernum) + ']: ' + Errors.StrError(Ernum);
371end;
372
373function LSocketError: Longint;
374begin
375  Result := fpgeterrno;
376end;
377
378function CleanError(const Ernum: Longint): Longint; inline;
379begin
380  Result := Byte(Ernum);
381end;
382
383function GetHostName(const Address: string): string;
384var
385  HE: THostEntry;
386begin
387  Result := '';
388  if GetHostbyAddr(in_addr(StrToHostAddr(Address)), HE) then
389    Result := HE.Name
390  else if ResolveHostbyAddr(in_addr(StrToHostAddr(Address)), HE) then
391    Result := HE.Name;
392end;
393
394function GetHostIP(const Name: string): string;
395var
396  HE: THostEntry;
397begin
398  Result := '';
399  if GetHostByName(Name, HE) then
400    Result := HostAddrToStr(Cardinal(HE.Addr)) // for localhost
401  else if ResolveHostByName(Name, HE) then
402    Result := NetAddrToStr(Cardinal(HE.Addr));
403end;
404
405function GetHostName6(const Address: string): string;
406var
407  HE: THostEntry6;
408begin
409  Result := '';
410{  if GetHostByAddr(StrToHostAddr6(Address), HE) then
411    Result := HE.Name
412  else} if ResolveHostbyAddr6(StrToHostAddr6(Address), HE) then
413    Result := HE.Name;
414end;
415
416function GetHostIP6(const Name: string): string;
417var
418  HE: THostEntry6;
419begin
420  Result := '';
421{  if GetHostByName(Name, HE) then
422    Result := HostAddrToStr6(HE.Addr) // for localhost
423  else} if ResolveHostByName6(Name, HE) then
424    Result := NetAddrToStr6(HE.Addr);
425end;
426
427function SetBlocking(const aHandle: Integer; const aValue: Boolean): Boolean;
428var
429  opt: cInt;
430begin
431  opt := fpfcntl(aHandle, F_GETFL);
432  if opt = SOCKET_ERROR then
433    Exit(False);
434
435  if aValue then
436    opt := opt and not O_NONBLOCK
437  else
438    opt := opt or O_NONBLOCK;
439
440  if fpfcntl(aHandle, F_SETFL, opt) = SOCKET_ERROR then
441    Exit(False);
442  Result := True;
443end;
444
445function IsBlockError(const anError: Integer): Boolean; inline;
446begin
447  Result := (anError = ESysEWOULDBLOCK) or (anError = ESysENOBUFS);
448end;
449
450function IsNonFatalError(const anError: Integer): Boolean; inline;
451begin
452  Result := (anError = ESysEINTR) or (anError = ESysEMSGSIZE)
453         or (anError = ESysEFAULT) or (anError = ESysEINVAL)
454         or (anError = ESysEOPNOTSUPP);
455end;
456
457function IsPipeError(const anError: Integer): Boolean; inline;
458begin
459  Result := anError = ESysEPIPE;
460end;
461
462function TZSeconds: Integer; inline;
463begin
464  Result := unixutil.TZSeconds;
465end;
466
467{$ENDIF}
468
469{function SetNoDelay(const aHandle: Integer; const aValue: Boolean): Boolean;
470var
471  opt: cInt = 0;
472begin
473  if aValue then
474    opt := 1;
475
476  if fpsetsockopt(aHandle, IPPROTO_TCP, TCP_NODELAY, opt, SizeOf(opt)) < 0 then
477    Exit(False);
478
479  Result := True;
480end;}
481
482function StrToHostAddr(const IP: string): Cardinal; inline;
483begin
484  Result := Cardinal(Sockets.StrToHostAddr(IP));
485end;
486
487function HostAddrToStr(const Entry: Cardinal): string; inline;
488begin
489  Result := Sockets.HostAddrToStr(in_addr(Entry));
490end;
491
492function StrToNetAddr(const IP: string): Cardinal; inline;
493begin
494  Result := Cardinal(Sockets.StrToNetAddr(IP));
495end;
496
497function NetAddrToStr(const Entry: Cardinal): string; inline;
498begin
499  Result := Sockets.NetAddrToStr(in_addr(Entry));
500end;
501
502function IsIP6Empty(const aIP6: TInetSockAddr6): Boolean; inline;
503var
504  i: Integer;
505begin
506  Result := True;
507  for i := 0 to High(aIP6.sin6_addr.u6_addr32) do
508    if aIP6.sin6_addr.u6_addr32[i] <> 0 then
509      Exit(False);
510end;
511
512procedure FillAddressInfo(var aAddrInfo: TLSocketAddress; const aFamily: sa_family_t;
513  const Address: string; const aPort: Word);
514begin
515  aAddrInfo.IPv4.sin_family := aFamily;
516  aAddrInfo.IPv4.sin_Port := htons(aPort);
517
518  case aFamily of
519    LAF_INET  :
520      begin
521        aAddrInfo.IPv4.sin_Addr.s_addr := StrToNetAddr(Address);
522        if (Address <> LADDR_ANY) and (aAddrInfo.IPv4.sin_Addr.s_addr = 0) then
523          aAddrInfo.IPv4.sin_Addr.s_addr := StrToNetAddr(GetHostIP(Address));
524      end;
525    LAF_INET6 :
526      begin
527        aAddrInfo.IPv6.sin6_addr := StrToNetAddr6(Address);
528        if (Address <> LADDR6_ANY) and (IsIP6Empty(aAddrInfo.IPv6)) then
529          aAddrInfo.IPv6.sin6_addr := StrToNetAddr6(GetHostIP6(Address));
530      end;
531  end;
532end;
533
534
535end.
536
537