IdStackUnix.pas 43 KB

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