IdStackUnix.pas 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400
  1. {
  2. $Project$
  3. $Workfile$
  4. $Revision$
  5. $DateUTC$
  6. $Id$
  7. This file is part of the Indy (Internet Direct) project, and is offered
  8. under the dual-licensing agreement described on the Indy website.
  9. (http://www.indyproject.org/)
  10. Copyright:
  11. (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  12. $Log$
  13. Rev 1.7 10/26/2004 8:20:04 PM JPMugaas
  14. Fixed some oversights with conversion. OOPS!!!
  15. Rev 1.6 10/26/2004 8:12:32 PM JPMugaas
  16. Now uses TIdStrings and TIdStringList for portability.
  17. Rev 1.5 12/06/2004 15:17:20 CCostelloe
  18. Restructured to correspond with IdStackWindows, now works.
  19. Rev 1.4 07/06/2004 21:31:02 CCostelloe
  20. Kylix 3 changes
  21. Rev 1.3 4/18/04 10:43:22 PM RLebeau
  22. Fixed syntax error
  23. Rev 1.2 4/18/04 10:29:46 PM RLebeau
  24. Renamed Int64Parts structure to TIdInt64Parts
  25. Rev 1.1 4/18/04 2:47:28 PM RLebeau
  26. Conversion support for Int64 values
  27. Removed WSHToNs(), WSNToHs(), WSHToNL(), and WSNToHL() methods, obsolete
  28. Rev 1.0 2004.02.03 3:14:48 PM czhower
  29. Move and updates
  30. Rev 1.3 10/19/2003 5:35:14 PM BGooijen
  31. SetSocketOption
  32. Rev 1.2 2003.10.01 9:11:24 PM czhower
  33. .Net
  34. Rev 1.1 7/5/2003 07:25:50 PM JPMugaas
  35. Added functions to the Linux stack which use the new TIdIPAddress record type
  36. for IP address parameters. I also fixed a compile bug.
  37. Rev 1.0 11/13/2002 08:59:24 AM JPMugaas
  38. }
  39. unit IdStackUnix;
  40. interface
  41. {$i IdCompilerDefines.inc}
  42. {$IFNDEF FPC}
  43. {$Message Fatal 'IdStackUnix is only for FreePascal.'}
  44. {$ENDIF}
  45. uses
  46. Classes,
  47. sockets,
  48. baseunix,
  49. IdStack,
  50. IdStackConsts,
  51. IdGlobal,
  52. IdStackBSDBase;
  53. {$IFDEF FREEBSD}
  54. {$DEFINE SOCK_HAS_SINLEN}
  55. {$ENDIF}
  56. {$IFDEF DARWIN}
  57. {$DEFINE SOCK_HAS_SINLEN}
  58. {$ENDIF}
  59. type
  60. {$IFNDEF NO_REDECLARE}
  61. Psockaddr = ^sockaddr;
  62. {$ENDIF}
  63. TIdSocketListUnix = class (TIdSocketList)
  64. protected
  65. FCount: Integer;
  66. FFDSet: TFDSet;
  67. //
  68. class function FDSelect(AReadSet: PFDSet; AWriteSet: PFDSet; AExceptSet: PFDSet;
  69. const ATimeout: Integer = IdTimeoutInfinite): Integer;
  70. function GetItem(AIndex: Integer): TIdStackSocketHandle; override;
  71. public
  72. procedure Add(AHandle: TIdStackSocketHandle); override;
  73. procedure Remove(AHandle: TIdStackSocketHandle); override;
  74. function Count: Integer; override;
  75. procedure Clear; override;
  76. function Clone: TIdSocketList; override;
  77. function ContainsSocket(AHandle: TIdStackSocketHandle): Boolean; override;
  78. procedure GetFDSet(var VSet: TFDSet);
  79. procedure SetFDSet(var VSet: TFDSet);
  80. class function Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
  81. AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  82. function SelectRead(const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  83. function SelectReadList(var VSocketList: TIdSocketList;
  84. const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  85. end;
  86. TIdStackUnix = class(TIdStackBSDBase)
  87. protected
  88. procedure WriteChecksumIPv6(s: TIdStackSocketHandle; var VBuffer: TIdBytes;
  89. const AOffset: Integer; const AIP: String; const APort: TIdPort);
  90. function GetLastError: Integer;
  91. procedure SetLastError(const AError: Integer);
  92. function HostByName(const AHostName: string;
  93. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
  94. function ReadHostName: string; override;
  95. function WSCloseSocket(ASocket: TIdStackSocketHandle): Integer; override;
  96. function WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
  97. const ABufferLength, AFlags: Integer): Integer; override;
  98. function WSSend(ASocket: TIdStackSocketHandle; const ABuffer;
  99. const ABufferLength, AFlags: Integer): Integer; override;
  100. function WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer; override;
  101. {$IFNDEF VCL_XE3_OR_ABOVE}
  102. procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  103. AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
  104. procedure WSSetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  105. AOptName: TIdSocketOption; const AOptVal; const AOptLen: Integer); override;
  106. {$ENDIF}
  107. public
  108. constructor Create; override;
  109. destructor Destroy; override;
  110. procedure SetBlocking(ASocket: TIdStackSocketHandle; const ABlocking: Boolean); override;
  111. function WouldBlock(const AResult: Integer): Boolean; override;
  112. function WSTranslateSocketErrorMsg(const AErr: Integer): string; override;
  113. function Accept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: TIdPort;
  114. var VIPVersion: TIdIPVersion): TIdStackSocketHandle; override;
  115. procedure Bind(ASocket: TIdStackSocketHandle; const AIP: string;
  116. const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  117. procedure Connect(const ASocket: TIdStackSocketHandle; const AIP: string;
  118. const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  119. function HostByAddress(const AAddress: string;
  120. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
  121. function WSGetLastError: Integer; override;
  122. procedure WSSetLastError(const AErr : Integer); override;
  123. function WSGetServByName(const AServiceName: string): TIdPort; override;
  124. procedure AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings); override;
  125. procedure GetPeerName(ASocket: TIdStackSocketHandle; var VIP: string;
  126. var VPort: TIdPort; var VIPVersion: TIdIPVersion); override;
  127. procedure GetSocketName(ASocket: TIdStackSocketHandle; var VIP: string;
  128. var VPort: TIdPort; var VIPVersion: TIdIPVersion); override;
  129. procedure Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer); override;
  130. function HostToNetwork(AValue: UInt16): UInt16; override;
  131. function NetworkToHost(AValue: UInt16): UInt16; override;
  132. function HostToNetwork(AValue: UInt32): UInt32; override;
  133. function NetworkToHost(AValue: UInt32): UInt32; override;
  134. function HostToNetwork(AValue: TIdUInt64): TIdUInt64; override;
  135. function NetworkToHost(AValue: TIdUInt64): TIdUInt64; override;
  136. function RecvFrom(const ASocket: TIdStackSocketHandle; var VBuffer;
  137. const ALength, AFlags: Integer; var VIP: string; var VPort: TIdPort;
  138. var VIPVersion: TIdIPVersion): Integer; override;
  139. function ReceiveMsg(ASocket: TIdStackSocketHandle;
  140. var VBuffer: TIdBytes; APkt: TIdPacketInfo): UInt32; override;
  141. procedure WSSendTo(ASocket: TIdStackSocketHandle; const ABuffer;
  142. const ABufferLength, AFlags: Integer; const AIP: string; const APort: TIdPort;
  143. AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  144. function WSSocket(AFamily, AStruct, AProtocol: Integer;
  145. const ANonBlocking: Boolean = False): TIdStackSocketHandle; override;
  146. procedure Disconnect(ASocket: TIdStackSocketHandle); override;
  147. {$IFDEF VCL_XE3_OR_ABOVE}
  148. procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  149. AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
  150. procedure SetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  151. AOptName: TIdSocketOption; const AOptVal; const AOptLen: Integer); override;
  152. {$ENDIF}
  153. procedure SetKeepAliveValues(ASocket: TIdStackSocketHandle;
  154. const AEnabled: Boolean; const ATimeMS, AInterval: Integer); override;
  155. function SupportsIPv4: Boolean; overload; override;
  156. function SupportsIPv6: Boolean; overload; override;
  157. function CheckIPVersionSupport(const AIPVersion: TIdIPVersion): boolean; override;
  158. //In Windows, this writes a checksum into a buffer. In Linux, it would probably
  159. //simply have the kernal write the checksum with something like this (RFC 2292):
  160. //
  161. // int offset = 2;
  162. // setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset));
  163. //
  164. // Note that this should be called
  165. //IMMEDIATELY before you do a SendTo because the Local IPv6 address might change
  166. procedure WriteChecksum(s : TIdStackSocketHandle; var VBuffer : TIdBytes;
  167. const AOffset : Integer; const AIP : String; const APort : TIdPort;
  168. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  169. function IOControl(const s: TIdStackSocketHandle; const cmd: UInt32;
  170. var arg: UInt32): Integer; override;
  171. procedure GetLocalAddressList(AAddresses: TIdStackLocalAddressList); override;
  172. end;
  173. {$IFNDEF NO_REDECLARE}
  174. TLinger = record
  175. l_onoff: UInt16;
  176. l_linger: UInt16;
  177. end;
  178. {$ENDIF}
  179. TIdLinger = TLinger;
  180. implementation
  181. // On Mac M1, it seems as though DNS resolution is not cached by the system.
  182. // so we're going to cache it here.
  183. {$IFDEF DCC}
  184. {$IFDEF CPUARM64}
  185. // DCC defines MACOS for both OSX and iOS
  186. {$IFDEF MACOS}
  187. {$DEFINE USE_DNSCACHE}
  188. {$ENDIF}
  189. {$ENDIF}
  190. {$ENDIF}
  191. {$IFDEF FPC}
  192. {$IFDEF CPUAARCH64}
  193. // FPC defines DARWIN for both OSX and iOS
  194. {$IFDEF DARWIN}
  195. {$DEFINE USE_DNSCACHE}
  196. {$ENDIF}
  197. {$ENDIF}
  198. {$ENDIF}
  199. uses
  200. netdb,
  201. unix,
  202. termio,
  203. IdResourceStrings,
  204. IdResourceStringsUnix,
  205. IdException,
  206. {$IFDEF USE_DNSCACHE}IdStackDnsHostCache,{$ENDIF}
  207. SysUtils;
  208. //from: netdbh.inc, we can not include it with the I derrective and netdb.pas
  209. //does not expose it.
  210. {const
  211. EAI_SYSTEM = -(11);}
  212. const
  213. FD_SETSIZE = FD_MAXFDSET;
  214. __FD_SETSIZE = FD_MAXFDSET;
  215. {$IFDEF DARWIN}
  216. { MSG_NOSIGNAL does not exist in OS X. Instead we have SO_NOSIGPIPE, which we set in Connect. }
  217. Id_MSG_NOSIGNAL = 0;
  218. {$ELSE}
  219. Id_MSG_NOSIGNAL = MSG_NOSIGNAL;
  220. {$ENDIF}
  221. ESysEPIPE = ESysEPIPE;
  222. //helper functions for some structs
  223. {Note: These hide an API difference in structures.
  224. BSD 4.4 introduced a minor API change. sa_family was changed from a 16bit
  225. word to an 8 bit byteee and an 8 bit byte feild named sa_len was added.
  226. }
  227. procedure InitSockaddr(var VSock : Sockaddr);
  228. {$IFDEF USE_INLINE} inline; {$ENDIF}
  229. begin
  230. FillChar(VSock, SizeOf(Sockaddr), 0);
  231. VSock.sin_family := PF_INET;
  232. {$IFDEF SOCK_HAS_SINLEN}
  233. VSock.sa_len := SizeOf(Sockaddr);
  234. {$ENDIF}
  235. end;
  236. procedure InitSockAddr_in6(var VSock : SockAddr_in6);
  237. {$IFDEF USE_INLINE} inline; {$ENDIF}
  238. begin
  239. FillChar(VSock, SizeOf(SockAddr_in6), 0);
  240. {$IFDEF SOCK_HAS_SINLEN}
  241. VSock.sin6_len := SizeOf(SockAddr_in6);
  242. {$ENDIF}
  243. VSock.sin6_family := PF_INET6;
  244. end;
  245. //
  246. constructor TIdStackUnix.Create;
  247. begin
  248. inherited Create;
  249. end;
  250. destructor TIdStackUnix.Destroy;
  251. begin
  252. inherited Destroy;
  253. end;
  254. function TIdStackUnix.GetLastError : Integer;
  255. begin
  256. Result := SocketError;
  257. end;
  258. procedure TIdStackUnix.SetLastError(Const AError : Integer);
  259. begin
  260. errno := AError;
  261. end;
  262. function TIdStackUnix.Accept(ASocket: TIdStackSocketHandle;
  263. var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion): TIdStackSocketHandle;
  264. var
  265. LA : socklen_t;
  266. LAddr: sockaddr_in6;
  267. begin
  268. LA := SizeOf(LAddr);
  269. Result := fpaccept(ASocket, @LAddr, @LA);
  270. //calls prefixed by fp to avoid clashing with libc
  271. if Result <> ID_SOCKET_ERROR then begin
  272. case LAddr.sin6_family of
  273. PF_INET : begin
  274. with Psockaddr(@LAddr)^ do
  275. begin
  276. VIP := NetAddrToStr(sin_addr);
  277. VPort := ntohs(sin_port);
  278. end;
  279. VIPVersion := Id_IPv4;
  280. end;
  281. PF_INET6: begin
  282. with LAddr do
  283. begin
  284. VIP := NetAddrToStr6(sin6_addr);
  285. VPort := Ntohs(sin6_port);
  286. end;
  287. VIPVersion := Id_IPv6;
  288. end;
  289. else begin
  290. fpclose(Result);
  291. Result := Id_INVALID_SOCKET;
  292. IPVersionUnsupported;
  293. end;
  294. end;
  295. end else begin
  296. if GetLastError = ESysEBADF then begin
  297. SetLastError(ESysEINTR);
  298. end;
  299. end;
  300. end;
  301. procedure TIdStackUnix.Bind(ASocket: TIdStackSocketHandle; const AIP: string;
  302. const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  303. var
  304. LAddr: sockaddr_in6;
  305. begin
  306. case AIPVersion of
  307. Id_IPv4: begin
  308. InitSockAddr(Psockaddr(@LAddr)^);
  309. with Psockaddr(@LAddr)^ do
  310. begin
  311. if AIP <> '' then begin
  312. sin_addr := StrToNetAddr(AIP);
  313. //TranslateStringToTInAddr(AIP, sin_addr, Id_IPv4);
  314. end;
  315. sin_port := htons(APort);
  316. end;
  317. CheckForSocketError(fpBind(ASocket, Psockaddr(@LAddr), SizeOf(sockaddr)));
  318. end;
  319. Id_IPv6: begin
  320. InitSockAddr_in6(LAddr);
  321. with LAddr do
  322. begin
  323. if AIP <> '' then begin
  324. sin6_addr := StrToNetAddr6(AIP);
  325. //TranslateStringToTInAddr(AIP, sin6_addr, Id_IPv6);
  326. end;
  327. sin6_port := htons(APort);
  328. end;
  329. CheckForSocketError(fpBind(ASocket, Psockaddr(@LAddr), SizeOf(Sockaddr_in6)));
  330. end;
  331. else begin
  332. IPVersionUnsupported;
  333. end;
  334. end;
  335. end;
  336. function TIdStackUnix.WSCloseSocket(ASocket: TIdStackSocketHandle): Integer;
  337. begin
  338. Result := fpclose(ASocket);
  339. end;
  340. procedure TIdStackUnix.Connect(const ASocket: TIdStackSocketHandle;
  341. const AIP: string; const APort: TIdPort;
  342. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  343. var
  344. LAddr: sockaddr_in6;
  345. begin
  346. case AIPVersion of
  347. Id_IPv4: begin
  348. InitSockAddr(Psockaddr(@LAddr)^);
  349. with Psockaddr(@LAddr)^ do
  350. begin
  351. sin_addr := StrToNetAddr(AIP);
  352. //TranslateStringToTInAddr(AIP, sin_addr, Id_IPv4);
  353. sin_port := htons(APort);
  354. end;
  355. CheckForSocketError(fpConnect(ASocket, Psockaddr(@LAddr), SizeOf(sockaddr)));
  356. end;
  357. Id_IPv6: begin
  358. InitSockAddr_in6(LAddr);
  359. with LAddr do
  360. begin
  361. sin6_addr := StrToNetAddr6(AIP);
  362. //TranslateStringToTInAddr(AIP, LAddr6.sin6_addr, Id_IPv6);
  363. sin6_port := htons(APort);
  364. end;
  365. CheckForSocketError(fpConnect(ASocket, Psockaddr(@LAddr), SizeOf(sockaddr_in6)));
  366. end;
  367. else begin
  368. IPVersionUnsupported;
  369. end;
  370. end;
  371. {$IFDEF DARWIN} // TODO: use HAS_SOCKET_NOSIGPIPE instead...
  372. SetSocketOption(ASocket, Id_SOL_SOCKET, SO_NOSIGPIPE, 1);
  373. {$ENDIF}
  374. end;
  375. function TIdStackUnix.HostByName(const AHostName: string;
  376. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
  377. var
  378. LI4 : array of THostAddr;
  379. LI6 : array of THostAddr6;
  380. LH4 : THostEntry;
  381. LRetVal : Integer;
  382. begin
  383. {$IFDEF USE_DNSCACHE}
  384. Result := GetCachedDnsHostAddress(AHostName, AIPVersion);
  385. if Result <> '' then Exit;
  386. {$ENDIF}
  387. case AIPVersion of
  388. Id_IPv4 :
  389. begin
  390. if GetHostByName(AHostName, LH4) then
  391. begin
  392. Result := HostAddrToStr(LH4.Addr);
  393. end else
  394. begin
  395. SetLength(LI4, 10);
  396. LRetVal := ResolveName(AHostName, LI4);
  397. if LRetVal < 1 then begin
  398. raise EIdResolveError.CreateFmt(RSResolveError, [AHostName, 'Error', LRetVal]); {do not localize}
  399. end;
  400. Result := NetAddrToStr(LI4[0]);
  401. end;
  402. end;
  403. Id_IPv6 :
  404. begin
  405. SetLength(LI6, 10);
  406. LRetVal := ResolveName6(AHostName, LI6);
  407. if LRetVal < 1 then begin
  408. raise EIdResolveError.CreateFmt(RSResolveError, [AHostName, LRetVal]);
  409. end;
  410. Result := NetAddrToStr6(LI6[0]);
  411. end;
  412. else
  413. Result := '';
  414. end;
  415. {$IFDEF USE_DNSCACHE}
  416. if Result <> '' then begin
  417. AddHostAddressToDnsCache(AHostName, AIPVersion, Result);
  418. end;
  419. {$ENDIF}
  420. end;
  421. function TIdStackUnix.ReadHostName: string;
  422. begin
  423. Result := GetHostName;
  424. end;
  425. procedure TIdStackUnix.Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer);
  426. begin
  427. CheckForSocketError(fpListen(ASocket, ABacklog));
  428. end;
  429. function TIdStackUnix.WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
  430. const ABufferLength, AFlags: Integer): Integer;
  431. begin
  432. //IdStackWindows is just: Result := Recv(ASocket, ABuffer, ABufferLength, AFlags);
  433. Result := fpRecv(ASocket, @ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
  434. end;
  435. function TIdStackUnix.RecvFrom(const ASocket: TIdStackSocketHandle; var VBuffer;
  436. const ALength, AFlags: Integer; var VIP: string; var VPort: TIdPort;
  437. var VIPVersion: TIdIPVersion): Integer;
  438. var
  439. LiSize: tsocklen;
  440. LAddr: sockaddr_in6;
  441. begin
  442. LiSize := SizeOf(sockaddr_in6);
  443. Result := fpRecvFrom(ASocket, @VBuffer, ALength, AFlags or Id_MSG_NOSIGNAL, Psockaddr(@LAddr), @LiSize);
  444. if Result >= 0 then
  445. begin
  446. case LAddr.sin6_family of
  447. Id_PF_INET4 :
  448. begin
  449. with Psockaddr(@LAddr)^ do
  450. begin
  451. VIP := NetAddrToStr(sin_addr);
  452. VPort := ntohs(sin_port);
  453. end;
  454. end;
  455. Id_PF_INET6:
  456. begin
  457. with LAddr do
  458. begin
  459. VIP := NetAddrToStr6(sin6_addr);
  460. VPort := ntohs(sin6_port);
  461. end;
  462. VIPVersion := Id_IPV6;
  463. end;
  464. end;
  465. end;
  466. end;
  467. function TIdStackUnix.ReceiveMsg(ASocket: TIdStackSocketHandle;
  468. var VBuffer: TIdBytes; APkt: TIdPacketInfo): UInt32;
  469. var
  470. LIP : String;
  471. LPort : TIdPort;
  472. LIPVersion : TIdIPVersion;
  473. begin
  474. Result := RecvFrom(ASocket, VBuffer, Length(VBuffer), 0, LIP, LPort, LIPVersion);
  475. APkt.Reset;
  476. APkt.SourceIP := LIP;
  477. APkt.SourcePort := LPort;
  478. APkt.SourceIPVersion := LIPVersion;
  479. APkt.DestIPVersion := LIPVersion;
  480. SetLength(VBuffer, Result);
  481. end;
  482. {The stuff below is commented out until I figure out what to do}
  483. {var
  484. LIP : String;
  485. LPort : TIdPort;
  486. LSize: UInt32;
  487. LAddr: SockAddr_In6;
  488. LMsg : msghdr;
  489. LMsgBuf : BUF;
  490. LControl : TIdBytes;
  491. LCurCmsg : CMSGHDR; //for iterating through the control buffer
  492. LCurPt : Pin_pktinfo;
  493. LCurPt6 : Pin6_pktinfo;
  494. LByte : PByte;
  495. LDummy, LDummy2 : UInt32;
  496. begin
  497. //we call the macro twice because we specified two possible structures.
  498. //Id_IPV6_HOPLIMIT and Id_IPV6_PKTINFO
  499. LSize := CMSG_SPACE(SizeOf(Byte)) + CMSG_SPACE(SizeOf(in6_pktinfo));
  500. SetLength(LControl, LSize);
  501. LMsgBuf.len := Length(VBuffer); // Length(VMsgData);
  502. LMsgBuf.buf := @VBuffer[0]; // @VMsgData[0];
  503. FillChar(LMsg,SizeOf(LMsg),0);
  504. LMsg.lpBuffers := @LMsgBuf;
  505. LMsg.dwBufferCount := 1;
  506. LMsg.Control.Len := LSize;
  507. LMsg.Control.buf := @LControl[0];
  508. LMsg.name := PSOCKADDR(@LAddr);
  509. LMsg.namelen := SizeOf(LAddr);
  510. CheckForSocketError( RecvMsg(ASocket,@LMsg,Result,@LDummy,LPwsaoverlapped_COMPLETION_ROUTINE(@LDummy2)));
  511. case LAddr.sin6_family of
  512. Id_PF_INET4: begin
  513. with PSOCKADDR(@LAddr)^do
  514. begin
  515. APkt.SourceIP := TranslateTInAddrToString(sin_addr,Id_IPv4);
  516. APkt.SourcePort := NToHs(sin_port);
  517. end;
  518. APkt.SourceIPVersion := Id_IPv4;
  519. end;
  520. Id_PF_INET6: begin
  521. with LAddr do
  522. begin
  523. APkt.SourceIP := TranslateTInAddrToString(sin6_addr, Id_IPv6);
  524. APkt.SourcePort := NToHs(sin6_port);
  525. end;
  526. APkt.SourceIPVersion := Id_IPv6;
  527. end;
  528. else begin
  529. Result := 0; // avoid warning
  530. IPVersionUnsupported;
  531. end;
  532. end;
  533. LCurCmsg := nil;
  534. repeat
  535. LCurCmsg := CMSG_NXTHDR(@LMsg,LCurCmsg);
  536. if LCurCmsg = nil then
  537. begin
  538. break;
  539. end;
  540. case LCurCmsg^.cmsg_type of
  541. IP_PKTINFO : //done this way because IPV6_PKTINF and IP_PKTINFO
  542. //are both 19
  543. begin
  544. case LAddr.sin6_family of
  545. Id_PF_INET4:
  546. begin
  547. LCurPt := WSA_CMSG_DATA(LCurCmsg);
  548. APkt.DestIP := GWindowsStack.TranslateTInAddrToString(LCurPt^.ipi_addr,Id_IPv4);
  549. APkt.DestIF := LCurPt^.ipi_ifindex;
  550. APkt.DestIPVersion := Id_IPv4;
  551. end;
  552. Id_PF_INET6:
  553. begin
  554. LCurPt6 := WSA_CMSG_DATA(LCurCmsg);
  555. APkt.DestIP := GWindowsStack.TranslateTInAddrToString(LCurPt6^.ipi6_addr,Id_IPv6);
  556. APkt.DestIF := LCurPt6^.ipi6_ifindex;
  557. APkt.DestIPVersion := Id_IPv6;
  558. end;
  559. end;
  560. end;
  561. Id_IPV6_HOPLIMIT :
  562. begin
  563. LByte := PByte(WSA_CMSG_DATA(LCurCmsg));
  564. APkt.TTL := LByte^;
  565. end;
  566. end;
  567. until False; }
  568. function TIdStackUnix.WSSend(ASocket: TIdStackSocketHandle;
  569. const ABuffer; const ABufferLength, AFlags: Integer): Integer;
  570. begin
  571. //CC: Should Id_MSG_NOSIGNAL be included?
  572. // Result := Send(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
  573. Result := CheckForSocketError(fpsend(ASocket, @ABuffer, ABufferLength, AFlags));
  574. end;
  575. procedure TIdStackUnix.WSSendTo(ASocket: TIdStackSocketHandle;
  576. const ABuffer; const ABufferLength, AFlags: Integer; const AIP: string;
  577. const APort: TIdPort; AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  578. var
  579. LAddr : sockaddr_in6;
  580. LBytesOut: Integer;
  581. begin
  582. case AIPVersion of
  583. Id_IPv4 :
  584. begin
  585. InitSockAddr(Psockaddr(@LAddr)^);
  586. with Psockaddr(@LAddr)^ do
  587. begin
  588. sin_addr := StrToNetAddr(AIP);
  589. sin_port := htons(APort);
  590. end;
  591. LBytesOut := fpSendTo(ASocket, @ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL, Psockaddr(@LAddr), SizeOf(sockaddr));
  592. end;
  593. Id_IPv6:
  594. begin
  595. InitSockAddr_in6(LAddr);
  596. with LAddr do
  597. begin
  598. sin6_addr := StrToHostAddr6(AIP);
  599. //TranslateStringToTInAddr(AIP, sin6_addr, AIPVersion);
  600. sin6_port := htons(APort);
  601. end;
  602. LBytesOut := fpSendTo(ASocket, @ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL, Psockaddr(@LAddr), SizeOf(sockaddr_in6));
  603. end;
  604. else begin
  605. LBytesOut := 0; // avoid warning
  606. IPVersionUnsupported;
  607. end;
  608. end;
  609. if LBytesOut = -1 then begin
  610. // TODO: move this into RaiseLastSocketError() directly
  611. if WSGetLastError() = Id_WSAEMSGSIZE then begin
  612. raise EIdPackageSizeTooBig.Create(RSPackageSizeTooBig);
  613. end else begin
  614. RaiseLastSocketError;
  615. end;
  616. end else if LBytesOut <> ABufferLength then begin
  617. raise EIdNotAllBytesSent.Create(RSNotAllBytesSent);
  618. end;
  619. end;
  620. procedure TIdStackUnix.{$IFDEF VCL_XE3_OR_ABOVE}GetSocketOption{$ELSE}WSGetSocketOption{$ENDIF}
  621. (ASocket: TIdStackSocketHandle; ALevel: TIdSocketProtocol; AOptName: TIdSocketOption;
  622. var AOptVal; var AOptLen: Integer);
  623. var
  624. LLen : TSockLen;
  625. begin
  626. LLen := AOptLen;
  627. CheckForSocketError(fpGetSockOpt(ASocket, ALevel, AOptName, PAnsiChar(@AOptVal), @LLen));
  628. AOptLen := LLen;
  629. end;
  630. procedure TIdStackUnix.{$IFDEF VCL_XE3_OR_ABOVE}SetSocketOption{$ELSE}WSSetSocketOption{$ENDIF}
  631. (ASocket: TIdStackSocketHandle; ALevel: TIdSocketProtocol; AOptName: TIdSocketOption;
  632. const AOptVal; const AOptLen: Integer);
  633. begin
  634. CheckForSocketError(fpSetSockOpt(ASocket, ALevel, AOptName, PAnsiChar(@AOptVal), AOptLen));
  635. end;
  636. function TIdStackUnix.WSGetLastError: Integer;
  637. begin
  638. //IdStackWindows just uses result := WSAGetLastError;
  639. Result := GetLastError; //System.GetLastOSError; - FPC doesn't define it in System
  640. if Result = ESysEPIPE then begin
  641. Result := Id_WSAECONNRESET;
  642. end;
  643. end;
  644. procedure TIdStackUnix.WSSetLastError(const AErr : Integer);
  645. begin
  646. SetLastError(AErr);
  647. end;
  648. function TIdStackUnix.WSSocket(AFamily, AStruct, AProtocol: Integer;
  649. const ANonBlocking: Boolean = False): TIdStackSocketHandle;
  650. var
  651. LValue: UInt32;
  652. begin
  653. Result := fpsocket(AFamily, AStruct, AProtocol);
  654. if Result <> Id_INVALID_SOCKET then begin
  655. //SetBlocking(Result, not ANonBlocking);
  656. LValue := UInt32(ANonBlocking);
  657. fpioctl(Result, FIONBIO, @LValue);
  658. end;
  659. end;
  660. function TIdStackUnix.WSGetServByName(const AServiceName: string): TIdPort;
  661. var
  662. LS : TServiceEntry;
  663. begin
  664. if GetServiceByName(AServiceName, '', LS) then begin
  665. Result := LS.Port;
  666. end else begin
  667. raise EIdInvalidServiceName.CreateFmt(RSInvalidServiceName, [AServiceName]);
  668. end;
  669. end;
  670. function TIdStackUnix.HostToNetwork(AValue: UInt16): UInt16;
  671. begin
  672. Result := htons(AValue);
  673. end;
  674. function TIdStackUnix.NetworkToHost(AValue: UInt16): UInt16;
  675. begin
  676. Result := ntohs(AValue);
  677. end;
  678. function TIdStackUnix.HostToNetwork(AValue: UInt32): UInt32;
  679. begin
  680. {$i IdRangeCheckingOff.inc} // disable range checking
  681. Result := htonl(AValue);
  682. {$i IdRangeCheckingOn.inc} // Restore range checking
  683. end;
  684. function TIdStackUnix.NetworkToHost(AValue: UInt32): UInt32;
  685. begin
  686. {$i IdRangeCheckingOff.inc} // disable range checking
  687. Result := ntohl(AValue);
  688. // Restore range checking
  689. {$i IdRangeCheckingOn.inc} // Restore range checking
  690. end;
  691. { RP - I'm not sure what endian Linux natively uses, thus the
  692. check to see if the bytes need swapping or not ... }
  693. function TIdStackUnix.HostToNetwork(AValue: TIdUInt64): TIdUInt64;
  694. var
  695. LParts: TIdUInt64Parts;
  696. L: UInt32;
  697. begin
  698. {$i IdRangeCheckingOff.inc} // disable range checking
  699. if (htonl(1) <> 1) then begin
  700. LParts.QuadPart := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  701. L := htonl(LParts.HighPart);
  702. LParts.HighPart := htonl(LParts.LowPart);
  703. LParts.LowPart := L;
  704. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := LParts.QuadPart;
  705. end else begin
  706. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  707. end;
  708. {$i IdRangeCheckingOn.inc} // Restore range checking
  709. end;
  710. function TIdStackUnix.NetworkToHost(AValue: TIdUInt64): TIdUInt64;
  711. var
  712. LParts: TIdUInt64Parts;
  713. L: UInt32;
  714. begin
  715. {$i IdRangeCheckingOff.inc} // disable range checking
  716. if (ntohl(1) <> 1) then begin
  717. LParts.QuadPart := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  718. L := ntohl(LParts.HighPart);
  719. LParts.HighPart := NetworkToHost(LParts.LowPart);
  720. LParts.LowPart := L;
  721. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := LParts.QuadPart;
  722. end else begin
  723. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  724. end;
  725. {$i IdRangeCheckingOn.inc} // Restore range checking
  726. end;
  727. {$IFDEF HAS_getifaddrs}
  728. // TODO: does FreePascal already define these anywhere?
  729. type
  730. pifaddrs = ^ifaddrs;
  731. ifaddrs = record
  732. ifa_next: pifaddrs; { Pointer to next struct }
  733. ifa_name: PIdAnsiChar; { Interface name }
  734. ifa_flags: Cardinal; { Interface flags }
  735. ifa_addr: psockaddr; { Interface address }
  736. ifa_netmask: psockaddr; { Interface netmask }
  737. ifa_broadaddr: psockaddr; { Interface broadcast address }
  738. ifa_dstaddr: psockaddr; { P2P interface destination }
  739. ifa_data: Pointer; { Address specific data }
  740. end;
  741. const
  742. IFF_LOOPBACK = $8;
  743. function getifaddrs(var ifap: pifaddrs): Integer; cdecl; external 'libc.so' name 'getifaddrs'; {do not localize}
  744. procedure freeifaddrs(ifap: pifaddrs); cdecl; external 'libc.so' name 'freeifaddrs'; {do not localize}
  745. {$IFDEF HAS_if_nametoindex}
  746. procedure if_nametoindex(const ifname: PIdAnsiChar): UInt32; cdecl; external 'libc.so' name 'if_nametoindex'; {do not localize}
  747. {$ENDIF}
  748. type
  749. TIdStackLocalAddressAccess = class(TIdStackLocalAddress)
  750. end;
  751. {$ENDIF}
  752. procedure TIdStackUnix.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
  753. var
  754. {$IFDEF HAS_getifaddrs}
  755. LAddrList, LAddrInfo: pifaddrs;
  756. LSubNetStr: String;
  757. LAddress: TIdStackLocalAddress;
  758. LName: string;
  759. {$ELSE}
  760. LI4 : array of THostAddr;
  761. LI6 : array of THostAddr6;
  762. i : Integer;
  763. LHostName : String;
  764. {$ENDIF}
  765. begin
  766. // TODO: Using gethostname() and ResolveName() like this may not always
  767. // return just the machine's IP addresses. Technically speaking, they will
  768. // return the local hostname, and then return the address(es) to which that
  769. // hostname resolves. It is possible for a machine to (a) be configured such
  770. // that its name does not resolve to an IP, or (b) be configured such that
  771. // its name resolves to multiple IPs, only one of which belongs to the local
  772. // machine. For better results, we should use getifaddrs() on platforms that
  773. // support it...
  774. {$IFDEF HAS_getifaddrs}
  775. if getifaddrs(LAddrList) = 0 then // TODO: raise an exception if it fails
  776. try
  777. AAddresses.BeginUpdate;
  778. try
  779. LAddrInfo := LAddrList;
  780. repeat
  781. if (LAddrInfo^.ifa_addr <> nil) and ((LAddrInfo^.ifa_flags and IFF_LOOPBACK) = 0) then
  782. begin
  783. LAddress := nil;
  784. case LAddrInfo^.ifa_addr^.sa_family of
  785. Id_PF_INET4: begin
  786. if LAddrInfo^.ifa_netmask <> nil then begin
  787. LSubNetStr := TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ifa_netmask)^.sin_addr, Id_IPv4);
  788. end else begin
  789. LSubNetStr := '';
  790. end;
  791. LAddress := TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ifa_addr)^.sin_addr, Id_IPv4), LSubNetStr);
  792. end;
  793. Id_PF_INET6: begin
  794. LAddress := TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In6(LAddrInfo^.ifa_addr)^.sin6_addr, Id_IPv6));
  795. end;
  796. end;
  797. if LAddress <> nil then begin
  798. LName := String(LAddrInfo^.ifa_name);
  799. TIdStackLocalAddressAccess(LAddress).FDescription := LName;
  800. TIdStackLocalAddressAccess(LAddress).FFriendlyName := LName;
  801. TIdStackLocalAddressAccess(LAddress).FInterfaceName := LName;
  802. {$IFDEF HAS_if_nametoindex}
  803. TIdStackLocalAddressAccess(LAddress).FInterfaceIndex := if_nametoindex(LAddrInfo^.ifa_name);
  804. {$ENDIF}
  805. end;
  806. end;
  807. LAddrInfo := LAddrInfo^.ifa_next;
  808. until LAddrInfo = nil;
  809. finally
  810. AAddresses.EndUpdate;
  811. end;
  812. finally
  813. freeifaddrs(LAddrList);
  814. end;
  815. {$ELSE}
  816. LHostName := GetHostName;
  817. if LHostName = '' then begin
  818. RaiseLastSocketError;
  819. end;
  820. AAddresses.BeginUpdate;
  821. try
  822. if ResolveName(LHostName, LI4) = 0 then
  823. begin
  824. for i := Low(LI4) to High(LI4) do
  825. begin
  826. TIdStackLocalAddressIPv4.Create(AAddresses, NetAddrToStr(LI4[i]), ''); // TODO: SubNet
  827. end;
  828. end;
  829. if ResolveName6(LHostName, LI6) = 0 then
  830. begin
  831. for i := Low(LI6) to High(LI6) do
  832. begin
  833. TIdStackLocalAddressIPv6.Create(AAddresses, NetAddrToStr6(LI6[i]));
  834. end;
  835. end;
  836. finally
  837. AAddresses.EndUpdate;
  838. end;
  839. {$ENDIF}
  840. end;
  841. function TIdStackUnix.HostByAddress(const AAddress: string;
  842. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
  843. var
  844. LI : Array of string;
  845. LAddr4: THostAddr;
  846. LAddr6: THostAddr6;
  847. begin
  848. Result := '';
  849. case AIPVersion of
  850. Id_IPv4 :
  851. begin
  852. LAddr4 := StrToNetAddr(AAddress);
  853. if ResolveAddress(LAddr4, LI) = 0 then begin
  854. Result := LI[0];
  855. end;
  856. end;
  857. Id_IPv6 :
  858. begin
  859. LAddr6 := StrToNetAddr6(AAddress);
  860. if ResolveAddress6(LAddr6, LI) = 0 then begin
  861. Result := LI[0];
  862. end;
  863. end;
  864. end;
  865. end;
  866. function TIdStackUnix.WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer;
  867. begin
  868. Result := fpShutdown(ASocket, AHow);
  869. end;
  870. procedure TIdStackUnix.Disconnect(ASocket: TIdStackSocketHandle);
  871. begin
  872. // Windows uses Id_SD_Send, Linux should use Id_SD_Both
  873. WSShutdown(ASocket, Id_SD_Both);
  874. // SO_LINGER is false - socket may take a little while to actually close after this
  875. WSCloseSocket(ASocket);
  876. end;
  877. procedure TIdStackUnix.GetPeerName(ASocket: TIdStackSocketHandle; var VIP: string;
  878. var VPort: TIdPort; var VIPVersion: TIdIPVersion);
  879. var
  880. i: tsocklen;
  881. LAddr: sockaddr_in6;
  882. begin
  883. i := SizeOf(LAddr);
  884. CheckForSocketError(fpGetPeerName(ASocket, @LAddr, @i));
  885. case LAddr.sin6_family of
  886. PF_INET: begin
  887. with Psockaddr(@LAddr)^ do
  888. begin
  889. VIP := NetAddrToStr(sin_addr);
  890. VPort := ntohs(sin_port);
  891. end;
  892. VIPVersion := Id_IPv4;
  893. end;
  894. PF_INET6: begin
  895. with LAddr do
  896. begin
  897. VIP := NetAddrToStr6(sin6_addr);
  898. VPort := ntohs(sin6_port);
  899. end;
  900. VIPVersion := Id_IPv6;
  901. end;
  902. else begin
  903. IPVersionUnsupported;
  904. end;
  905. end;
  906. end;
  907. procedure TIdStackUnix.GetSocketName(ASocket: TIdStackSocketHandle;
  908. var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
  909. var
  910. i: tsocklen;
  911. LAddr: sockaddr_in6;
  912. begin
  913. i := SizeOf(LAddr);
  914. CheckForSocketError(fpGetSockName(ASocket, @LAddr, @i));
  915. case LAddr.sin6_family of
  916. PF_INET : begin
  917. with Psockaddr(@LAddr)^ do
  918. begin
  919. VIP := NetAddrToStr(sin_addr);
  920. VPort := ntohs(sin_port);
  921. end;
  922. VIPVersion := Id_IPV4;
  923. end;
  924. PF_INET6: begin
  925. with LAddr do
  926. begin
  927. VIP := NetAddrToStr6(sin6_addr);
  928. VPort := ntohs(sin6_port);
  929. end;
  930. VIPVersion := Id_IPv6;
  931. end;
  932. else begin
  933. IPVersionUnsupported;
  934. end;
  935. end;
  936. end;
  937. procedure TIdStackUnix.AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings);
  938. var
  939. LS : TServiceEntry;
  940. begin
  941. if GetServiceByPort(APortNumber, '', LS) then begin
  942. AAddresses.Add(LS.Name);
  943. end;
  944. end;
  945. function TIdStackUnix.WSTranslateSocketErrorMsg(const AErr: Integer): string;
  946. begin
  947. //we override this function for the herr constants that
  948. //are returned by the DNS functions
  949. //note that this is not really applicable because we are using some
  950. //FPC functions that do direct DNS lookups without the standard Unix
  951. //DNS functions. It sounds odd but I think there's a good reason for it.
  952. Result := inherited WSTranslateSocketErrorMsg(AErr);
  953. end;
  954. procedure TIdStackUnix.SetBlocking(ASocket: TIdStackSocketHandle;
  955. const ABlocking: Boolean);
  956. var
  957. LValue: UInt32;
  958. begin
  959. LValue := UInt32(not ABlocking);
  960. CheckForSocketError(fpioctl(ASocket, FIONBIO, @LValue));
  961. end;
  962. function TIdStackUnix.WouldBlock(const AResult: Integer): Boolean;
  963. begin
  964. // using if-else instead of in..range because EAGAIN and EWOULDBLOCK
  965. // have often the same value and so FPC might report a range error
  966. Result := (AResult = Id_WSAEAGAIN) or
  967. (AResult = Id_WSAEWOULDBLOCK) or
  968. (AResult = Id_WSAEINPROGRESS);
  969. end;
  970. function TIdStackUnix.SupportsIPv4: Boolean;
  971. //In Windows, this does something else. It checks the LSP's installed.
  972. begin
  973. Result := CheckIPVersionSupport(Id_IPv4);
  974. end;
  975. function TIdStackUnix.SupportsIPv6: Boolean;
  976. //In Windows, this does something else. It checks the LSP's installed.
  977. begin
  978. Result := CheckIPVersionSupport(Id_IPv6);
  979. end;
  980. function TIdStackUnix.CheckIPVersionSupport(const AIPVersion: TIdIPVersion): Boolean;
  981. var
  982. LTmpSocket: TIdStackSocketHandle;
  983. begin
  984. // TODO: on nix systems (or maybe just Linux?), an alternative would be to
  985. // check for the existance of the '/proc/net/if_inet6' kernel pseudo-file
  986. LTmpSocket := WSSocket(IdIPFamily[AIPVersion], Integer(Id_SOCK_STREAM), Id_IPPROTO_IP);
  987. Result := LTmpSocket <> Id_INVALID_SOCKET;
  988. if Result then begin
  989. WSCloseSocket(LTmpSocket);
  990. end;
  991. end;
  992. procedure TIdStackUnix.WriteChecksum(s: TIdStackSocketHandle;
  993. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  994. const APort: TIdPort; const AIPVersion: TIdIPVersion);
  995. begin
  996. case AIPVersion of
  997. Id_IPv4 : CopyTIdUInt16(HostToLittleEndian(CalcCheckSum(VBuffer)), VBuffer, AOffset);
  998. Id_IPv6 : WriteChecksumIPv6(s, VBuffer, AOffset, AIP, APort);
  999. else
  1000. IPVersionUnsupported;
  1001. end;
  1002. end;
  1003. procedure TIdStackUnix.WriteChecksumIPv6(s: TIdStackSocketHandle;
  1004. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  1005. const APort: TIdPort);
  1006. begin
  1007. //we simply request that the kernal write the checksum when the data
  1008. //is sent. All of the parameters required are because Windows is bonked
  1009. //because it doesn't have the IPV6CHECKSUM socket option meaning we have
  1010. //to querry the network interface in TIdStackWindows -- yuck!!
  1011. SetSocketOption(s, Id_IPPROTO_IPV6, IPV6_CHECKSUM, AOffset);
  1012. end;
  1013. function TIdStackUnix.IOControl(const s: TIdStackSocketHandle; const cmd: UInt32;
  1014. var arg: UInt32): Integer;
  1015. begin
  1016. Result := fpioctl(s, cmd, @arg);
  1017. end;
  1018. (*
  1019. Why did I remove this again?
  1020. 1) it sends SIGPIPE even if the socket is created with the no-sigpipe bit set
  1021. that could be solved by blocking sigpipe within this thread
  1022. This is probably a bug in the Linux kernel, but we could work around it
  1023. by blocking that signal for the time of sending the file (just get the
  1024. sigprocmask, see if pipe bit is set, if not set it and remove again after
  1025. sending the file)
  1026. But the more serious reason is another one, which exists in Windows too:
  1027. 2) I think that ServeFile is misdesigned:
  1028. ServeFile does not raise an exception if it didn't send all the bytes.
  1029. Now what happens if I have OnExecute assigned like this
  1030. AThread.Connection.ServeFile('...', True); // <-- true to send via kernel
  1031. is that it will return 0, but notice that in this case I didn't ask for the
  1032. result. Net effect is that the thread will loop in OnExecute even if the
  1033. socket is long gone. This doesn't fit Indy semantics at all, exceptions are
  1034. always raised if the remote end disconnects. Even if I would do
  1035. AThread.Connection.ServeFile('...', False);
  1036. then it would raise an exception.
  1037. I think this is a big flaw in the design of the ServeFile function.
  1038. Maybe GServeFile should only return the bytes sent, but then
  1039. TCPConnection.ServeFile() should raise an exception if GServeFile didn't
  1040. send all the bytes.
  1041. JM Berg, 2002-09-09
  1042. function ServeFile(ASocket: TIdStackSocketHandle; AFileName: string): UInt32;
  1043. var
  1044. LFileHandle: integer;
  1045. offset: integer;
  1046. stat: _stat;
  1047. begin
  1048. LFileHandle := open(PChar(AFileName), O_RDONLY);
  1049. try
  1050. offset := 0;
  1051. fstat(LFileHandle, stat);
  1052. Result := sendfile(ASocket, LFileHandle, offset, stat.st_size);
  1053. //** if Result = UInt32(-1) then RaiseLastOSError;
  1054. finally libc.__close(LFileHandle); end;
  1055. end;
  1056. *)
  1057. procedure TIdStackUnix.SetKeepAliveValues(ASocket: TIdStackSocketHandle;
  1058. const AEnabled: Boolean; const ATimeMS, AInterval: Integer);
  1059. begin
  1060. if AEnabled then begin
  1061. {$IFDEF HAS_TCP_KEEPIDLE}
  1062. SetSocketOption(ASocket, Id_SOL_TCP, Id_TCP_KEEPIDLE, ATimeMS div MSecsPerSec);
  1063. {$ENDIF}
  1064. {$IFDEF HAS_TCP_KEEPINTVL}
  1065. SetSocketOption(ASocket, Id_SOL_TCP, Id_TCP_KEEPINTVL, AInterval div MSecsPerSec);
  1066. {$ENDIF}
  1067. end;
  1068. inherited;
  1069. end;
  1070. { TIdSocketListUnix }
  1071. procedure TIdSocketListUnix.Add(AHandle: TIdStackSocketHandle);
  1072. begin
  1073. Lock;
  1074. try
  1075. if fpFD_ISSET(AHandle, FFDSet) = 0 then begin
  1076. if Count >= FD_SETSIZE then begin
  1077. raise EIdStackSetSizeExceeded.Create(RSSetSizeExceeded);
  1078. end;
  1079. fpFD_SET(AHandle, FFDSet);
  1080. Inc(FCount);
  1081. end;
  1082. finally
  1083. Unlock;
  1084. end;
  1085. end;//
  1086. procedure TIdSocketListUnix.Clear;
  1087. begin
  1088. Lock;
  1089. try
  1090. fpFD_ZERO(FFDSet);
  1091. FCount := 0;
  1092. finally
  1093. Unlock;
  1094. end;
  1095. end;
  1096. function TIdSocketListUnix.ContainsSocket(AHandle: TIdStackSocketHandle): Boolean;
  1097. begin
  1098. Lock;
  1099. try
  1100. Result := fpFD_ISSET(AHandle, FFDSet) > 0;
  1101. finally
  1102. Unlock;
  1103. end;
  1104. end;
  1105. function TIdSocketListUnix.Count: Integer;
  1106. begin
  1107. Lock;
  1108. try
  1109. Result := FCount;
  1110. finally
  1111. Unlock;
  1112. end;
  1113. end;//
  1114. class function TIdSocketListUnix.FDSelect(AReadSet, AWriteSet, AExceptSet: PFDSet;
  1115. const ATimeout: Integer): Integer;
  1116. var
  1117. LTime: TTimeVal;
  1118. LTimePtr: PTimeVal;
  1119. begin
  1120. if ATimeout = IdTimeoutInfinite then begin
  1121. LTimePtr := nil;
  1122. end else begin
  1123. LTime.tv_sec := ATimeout div 1000;
  1124. LTime.tv_usec := (ATimeout mod 1000) * 1000;
  1125. LTimePtr := @LTime;
  1126. end;
  1127. // TODO: calculate the actual nfds value based on the Sets provided...
  1128. Result := fpSelect(FD_SETSIZE, AReadSet, AWriteSet, AExceptSet, LTimePtr);
  1129. end;
  1130. procedure TIdSocketListUnix.GetFDSet(var VSet: TFDSet);
  1131. begin
  1132. Lock;
  1133. try
  1134. VSet := FFDSet;
  1135. finally
  1136. Unlock;
  1137. end;
  1138. end;
  1139. function TIdSocketListUnix.GetItem(AIndex: Integer): TIdStackSocketHandle;
  1140. var
  1141. LIndex, i: Integer;
  1142. begin
  1143. Result := 0;
  1144. // TODO: is this missing Lock/Unlock calls?
  1145. LIndex := 0;
  1146. //? use FMaxHandle div x
  1147. for i:= 0 to __FD_SETSIZE - 1 do begin
  1148. if fpFD_ISSET(i, FFDSet) = 1 then begin
  1149. if LIndex = AIndex then begin
  1150. Result := i;
  1151. Break;
  1152. end;
  1153. Inc(LIndex);
  1154. end;
  1155. end;
  1156. end;
  1157. procedure TIdSocketListUnix.Remove(AHandle: TIdStackSocketHandle);
  1158. begin
  1159. Lock;
  1160. try
  1161. if fpFD_ISSET(AHandle, FFDSet) = 1 then
  1162. begin
  1163. Dec(FCount);
  1164. fpFD_CLR(AHandle, FFDSet);
  1165. end;
  1166. finally
  1167. Unlock;
  1168. end;
  1169. end;//
  1170. procedure TIdSocketListUnix.SetFDSet(var VSet: TFDSet);
  1171. begin
  1172. Lock;
  1173. try
  1174. FFDSet := VSet;
  1175. finally
  1176. Unlock;
  1177. end;
  1178. end;
  1179. class function TIdSocketListUnix.Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
  1180. AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1181. var
  1182. LReadSet: TFDSet;
  1183. LWriteSet: TFDSet;
  1184. LExceptSet: TFDSet;
  1185. LPReadSet: PFDSet;
  1186. LPWriteSet: PFDSet;
  1187. LPExceptSet: PFDSet;
  1188. procedure ReadSet(AList: TIdSocketList; var ASet: TFDSet; var APSet: PFDSet);
  1189. begin
  1190. if AList <> nil then begin
  1191. TIdSocketListUnix(AList).GetFDSet(ASet);
  1192. APSet := @ASet;
  1193. end else begin
  1194. APSet := nil;
  1195. end;
  1196. end;
  1197. begin
  1198. ReadSet(AReadList, LReadSet, LPReadSet);
  1199. ReadSet(AWriteList, LWriteSet, LPWriteSet);
  1200. ReadSet(AExceptList, LExceptSet, LPExceptSet);
  1201. //
  1202. Result := FDSelect(LPReadSet, LPWriteSet, LPExceptSet, ATimeout) <> 0;
  1203. //
  1204. if AReadList <> nil then begin
  1205. TIdSocketListUnix(AReadList).SetFDSet(LReadSet);
  1206. end;
  1207. if AWriteList <> nil then begin
  1208. TIdSocketListUnix(AWriteList).SetFDSet(LWriteSet);
  1209. end;
  1210. if AExceptList <> nil then begin
  1211. TIdSocketListUnix(AExceptList).SetFDSet(LExceptSet);
  1212. end;
  1213. end;
  1214. function TIdSocketListUnix.SelectRead(const ATimeout: Integer): Boolean;
  1215. var
  1216. LSet: TFDSet;
  1217. begin
  1218. Lock;
  1219. try
  1220. LSet := FFDSet;
  1221. // select() updates this structure on return,
  1222. // so we need to copy it each time we need it
  1223. finally
  1224. Unlock;
  1225. end;
  1226. Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
  1227. end;
  1228. function TIdSocketListUnix.SelectReadList(var VSocketList: TIdSocketList;
  1229. const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1230. var
  1231. LSet: TFDSet;
  1232. begin
  1233. Lock;
  1234. try
  1235. LSet := FFDSet;
  1236. // select() updates this structure on return,
  1237. // so we need to copy it each time we need it
  1238. finally
  1239. Unlock;
  1240. end;
  1241. Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
  1242. if Result then begin
  1243. if VSocketList = nil then begin
  1244. VSocketList := TIdSocketList.CreateSocketList;
  1245. end;
  1246. TIdSocketListUnix(VSocketList).SetFDSet(LSet);
  1247. end;
  1248. end;
  1249. function TIdSocketListUnix.Clone: TIdSocketList;
  1250. begin
  1251. Result := TIdSocketListUnix.Create;
  1252. try
  1253. Lock;
  1254. try
  1255. TIdSocketListUnix(Result).SetFDSet(FFDSet);
  1256. finally
  1257. Unlock;
  1258. end;
  1259. except
  1260. FreeAndNil(Result);
  1261. raise;
  1262. end;
  1263. end;
  1264. initialization
  1265. GSocketListClass := TIdSocketListUnix;
  1266. end.