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