IdStackUnix.pas 42 KB

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