IdStackUnix.pas 43 KB

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