IdStackUnix.pas 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380
  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 VCL_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: TIdUInt64): TIdUInt64; override;
  112. function NetworkToHost(AValue: TIdUInt64): TIdUInt64; 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 VCL_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_SPACE(SizeOf(Byte)) + CMSG_SPACE(SizeOf(in6_pktinfo));
  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 VCL_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 VCL_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} // disable range checking
  628. Result := htonl(AValue);
  629. {$i IdRangeCheckingOn.inc} // Restore range checking
  630. end;
  631. function TIdStackUnix.NetworkToHost(AValue: UInt32): UInt32;
  632. begin
  633. {$i IdRangeCheckingOff.inc} // disable range checking
  634. Result := ntohl(AValue);
  635. // Restore range checking
  636. {$i IdRangeCheckingOn.inc} // Restore range checking
  637. end;
  638. { RP - I'm not sure what endian Linux natively uses, thus the
  639. check to see if the bytes need swapping or not ... }
  640. function TIdStackUnix.HostToNetwork(AValue: TIdUInt64): TIdUInt64;
  641. var
  642. LParts: TIdUInt64Parts;
  643. L: UInt32;
  644. begin
  645. {$i IdRangeCheckingOff.inc} // disable range checking
  646. if (htonl(1) <> 1) then begin
  647. LParts.QuadPart := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  648. L := htonl(LParts.HighPart);
  649. LParts.HighPart := htonl(LParts.LowPart);
  650. LParts.LowPart := L;
  651. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := LParts.QuadPart;
  652. end else begin
  653. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  654. end;
  655. {$i IdRangeCheckingOn.inc} // Restore range checking
  656. end;
  657. function TIdStackUnix.NetworkToHost(AValue: TIdUInt64): TIdUInt64;
  658. var
  659. LParts: TIdUInt64Parts;
  660. L: UInt32;
  661. begin
  662. {$i IdRangeCheckingOff.inc} // disable range checking
  663. if (ntohl(1) <> 1) then begin
  664. LParts.QuadPart := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  665. L := ntohl(LParts.HighPart);
  666. LParts.HighPart := NetworkToHost(LParts.LowPart);
  667. LParts.LowPart := L;
  668. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := LParts.QuadPart;
  669. end else begin
  670. Result{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF} := AValue{$IFDEF TIdUInt64_HAS_QuadPart}.QuadPart{$ENDIF};
  671. end;
  672. {$i IdRangeCheckingOn.inc} // Restore range checking
  673. end;
  674. {$IFDEF HAS_getifaddrs}
  675. // TODO: does FreePascal already define these anywhere?
  676. type
  677. pifaddrs = ^ifaddrs;
  678. ifaddrs = record
  679. ifa_next: pifaddrs; { Pointer to next struct }
  680. ifa_name: PIdAnsiChar; { Interface name }
  681. // Solaris ifaddrs struct implements 64bit ifa_flags. (Details: https://docs.oracle.com/cd/E88353_01/html/E37843/getifaddrs-3c.html)
  682. {$IFDEF SOLARIS}
  683. ifa_flags: UInt64; { Interface flags }
  684. {$ELSE}
  685. ifa_flags: Cardinal; { Interface flags }
  686. {$ENDIF}
  687. ifa_addr: psockaddr; { Interface address }
  688. ifa_netmask: psockaddr; { Interface netmask }
  689. ifa_broadaddr: psockaddr; { Interface broadcast address }
  690. ifa_dstaddr: psockaddr; { P2P interface destination }
  691. ifa_data: Pointer; { Address specific data }
  692. end;
  693. const
  694. IFF_LOOPBACK = $8;
  695. function getifaddrs(var ifap: pifaddrs): Integer; cdecl; external 'libc.so' name 'getifaddrs'; {do not localize}
  696. procedure freeifaddrs(ifap: pifaddrs); cdecl; external 'libc.so' name 'freeifaddrs'; {do not localize}
  697. {$IFDEF HAS_if_nametoindex}
  698. procedure if_nametoindex(const ifname: PIdAnsiChar): UInt32; cdecl; external 'libc.so' name 'if_nametoindex'; {do not localize}
  699. {$ENDIF}
  700. type
  701. TIdStackLocalAddressAccess = class(TIdStackLocalAddress)
  702. end;
  703. {$ENDIF}
  704. procedure TIdStackUnix.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
  705. var
  706. {$IFDEF HAS_getifaddrs}
  707. LAddrList, LAddrInfo: pifaddrs;
  708. LSubNetStr: String;
  709. LAddress: TIdStackLocalAddress;
  710. LName: string;
  711. {$ELSE}
  712. LI4 : array of THostAddr;
  713. LI6 : array of THostAddr6;
  714. i : Integer;
  715. LHostName : String;
  716. {$ENDIF}
  717. begin
  718. // TODO: Using gethostname() and ResolveName() like this may not always
  719. // return just the machine's IP addresses. Technically speaking, they will
  720. // return the local hostname, and then return the address(es) to which that
  721. // hostname resolves. It is possible for a machine to (a) be configured such
  722. // that its name does not resolve to an IP, or (b) be configured such that
  723. // its name resolves to multiple IPs, only one of which belongs to the local
  724. // machine. For better results, we should use getifaddrs() on platforms that
  725. // support it...
  726. {$IFDEF HAS_getifaddrs}
  727. if getifaddrs(LAddrList) = 0 then // TODO: raise an exception if it fails
  728. try
  729. AAddresses.BeginUpdate;
  730. try
  731. LAddrInfo := LAddrList;
  732. repeat
  733. if (LAddrInfo^.ifa_addr <> nil) and ((LAddrInfo^.ifa_flags and IFF_LOOPBACK) = 0) then
  734. begin
  735. LAddress := nil;
  736. case LAddrInfo^.ifa_addr^.sa_family of
  737. Id_PF_INET4: begin
  738. if LAddrInfo^.ifa_netmask <> nil then begin
  739. LSubNetStr := TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ifa_netmask)^.sin_addr, Id_IPv4);
  740. end else begin
  741. LSubNetStr := '';
  742. end;
  743. LAddress := TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In(LAddrInfo^.ifa_addr)^.sin_addr, Id_IPv4), LSubNetStr);
  744. end;
  745. Id_PF_INET6: begin
  746. LAddress := TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString( PSockAddr_In6(LAddrInfo^.ifa_addr)^.sin6_addr, Id_IPv6));
  747. end;
  748. end;
  749. if LAddress <> nil then begin
  750. LName := String(LAddrInfo^.ifa_name);
  751. {$I IdObjectChecksOff.inc}
  752. TIdStackLocalAddressAccess(LAddress).FDescription := LName;
  753. TIdStackLocalAddressAccess(LAddress).FFriendlyName := LName;
  754. TIdStackLocalAddressAccess(LAddress).FInterfaceName := LName;
  755. {$IFDEF HAS_if_nametoindex}
  756. TIdStackLocalAddressAccess(LAddress).FInterfaceIndex := if_nametoindex(LAddrInfo^.ifa_name);
  757. {$ENDIF}
  758. {$I IdObjectChecksOn.inc}
  759. end;
  760. end;
  761. LAddrInfo := LAddrInfo^.ifa_next;
  762. until LAddrInfo = nil;
  763. finally
  764. AAddresses.EndUpdate;
  765. end;
  766. finally
  767. freeifaddrs(LAddrList);
  768. end;
  769. {$ELSE}
  770. LHostName := GetHostName;
  771. if LHostName = '' then begin
  772. RaiseLastSocketError;
  773. end;
  774. AAddresses.BeginUpdate;
  775. try
  776. if ResolveName(LHostName, LI4) = 0 then
  777. begin
  778. for i := Low(LI4) to High(LI4) do
  779. begin
  780. TIdStackLocalAddressIPv4.Create(AAddresses, NetAddrToStr(LI4[i]), ''); // TODO: SubNet
  781. end;
  782. end;
  783. if ResolveName6(LHostName, LI6) = 0 then
  784. begin
  785. for i := Low(LI6) to High(LI6) do
  786. begin
  787. TIdStackLocalAddressIPv6.Create(AAddresses, NetAddrToStr6(LI6[i]));
  788. end;
  789. end;
  790. finally
  791. AAddresses.EndUpdate;
  792. end;
  793. {$ENDIF}
  794. end;
  795. function TIdStackUnix.HostByAddress(const AAddress: string;
  796. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
  797. var
  798. LI : Array of string;
  799. LAddr4: THostAddr;
  800. LAddr6: THostAddr6;
  801. begin
  802. Result := '';
  803. case AIPVersion of
  804. Id_IPv4 :
  805. begin
  806. LAddr4 := StrToNetAddr(AAddress);
  807. if ResolveAddress(LAddr4, LI) = 0 then begin
  808. Result := LI[0];
  809. end;
  810. end;
  811. Id_IPv6 :
  812. begin
  813. LAddr6 := StrToNetAddr6(AAddress);
  814. if ResolveAddress6(LAddr6, LI) = 0 then begin
  815. Result := LI[0];
  816. end;
  817. end;
  818. end;
  819. end;
  820. function TIdStackUnix.WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer;
  821. begin
  822. Result := fpShutdown(ASocket, AHow);
  823. end;
  824. procedure TIdStackUnix.Disconnect(ASocket: TIdStackSocketHandle);
  825. begin
  826. // Windows uses Id_SD_Send, Linux should use Id_SD_Both
  827. WSShutdown(ASocket, Id_SD_Both);
  828. // SO_LINGER is false - socket may take a little while to actually close after this
  829. WSCloseSocket(ASocket);
  830. end;
  831. procedure TIdStackUnix.GetPeerName(ASocket: TIdStackSocketHandle; var VIP: string;
  832. var VPort: TIdPort; var VIPVersion: TIdIPVersion);
  833. var
  834. i: tsocklen;
  835. LAddr: sockaddr_in6;
  836. begin
  837. i := SizeOf(LAddr);
  838. CheckForSocketError(fpGetPeerName(ASocket, @LAddr, @i));
  839. case LAddr.sin6_family of
  840. PF_INET: begin
  841. with Psockaddr(@LAddr)^ do
  842. begin
  843. VIP := NetAddrToStr(sin_addr);
  844. VPort := ntohs(sin_port);
  845. end;
  846. VIPVersion := Id_IPv4;
  847. end;
  848. PF_INET6: begin
  849. with LAddr do
  850. begin
  851. VIP := NetAddrToStr6(sin6_addr);
  852. VPort := ntohs(sin6_port);
  853. end;
  854. VIPVersion := Id_IPv6;
  855. end;
  856. else begin
  857. IPVersionUnsupported;
  858. end;
  859. end;
  860. end;
  861. procedure TIdStackUnix.GetSocketName(ASocket: TIdStackSocketHandle;
  862. var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
  863. var
  864. i: tsocklen;
  865. LAddr: sockaddr_in6;
  866. begin
  867. i := SizeOf(LAddr);
  868. CheckForSocketError(fpGetSockName(ASocket, @LAddr, @i));
  869. case LAddr.sin6_family of
  870. PF_INET : begin
  871. with Psockaddr(@LAddr)^ do
  872. begin
  873. VIP := NetAddrToStr(sin_addr);
  874. VPort := ntohs(sin_port);
  875. end;
  876. VIPVersion := Id_IPV4;
  877. end;
  878. PF_INET6: begin
  879. with LAddr do
  880. begin
  881. VIP := NetAddrToStr6(sin6_addr);
  882. VPort := ntohs(sin6_port);
  883. end;
  884. VIPVersion := Id_IPv6;
  885. end;
  886. else begin
  887. IPVersionUnsupported;
  888. end;
  889. end;
  890. end;
  891. procedure TIdStackUnix.AddServByPortToList(const APortNumber: TIdPort; AAddresses: TStrings);
  892. var
  893. LS : TServiceEntry;
  894. begin
  895. if GetServiceByPort(APortNumber, '', LS) then begin
  896. AAddresses.Add(LS.Name);
  897. end;
  898. end;
  899. function TIdStackUnix.WSTranslateSocketErrorMsg(const AErr: Integer): string;
  900. begin
  901. //we override this function for the herr constants that
  902. //are returned by the DNS functions
  903. //note that this is not really applicable because we are using some
  904. //FPC functions that do direct DNS lookups without the standard Unix
  905. //DNS functions. It sounds odd but I think there's a good reason for it.
  906. Result := inherited WSTranslateSocketErrorMsg(AErr);
  907. end;
  908. procedure TIdStackUnix.SetBlocking(ASocket: TIdStackSocketHandle;
  909. const ABlocking: Boolean);
  910. var
  911. LValue: UInt32;
  912. begin
  913. LValue := UInt32(not ABlocking);
  914. CheckForSocketError(fpioctl(ASocket, FIONBIO, @LValue));
  915. end;
  916. function TIdStackUnix.WouldBlock(const AResult: Integer): Boolean;
  917. begin
  918. // using if-else instead of in..range because EAGAIN and EWOULDBLOCK
  919. // have often the same value and so FPC might report a range error
  920. Result := (AResult = Id_WSAEAGAIN) or
  921. (AResult = Id_WSAEWOULDBLOCK) or
  922. (AResult = Id_WSAEINPROGRESS);
  923. end;
  924. function TIdStackUnix.SupportsIPv4: Boolean;
  925. //In Windows, this does something else. It checks the LSP's installed.
  926. begin
  927. Result := CheckIPVersionSupport(Id_IPv4);
  928. end;
  929. function TIdStackUnix.SupportsIPv6: Boolean;
  930. //In Windows, this does something else. It checks the LSP's installed.
  931. begin
  932. Result := CheckIPVersionSupport(Id_IPv6);
  933. end;
  934. function TIdStackUnix.CheckIPVersionSupport(const AIPVersion: TIdIPVersion): Boolean;
  935. var
  936. LTmpSocket: TIdStackSocketHandle;
  937. begin
  938. // TODO: on nix systems (or maybe just Linux?), an alternative would be to
  939. // check for the existance of the '/proc/net/if_inet6' kernel pseudo-file
  940. LTmpSocket := WSSocket(IdIPFamily[AIPVersion], Integer(Id_SOCK_STREAM), Id_IPPROTO_IP);
  941. Result := LTmpSocket <> Id_INVALID_SOCKET;
  942. if Result then begin
  943. WSCloseSocket(LTmpSocket);
  944. end;
  945. end;
  946. procedure TIdStackUnix.WriteChecksum(s: TIdStackSocketHandle;
  947. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  948. const APort: TIdPort; const AIPVersion: TIdIPVersion);
  949. begin
  950. case AIPVersion of
  951. Id_IPv4 : CopyTIdUInt16(HostToLittleEndian(CalcCheckSum(VBuffer)), VBuffer, AOffset);
  952. Id_IPv6 : WriteChecksumIPv6(s, VBuffer, AOffset, AIP, APort);
  953. else
  954. IPVersionUnsupported;
  955. end;
  956. end;
  957. procedure TIdStackUnix.WriteChecksumIPv6(s: TIdStackSocketHandle;
  958. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  959. const APort: TIdPort);
  960. begin
  961. //we simply request that the kernal write the checksum when the data
  962. //is sent. All of the parameters required are because Windows is bonked
  963. //because it doesn't have the IPV6CHECKSUM socket option meaning we have
  964. //to querry the network interface in TIdStackWindows -- yuck!!
  965. SetSocketOption(s, Id_IPPROTO_IPV6, IPV6_CHECKSUM, AOffset);
  966. end;
  967. function TIdStackUnix.IOControl(const s: TIdStackSocketHandle; const cmd: UInt32;
  968. var arg: UInt32): Integer;
  969. begin
  970. Result := fpioctl(s, cmd, @arg);
  971. end;
  972. (*
  973. Why did I remove this again?
  974. 1) it sends SIGPIPE even if the socket is created with the no-sigpipe bit set
  975. that could be solved by blocking sigpipe within this thread
  976. This is probably a bug in the Linux kernel, but we could work around it
  977. by blocking that signal for the time of sending the file (just get the
  978. sigprocmask, see if pipe bit is set, if not set it and remove again after
  979. sending the file)
  980. But the more serious reason is another one, which exists in Windows too:
  981. 2) I think that ServeFile is misdesigned:
  982. ServeFile does not raise an exception if it didn't send all the bytes.
  983. Now what happens if I have OnExecute assigned like this
  984. AThread.Connection.ServeFile('...', True); // <-- true to send via kernel
  985. is that it will return 0, but notice that in this case I didn't ask for the
  986. result. Net effect is that the thread will loop in OnExecute even if the
  987. socket is long gone. This doesn't fit Indy semantics at all, exceptions are
  988. always raised if the remote end disconnects. Even if I would do
  989. AThread.Connection.ServeFile('...', False);
  990. then it would raise an exception.
  991. I think this is a big flaw in the design of the ServeFile function.
  992. Maybe GServeFile should only return the bytes sent, but then
  993. TCPConnection.ServeFile() should raise an exception if GServeFile didn't
  994. send all the bytes.
  995. JM Berg, 2002-09-09
  996. function ServeFile(ASocket: TIdStackSocketHandle; AFileName: string): UInt32;
  997. var
  998. LFileHandle: integer;
  999. offset: integer;
  1000. stat: _stat;
  1001. begin
  1002. LFileHandle := open(PChar(AFileName), O_RDONLY);
  1003. try
  1004. offset := 0;
  1005. fstat(LFileHandle, stat);
  1006. Result := sendfile(ASocket, LFileHandle, offset, stat.st_size);
  1007. //** if Result = UInt32(-1) then RaiseLastOSError;
  1008. finally libc.__close(LFileHandle); end;
  1009. end;
  1010. *)
  1011. procedure TIdStackUnix.SetKeepAliveValues(ASocket: TIdStackSocketHandle;
  1012. const AEnabled: Boolean; const ATimeMS, AInterval: Integer);
  1013. begin
  1014. inherited; // turn SO_KEEPALIVE on/off first...
  1015. // TODO: remove below, as it should be handled by TIdStack.SetKeepAliveValues() now...
  1016. if AEnabled then begin
  1017. {$IFDEF HAS_TCP_KEEPIDLE}
  1018. SetSocketOption(ASocket, Id_SOL_TCP, Id_TCP_KEEPIDLE, ATimeMS div MSecsPerSec);
  1019. {$ENDIF}
  1020. {$IFDEF HAS_TCP_KEEPINTVL}
  1021. SetSocketOption(ASocket, Id_SOL_TCP, Id_TCP_KEEPINTVL, AInterval div MSecsPerSec);
  1022. {$ENDIF}
  1023. end;
  1024. end;
  1025. { TIdSocketListUnix }
  1026. type
  1027. // TODO: rewrite this to use poll() instead of select(), similar to TIdSocketListVCLPosix
  1028. TIdSocketListUnix = class (TIdSocketList)
  1029. protected
  1030. FCount: Integer;
  1031. FFDSet: TFDSet;
  1032. //
  1033. class function FDSelect(AReadSet: PFDSet; AWriteSet: PFDSet; AExceptSet: PFDSet;
  1034. const ATimeout: Integer = IdTimeoutInfinite): Integer;
  1035. function GetItem(AIndex: Integer): TIdStackSocketHandle; override;
  1036. public
  1037. procedure Add(AHandle: TIdStackSocketHandle); override;
  1038. procedure Remove(AHandle: TIdStackSocketHandle); override;
  1039. function Count: Integer; override;
  1040. procedure Clear; override;
  1041. function Clone: TIdSocketList; override;
  1042. function ContainsSocket(AHandle: TIdStackSocketHandle): Boolean; override;
  1043. procedure GetFDSet(var VSet: TFDSet);
  1044. procedure SetFDSet(var VSet: TFDSet);
  1045. class function Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
  1046. AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  1047. function SelectRead(const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  1048. function SelectReadList(var VSocketList: TIdSocketList;
  1049. const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  1050. end;
  1051. procedure TIdSocketListUnix.Add(AHandle: TIdStackSocketHandle);
  1052. begin
  1053. Lock;
  1054. try
  1055. if fpFD_ISSET(AHandle, FFDSet) = 0 then begin
  1056. if AHandle >= FD_SETSIZE then begin
  1057. raise EIdStackSetSizeExceeded.Create(RSSetSizeExceeded);
  1058. end;
  1059. fpFD_SET(AHandle, FFDSet);
  1060. Inc(FCount);
  1061. end;
  1062. finally
  1063. Unlock;
  1064. end;
  1065. end;//
  1066. procedure TIdSocketListUnix.Clear;
  1067. begin
  1068. Lock;
  1069. try
  1070. fpFD_ZERO(FFDSet);
  1071. FCount := 0;
  1072. finally
  1073. Unlock;
  1074. end;
  1075. end;
  1076. function TIdSocketListUnix.ContainsSocket(AHandle: TIdStackSocketHandle): Boolean;
  1077. begin
  1078. Lock;
  1079. try
  1080. Result := fpFD_ISSET(AHandle, FFDSet) > 0;
  1081. finally
  1082. Unlock;
  1083. end;
  1084. end;
  1085. function TIdSocketListUnix.Count: Integer;
  1086. begin
  1087. Lock;
  1088. try
  1089. Result := FCount;
  1090. finally
  1091. Unlock;
  1092. end;
  1093. end;//
  1094. class function TIdSocketListUnix.FDSelect(AReadSet, AWriteSet, AExceptSet: PFDSet;
  1095. const ATimeout: Integer): Integer;
  1096. var
  1097. LTime: TTimeVal;
  1098. LTimePtr: PTimeVal;
  1099. begin
  1100. if ATimeout = IdTimeoutInfinite then begin
  1101. LTimePtr := nil;
  1102. end else begin
  1103. LTime.tv_sec := ATimeout div 1000;
  1104. LTime.tv_usec := (ATimeout mod 1000) * 1000;
  1105. LTimePtr := @LTime;
  1106. end;
  1107. // TODO: calculate the actual nfds value based on the Sets provided...
  1108. // TODO: use poll() instead of select() to remove limit on how many sockets can be queried
  1109. Result := fpSelect(FD_SETSIZE, AReadSet, AWriteSet, AExceptSet, LTimePtr);
  1110. end;
  1111. procedure TIdSocketListUnix.GetFDSet(var VSet: TFDSet);
  1112. begin
  1113. Lock;
  1114. try
  1115. VSet := FFDSet;
  1116. finally
  1117. Unlock;
  1118. end;
  1119. end;
  1120. function TIdSocketListUnix.GetItem(AIndex: Integer): TIdStackSocketHandle;
  1121. var
  1122. LIndex, i: Integer;
  1123. begin
  1124. Result := 0;
  1125. // TODO: is this missing Lock/Unlock calls?
  1126. LIndex := 0;
  1127. //? use FMaxHandle div x
  1128. for i:= 0 to __FD_SETSIZE - 1 do begin
  1129. if fpFD_ISSET(i, FFDSet) = 1 then begin
  1130. if LIndex = AIndex then begin
  1131. Result := i;
  1132. Break;
  1133. end;
  1134. Inc(LIndex);
  1135. end;
  1136. end;
  1137. end;
  1138. procedure TIdSocketListUnix.Remove(AHandle: TIdStackSocketHandle);
  1139. begin
  1140. Lock;
  1141. try
  1142. if fpFD_ISSET(AHandle, FFDSet) = 1 then
  1143. begin
  1144. Dec(FCount);
  1145. fpFD_CLR(AHandle, FFDSet);
  1146. end;
  1147. finally
  1148. Unlock;
  1149. end;
  1150. end;//
  1151. procedure TIdSocketListUnix.SetFDSet(var VSet: TFDSet);
  1152. begin
  1153. Lock;
  1154. try
  1155. FFDSet := VSet;
  1156. finally
  1157. Unlock;
  1158. end;
  1159. end;
  1160. class function TIdSocketListUnix.Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
  1161. AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1162. var
  1163. LReadSet: TFDSet;
  1164. LWriteSet: TFDSet;
  1165. LExceptSet: TFDSet;
  1166. LPReadSet: PFDSet;
  1167. LPWriteSet: PFDSet;
  1168. LPExceptSet: PFDSet;
  1169. procedure ReadSet(AList: TIdSocketList; var ASet: TFDSet; var APSet: PFDSet);
  1170. begin
  1171. if AList <> nil then begin
  1172. TIdSocketListUnix(AList).GetFDSet(ASet);
  1173. APSet := @ASet;
  1174. end else begin
  1175. APSet := nil;
  1176. end;
  1177. end;
  1178. begin
  1179. ReadSet(AReadList, LReadSet, LPReadSet);
  1180. ReadSet(AWriteList, LWriteSet, LPWriteSet);
  1181. ReadSet(AExceptList, LExceptSet, LPExceptSet);
  1182. //
  1183. Result := FDSelect(LPReadSet, LPWriteSet, LPExceptSet, ATimeout) <> 0;
  1184. //
  1185. if AReadList <> nil then begin
  1186. TIdSocketListUnix(AReadList).SetFDSet(LReadSet);
  1187. end;
  1188. if AWriteList <> nil then begin
  1189. TIdSocketListUnix(AWriteList).SetFDSet(LWriteSet);
  1190. end;
  1191. if AExceptList <> nil then begin
  1192. TIdSocketListUnix(AExceptList).SetFDSet(LExceptSet);
  1193. end;
  1194. end;
  1195. function TIdSocketListUnix.SelectRead(const ATimeout: Integer): Boolean;
  1196. var
  1197. LSet: TFDSet;
  1198. begin
  1199. Lock;
  1200. try
  1201. LSet := FFDSet;
  1202. // select() updates this structure on return,
  1203. // so we need to copy it each time we need it
  1204. finally
  1205. Unlock;
  1206. end;
  1207. Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
  1208. end;
  1209. function TIdSocketListUnix.SelectReadList(var VSocketList: TIdSocketList;
  1210. const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1211. var
  1212. LSet: TFDSet;
  1213. begin
  1214. Lock;
  1215. try
  1216. LSet := FFDSet;
  1217. // select() updates this structure on return,
  1218. // so we need to copy it each time we need it
  1219. finally
  1220. Unlock;
  1221. end;
  1222. Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
  1223. if Result then begin
  1224. if VSocketList = nil then begin
  1225. VSocketList := TIdSocketList.CreateSocketList;
  1226. end;
  1227. TIdSocketListUnix(VSocketList).SetFDSet(LSet);
  1228. end;
  1229. end;
  1230. function TIdSocketListUnix.Clone: TIdSocketList;
  1231. begin
  1232. Result := TIdSocketListUnix.Create;
  1233. try
  1234. Lock;
  1235. try
  1236. TIdSocketListUnix(Result).SetFDSet(FFDSet);
  1237. finally
  1238. Unlock;
  1239. end;
  1240. except
  1241. FreeAndNil(Result);
  1242. raise;
  1243. end;
  1244. end;
  1245. initialization
  1246. GSocketListClass := TIdSocketListUnix;
  1247. end.