IdStackLinux.pas 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446
  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. }
  13. {
  14. $Log$
  15. }
  16. {
  17. Rev 1.7 10/26/2004 8:20:04 PM JPMugaas
  18. Fixed some oversights with conversion. OOPS!!!
  19. Rev 1.6 10/26/2004 8:12:32 PM JPMugaas
  20. Now uses TIdStrings and TIdStringList for portability.
  21. Rev 1.5 12/06/2004 15:17:20 CCostelloe
  22. Restructured to correspond with IdStackWindows, now works.
  23. Rev 1.4 07/06/2004 21:31:02 CCostelloe
  24. Kylix 3 changes
  25. Rev 1.3 4/18/04 10:43:22 PM RLebeau
  26. Fixed syntax error
  27. Rev 1.2 4/18/04 10:29:46 PM RLebeau
  28. Renamed Int64Parts structure to TIdInt64Parts
  29. Rev 1.1 4/18/04 2:47:28 PM RLebeau
  30. Conversion support for Int64 values
  31. Removed WSHToNs(), WSNToHs(), WSHToNL(), and WSNToHL() methods, obsolete
  32. Rev 1.0 2004.02.03 3:14:48 PM czhower
  33. Move and updates
  34. Rev 1.3 10/19/2003 5:35:14 PM BGooijen
  35. SetSocketOption
  36. Rev 1.2 2003.10.01 9:11:24 PM czhower
  37. .Net
  38. Rev 1.1 7/5/2003 07:25:50 PM JPMugaas
  39. Added functions to the Linux stack which use the new TIdIPAddress record type
  40. for IP address parameters. I also fixed a compile bug.
  41. Rev 1.0 11/13/2002 08:59:24 AM JPMugaas
  42. }
  43. unit IdStackLinux;
  44. interface
  45. {$i IdCompilerDefines.inc}
  46. uses
  47. Classes,
  48. Libc,
  49. IdStack,
  50. IdStackConsts,
  51. IdGlobal,
  52. IdStackBSDBase;
  53. type
  54. TIdStackLinux = class(TIdStackBSDBase)
  55. private
  56. procedure WriteChecksumIPv6(s: TIdStackSocketHandle;
  57. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  58. const APort: TIdPort);
  59. protected
  60. function GetLastError : Integer;
  61. procedure SetLastError(Const AError : Integer);
  62. function HostByName(const AHostName: string;
  63. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
  64. function ReadHostName: string; override;
  65. function WSCloseSocket(ASocket: TIdStackSocketHandle): Integer; override;
  66. function WSRecv(ASocket: TIdStackSocketHandle;
  67. var ABuffer; const ABufferLength, AFlags: Integer): Integer; override;
  68. function WSSend(ASocket: TIdStackSocketHandle; const ABuffer; const ABufferLength, AFlags: Integer): Integer; override;
  69. function WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer; override;
  70. {$IFNDEF DCC_XE3_OR_ABOVE}
  71. procedure WSGetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  72. AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
  73. procedure WSSetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  74. AOptName: TIdSocketOption; const AOptVal; const AOptLen: Integer); override;
  75. {$ENDIF}
  76. public
  77. procedure SetBlocking(ASocket: TIdStackSocketHandle;
  78. const ABlocking: Boolean); override;
  79. function WouldBlock(const AResult: Integer): Boolean; override;
  80. function WSTranslateSocketErrorMsg(const AErr: Integer): string; override;
  81. function Accept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: TIdPort;
  82. var VIPVersion: TIdIPVersion): TIdStackSocketHandle; override;
  83. procedure Bind(ASocket: TIdStackSocketHandle; const AIP: string;
  84. const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  85. procedure Connect(const ASocket: TIdStackSocketHandle; const AIP: string;
  86. const APort: TIdPort; const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  87. function HostByAddress(const AAddress: string;
  88. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string; override;
  89. function WSGetLastError: Integer; override;
  90. procedure WSSetLastError(const AErr : Integer); override;
  91. function WSGetServByName(const AServiceName: string): TIdPort; override;
  92. function WSGetServByPort(const APortNumber: TIdPort): TStrings; override;
  93. procedure GetPeerName(ASocket: TIdStackSocketHandle; var VIP: string;
  94. var VPort: TIdPort; var VIPVersion: TIdIPVersion); override;
  95. procedure GetSocketName(ASocket: TIdStackSocketHandle; var VIP: string;
  96. var VPort: TIdPort; var VIPVersion: TIdIPVersion); override;
  97. procedure Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer); override;
  98. function HostToNetwork(AValue: UInt16): UInt16; override;
  99. function NetworkToHost(AValue: UInt16): UInt16; override;
  100. function HostToNetwork(AValue: UInt32): UInt32; override;
  101. function NetworkToHost(AValue: UInt32): UInt32; override;
  102. function HostToNetwork(AValue: UInt64): UInt64; override;
  103. function NetworkToHost(AValue: UInt64): UInt64; override;
  104. function RecvFrom(const ASocket: TIdStackSocketHandle; var VBuffer;
  105. const ALength, AFlags: Integer; var VIP: string; var VPort: TIdPort;
  106. var VIPVersion: TIdIPVersion): Integer; override;
  107. function ReceiveMsg(ASocket: TIdStackSocketHandle; var VBuffer: TIdBytes;
  108. APkt: TIdPacketInfo): UInt32; override;
  109. procedure WSSendTo(ASocket: TIdStackSocketHandle; const ABuffer;
  110. const ABufferLength, AFlags: Integer;
  111. const AIP: string; const APort: TIdPort; AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  112. function WSSocket(AFamily : Integer; AStruct : TIdSocketType; AProtocol: Integer;
  113. const ANonBlocking: Boolean = False): TIdStackSocketHandle; override;
  114. procedure Disconnect(ASocket: TIdStackSocketHandle); override;
  115. {$IFDEF DCC_XE3_OR_ABOVE}
  116. procedure GetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  117. AOptName: TIdSocketOption; var AOptVal; var AOptLen: Integer); override;
  118. procedure SetSocketOption(ASocket: TIdStackSocketHandle; ALevel: TIdSocketOptionLevel;
  119. AOptName: TIdSocketOption; const AOptVal; const AOptLen: Integer); override;
  120. {$ENDIF}
  121. function SupportsIPv4: Boolean; overload; override;
  122. function SupportsIPv6: Boolean; overload; override;
  123. function CheckIPVersionSupport(const AIPVersion: TIdIPVersion): boolean; override;
  124. constructor Create; override;
  125. destructor Destroy; override;
  126. //In Windows, this writes a checksum into a buffer. In Linux, it would probably
  127. //simply have the kernal write the checksum with something like this (RFC 2292):
  128. //
  129. // int offset = 2;
  130. // setsockopt(fd, IPPROTO_IPV6, IPV6_CHECKSUM, &offset, sizeof(offset));
  131. //
  132. // Note that this should be called
  133. //IMMEDIATELY before you do a SendTo because the Local IPv6 address might change
  134. procedure WriteChecksum(s : TIdStackSocketHandle;
  135. var VBuffer : TIdBytes;
  136. const AOffset : Integer;
  137. const AIP : String;
  138. const APort : TIdPort;
  139. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION); override;
  140. function IOControl(const s: TIdStackSocketHandle; const cmd: UInt32;
  141. var arg: UInt32): Integer; override;
  142. procedure GetLocalAddressList(AAddresses: TIdStackLocalAddressList); override;
  143. end;
  144. TLinger = record
  145. l_onoff: UInt16;
  146. l_linger: UInt16;
  147. end;
  148. TIdLinger = TLinger;
  149. implementation
  150. uses
  151. IdResourceStrings,
  152. IdResourceStringsKylixCompat,
  153. IdResourceStringsUnix,
  154. IdException,
  155. SysUtils;
  156. type
  157. psockaddr_in6 = ^sockaddr_in6;
  158. const
  159. Id_MSG_NOSIGNAL = MSG_NOSIGNAL;
  160. Id_WSAEPIPE = EPIPE;
  161. constructor TIdStackLinux.Create;
  162. begin
  163. inherited Create;
  164. end;
  165. destructor TIdStackLinux.Destroy;
  166. begin
  167. inherited Destroy;
  168. end;
  169. function TIdStackLinux.GetLastError : Integer;
  170. begin
  171. Result := errno;
  172. end;
  173. procedure TIdStackLinux.SetLastError(Const AError : Integer);
  174. begin
  175. __errno_location^ := AError;
  176. end;
  177. procedure TIdStackLinux.WSSetLastError(const AErr : Integer);
  178. begin
  179. SetLastError(AErr);
  180. end;
  181. function TIdStackLinux.Accept(ASocket: TIdStackSocketHandle;
  182. var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion): TIdStackSocketHandle;
  183. var
  184. LN: UInt32;
  185. LAddr: sockaddr_in6;
  186. begin
  187. LN := SizeOf(LAddr);
  188. Result := Libc.accept(ASocket, PSockAddr(@LAddr), @LN);
  189. if Result <> SOCKET_ERROR then begin
  190. case LAddr.sin6_family of
  191. Id_PF_INET4: begin
  192. with Psockaddr(@LAddr)^ do
  193. begin
  194. VIP := TranslateTInAddrToString(sin_addr, Id_IPv4);
  195. VPort := Ntohs(sin_port);
  196. end;
  197. VIPVersion := Id_IPV4;
  198. end;
  199. Id_PF_INET6: begin
  200. with LAddr do begin
  201. VIP := TranslateTInAddrToString(sin6_addr, Id_IPv6);
  202. VPort := ntohs(sin6_port);
  203. end;
  204. VIPVersion := Id_IPV6;
  205. end;
  206. else begin
  207. Libc.__close(Result);
  208. Result := Id_INVALID_SOCKET;
  209. IPVersionUnsupported;
  210. end;
  211. end;
  212. end else begin
  213. if GetLastError = EBADF then begin
  214. SetLastError(EINTR);
  215. end;
  216. end;
  217. end;
  218. procedure TIdStackLinux.Bind(ASocket: TIdStackSocketHandle;
  219. const AIP: string; const APort: TIdPort;
  220. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  221. var
  222. LAddr: sockaddr_in6;
  223. LSize: UInt32;
  224. begin
  225. FillChar(LAddr, SizeOf(LAddr), 0);
  226. case AIPVersion of
  227. Id_IPv4: begin
  228. with Psockaddr(@LAddr)^ do begin
  229. sin_family := Id_PF_INET4;
  230. if AIP <> '' then begin
  231. TranslateStringToTInAddr(AIP, sin_addr, Id_IPv4);
  232. end;
  233. sin_port := htons(APort);
  234. end;
  235. LSize := SizeOf(sockaddr);
  236. end;
  237. Id_IPv6: begin
  238. with LAddr do
  239. begin
  240. sin6_family := Id_PF_INET6;
  241. if AIP <> '' then begin
  242. TranslateStringToTInAddr(AIP, sin6_addr, Id_IPv6);
  243. end;
  244. sin6_port := htons(APort);
  245. end;
  246. LSize := SizeOf(sockaddr_in6);
  247. end;
  248. else begin
  249. LSize := 0; // avoid warning
  250. IPVersionUnsupported;
  251. end;
  252. end;
  253. CheckForSocketError(Libc.bind(ASocket, Psockaddr(@LAddr), LSize);
  254. end;
  255. function TIdStackLinux.WSCloseSocket(ASocket: TIdStackSocketHandle): Integer;
  256. begin
  257. Result := Libc.__close(ASocket);
  258. end;
  259. procedure TIdStackLinux.Connect(const ASocket: TIdStackSocketHandle;
  260. const AIP: string; const APort: TIdPort;
  261. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  262. var
  263. LAddr: sockaddr_in6;
  264. LSize: UInt32;
  265. begin
  266. FillChar(LAddr, SizeOf(LAddr), 0);
  267. case AIPVersion of
  268. Id_IPv4: begin
  269. with Psockaddr(@LAddr)^ do begin
  270. sin_family := Id_PF_INET4;
  271. TranslateStringToTInAddr(AIP, sin_addr, Id_IPv4);
  272. sin_port := htons(APort);
  273. end;
  274. LSize := SizeOf(sockaddr);
  275. end;
  276. Id_IPv6: begin
  277. with LAddr do begin
  278. sin6_family := Id_PF_INET6;
  279. TranslateStringToTInAddr(AIP, sin6_addr, Id_IPv6);
  280. sin6_port := htons(APort);
  281. end;
  282. LSize := SizeOf(sockaddr_in6);
  283. end;
  284. else begin
  285. LSize := 0; // avoid warning
  286. IPVersionUnsupported;
  287. end;
  288. end;
  289. CheckForSocketError(Libc.connect(ASocket, Psockaddr(@LAddr), LSize));
  290. end;
  291. function TIdStackLinux.HostByName(const AHostName: string;
  292. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
  293. var
  294. Lpa: PAnsiChar;
  295. Lsa: TInAddr;
  296. LHost: PHostEnt;
  297. // ipv6
  298. LHints: TAddressInfo;
  299. LAddrInfo: PAddrInfo;
  300. LRetVal: Integer;
  301. LAStr: AnsiString;
  302. begin
  303. case AIPVersion of
  304. Id_IPv4: begin
  305. LAStr := AnsiString(AHostName); // explicit convert to Ansi
  306. // TODO: use getaddrinfo() instead for IPv4 as well...
  307. LHost := Libc.gethostbyname(PAnsiChar(LAStr));
  308. if LHost <> nil then begin
  309. // TODO: gethostbyname() might return other things besides IPv4
  310. // addresses, so we should be validating the address type before
  311. // attempting the conversion...
  312. Lpa := LHost^.h_addr_list^;
  313. Lsa.S_un_b.s_b1 := Ord(Lpa[0]);
  314. Lsa.S_un_b.s_b2 := Ord(Lpa[1]);
  315. Lsa.S_un_b.s_b3 := Ord(Lpa[2]);
  316. Lsa.S_un_b.s_b4 := Ord(Lpa[3]);
  317. Result := TranslateTInAddrToString(Lsa, Id_IPv4);
  318. end else begin
  319. //RaiseSocketError(h_errno);
  320. RaiseLastSocketError;
  321. end;
  322. end;
  323. Id_IPv6: begin
  324. FillChar(LHints, SizeOf(LHints), 0);
  325. LHints.ai_family := IdIPFamily[AIPVersion];
  326. LHints.ai_socktype := Integer(SOCK_STREAM);
  327. LAddrInfo := nil;
  328. LAStr := AnsiString(AHostName); // explicit convert to Ansi
  329. LRetVal := getaddrinfo(PAnsiChar(LAStr), nil, @LHints, @LAddrInfo);
  330. if LRetVal <> 0 then begin
  331. if LRetVal = EAI_SYSTEM then begin
  332. IndyRaiseLastError;
  333. end else begin
  334. raise EIdResolveError.CreateFmt(RSResolveError, [AHostName, gai_strerror(LRetVal), LRetVal]);
  335. end;
  336. end;
  337. try
  338. Result := TranslateTInAddrToString(LAddrInfo^.ai_addr^.sin_zero, Id_IPv6);
  339. finally
  340. freeaddrinfo(LAddrInfo);
  341. end;
  342. end;
  343. else
  344. Result := ''; // avoid warning
  345. IPVersionUnsupported;
  346. end;
  347. end;
  348. function TIdStackLinux.ReadHostName: string;
  349. var
  350. LStr: array[0..250] of AnsiChar;
  351. begin
  352. if Libc.gethostname(LStr, 250) <> -1 then begin
  353. LStr[250] := #0;
  354. Result := String(LStr);
  355. end else begin
  356. Result := '';
  357. end;
  358. end;
  359. procedure TIdStackLinux.Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer);
  360. begin
  361. CheckForSocketError(Libc.listen(ASocket, ABacklog));
  362. end;
  363. function TIdStackLinux.WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
  364. const ABufferLength, AFlags: Integer): Integer;
  365. begin
  366. //IdStackWindows is just: Result := Recv(ASocket, ABuffer, ABufferLength, AFlags);
  367. Result := Recv(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
  368. end;
  369. function TIdStackLinux.RecvFrom(const ASocket: TIdStackSocketHandle;
  370. var VBuffer; const ALength, AFlags: Integer; var VIP: string;
  371. var VPort: TIdPort; var VIPVersion: TIdIPVersion): Integer;
  372. var
  373. LiSize: UInt32;
  374. LAddr: sockaddr_in6;
  375. begin
  376. LiSize := SizeOf(sockaddr_in6);
  377. Result := Libc.recvfrom(ASocket, VBuffer, ALength, AFlags or Id_MSG_NOSIGNAL, PSockAddr(@LAddr), @LiSize);
  378. if Result >= 0 then
  379. begin
  380. case LAddr.sin6_family of
  381. Id_PF_INET4: begin
  382. with Psockaddr(@LAddr)^ do begin
  383. VIP := TranslateTInAddrToString(sin_addr, Id_IPv4);
  384. VPort := Ntohs(sin_port);
  385. end;
  386. VIPVersion := Id_IPV4;
  387. end;
  388. Id_PF_INET6: begin
  389. with LAddr do begin
  390. VIP := TranslateTInAddrToString(sin6_addr, Id_IPv6);
  391. VPort := ntohs(sin6_port);
  392. end;
  393. VIPVersion := Id_IPV6;
  394. end;
  395. else begin
  396. Result := 0;
  397. IPVersionUnsupported;
  398. end;
  399. end;
  400. end;
  401. end;
  402. function TIdStackLinux.ReceiveMsg(ASocket: TIdStackSocketHandle;
  403. var VBuffer: TIdBytes; APkt: TIdPacketInfo): UInt32;
  404. {var
  405. LIP : String;
  406. LPort : TIdPort;
  407. LSize: UInt32;
  408. LAddr: SockAddr_In6;
  409. LMsg : msghdr;
  410. LMsgBuf : BUF;
  411. LControl : TIdBytes;
  412. LCurCmsg : CMSGHDR; //for iterating through the control buffer
  413. LByte : PByte;
  414. LDummy, LDummy2 : UInt32;
  415. begin
  416. //we call the macro twice because we specified two possible structures.
  417. //Id_IPV6_HOPLIMIT and Id_IPV6_PKTINFO
  418. LSize := CMSG_LEN(CMSG_LEN(Length(VBuffer)));
  419. SetLength( LControl,LSize);
  420. LMsgBuf.len := Length(VBuffer); // Length(VMsgData);
  421. LMsgBuf.buf := @VBuffer[0]; // @VMsgData[0];
  422. FillChar(LMsg,SizeOf(LMsg),0);
  423. LMsg.lpBuffers := @LMsgBuf;
  424. LMsg.dwBufferCount := 1;
  425. LMsg.Control.Len := LSize;
  426. LMsg.Control.buf := @LControl[0];
  427. LMsg.name := PSOCKADDR(@LAddr);
  428. LMsg.namelen := SizeOf(LAddr);
  429. CheckForSocketError(RecvMsg(ASocket, @LMsg, Result, @LDummy, LPwsaoverlapped_COMPLETION_ROUTINE(@LDummy2)));
  430. APkt.Reset;
  431. case LAddr.sin6_family of
  432. Id_PF_INET4: begin
  433. with Psockaddr(@LAddr)^ do
  434. begin
  435. APkt.SourceIP := TranslateTInAddrToString(sin_addr, Id_IPv4);
  436. APkt.SourcePort := ntohs(sin_port);
  437. end;
  438. APkt.SourceIPVersion := Id_IPv4;
  439. end;
  440. Id_PF_INET6: begin
  441. with LAddr do
  442. begin
  443. APkt.SourceIP := TranslateTInAddrToString(sin6_addr, Id_IPv6);
  444. APkt.SourcePort := ntohs(sin6_port);
  445. end;
  446. APkt.SourceIPVersion := Id_IPv6;
  447. end;
  448. else begin
  449. Result := 0; // avoid warning
  450. IPVersionUnsupported;
  451. end;
  452. end;
  453. LCurCmsg := nil;
  454. repeat
  455. LCurCmsg := CMSG_NXTHDR(@LMsg,LCurCmsg);
  456. if LCurCmsg=nil then
  457. begin
  458. break;
  459. end;
  460. case LCurCmsg^.cmsg_type of
  461. IP_PKTINFO : //done this way because IPV6_PKTINF and IP_PKTINFO
  462. //are both 19
  463. begin
  464. case LAddr.sin6_family of
  465. Id_PF_INET4: begin
  466. with Pin_pktinfo(WSA_CMSG_DATA(LCurCmsg))^ do
  467. begin
  468. APkt.DestIP := GWindowsStack.TranslateTInAddrToString(ipi_addr, Id_IPv4);
  469. APkt.DestIF := ipi_ifindex;
  470. end;
  471. APkt.DestIPVersion := Id_IPv4;
  472. end;
  473. Id_PF_INET6: begin
  474. with Pin6_pktinfo(WSA_CMSG_DATA(LCurCmsg))^ do
  475. begin
  476. APkt.DestIP := GWindowsStack.TranslateTInAddrToString(ipi6_addr, Id_IPv6);
  477. APkt.DestIF := ipi6_ifindex;
  478. end;
  479. APkt.DestIPVersion := Id_IPv6;
  480. end;
  481. end;
  482. end;
  483. Id_IPV6_HOPLIMIT :
  484. begin
  485. LByte := PByte(WSA_CMSG_DATA(LCurCmsg));
  486. APkt.TTL := LByte^;
  487. end;
  488. end;
  489. until False; }
  490. begin
  491. APkt.Reset;
  492. Result := 0; // avoid warning
  493. end;
  494. function TIdStackLinux.WSSend(ASocket: TIdStackSocketHandle;
  495. const ABuffer; const ABufferLength, AFlags: Integer): Integer;
  496. begin
  497. //CC: Should Id_MSG_NOSIGNAL be included?
  498. // Result := Send(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
  499. Result := CheckForSocketError(Libc.send(ASocket, ABuffer, ABufferLength, AFlags));
  500. end;
  501. procedure TIdStackLinux.WSSendTo(ASocket: TIdStackSocketHandle;
  502. const ABuffer; const ABufferLength, AFlags: Integer; const AIP: string;
  503. const APort: TIdPort; AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION);
  504. var
  505. LAddr: sockaddr_in6;
  506. LiSize: Integer;
  507. begin
  508. FillChar(LAddr, SizeOf(LAddr), 0);
  509. case AIPVersion of
  510. Id_IPv4: begin
  511. with Psockaddr(@LAddr)^ do begin
  512. sin_family := Id_PF_INET4;
  513. TranslateStringToTInAddr(AIP, sin_addr, Id_IPv4);
  514. sin_port := htons(APort);
  515. end;
  516. LiSize := SizeOf(sockaddr);
  517. end;
  518. Id_IPv6: begin
  519. with LAddr do begin
  520. sin6_family := Id_PF_INET6;
  521. TranslateStringToTInAddr(AIP, sin6_addr, Id_IPv6);
  522. sin6_port := htons(APort);
  523. end;
  524. LiSize := SizeOf(sockaddr_in6);
  525. end;
  526. else begin
  527. LiSize := 0; // avoid warning
  528. IPVersionUnsupported;
  529. end;
  530. end;
  531. LiSize := Libc.sendto(
  532. ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL,
  533. Psockaddr(@LAddr), LiSize);
  534. end;
  535. if LiSize = Id_SOCKET_ERROR then begin
  536. // TODO: move this into RaiseLastSocketError directly
  537. if WSGetLastError() = Id_WSAEMSGSIZE then begin
  538. raise EIdPackageSizeTooBig.Create(RSPackageSizeTooBig);
  539. end else begin
  540. RaiseLastSocketError;
  541. end;
  542. end
  543. else if LiSize <> ABufferLength then begin
  544. raise EIdNotAllBytesSent.Create(RSNotAllBytesSent);
  545. end;
  546. end;
  547. procedure TIdStackLinux.{$IFDEF DCC_XE3_OR_ABOVE}GetSocketOption{$ELSE}WSGetSocketOption{$ENDIF}
  548. (ASocket: TIdStackSocketHandle; ALevel: TIdSocketProtocol; AOptName: TIdSocketOption;
  549. var AOptVal; var AOptLen: Integer);
  550. var
  551. LLen: UInt32;
  552. begin
  553. LLen := AOptLen;
  554. CheckForSocketError(Libc.getsockopt(ASocket, ALevel, AOptName, PAnsiChar(@AOptVal), LLen));
  555. AOptLen := LLen;
  556. end;
  557. procedure TIdStackLinux.{$IFDEF DCC_XE3_OR_ABOVE}SetSocketOption{$ELSE}WSSetSocketOption{$ENDIF}
  558. (ASocket: TIdStackSocketHandle; ALevel: TIdSocketProtocol; AOptName: TIdSocketOption;
  559. const AOptVal; const AOptLen: Integer);
  560. begin
  561. CheckForSocketError(Libc.setsockopt(ASocket, ALevel, AOptName, PAnsiChar(@AOptVal), AOptLen));
  562. end;
  563. function TIdStackLinux.WSGetLastError: Integer;
  564. begin
  565. //IdStackWindows just uses result := WSAGetLastError;
  566. Result := GetLastError; //System.GetLastOSError; - FPC doesn't define it in System
  567. if Result = Id_WSAEPIPE then begin
  568. Result := Id_WSAECONNRESET;
  569. end;
  570. end;
  571. function TIdStackLinux.WSSocket(AFamily : Integer; AStruct : TIdSocketType; AProtocol: Integer;
  572. const ANonBlocking: Boolean = False): TIdStackSocketHandle;
  573. begin
  574. Result := Libc.socket(AFamily, AStruct or iif(ANonBlocking, SOCK_NONBLOCK, 0), AProtocol);
  575. end;
  576. function TIdStackLinux.WSGetServByName(const AServiceName: string): TIdPort;
  577. var
  578. Lps: PServEnt;
  579. LAStr: AnsiString;
  580. begin
  581. LAStr := AnsiString(AServiceName); // explicit convert to Ansi
  582. Lps := Libc.getservbyname(PAnsiChar(LAStr), nil);
  583. if Lps <> nil then begin
  584. Result := ntohs(Lps^.s_port);
  585. end else begin
  586. try
  587. Result := IndyStrToInt(AServiceName);
  588. except
  589. on EConvertError do begin
  590. IndyRaiseOuterException(EIdInvalidServiceName.CreateFmt(RSInvalidServiceName, [AServiceName]));
  591. end;
  592. end;
  593. end;
  594. end;
  595. function TIdStackLinux.WSGetServByPort(const APortNumber: TIdPort): TStrings;
  596. type
  597. PPAnsiCharArray = ^TPAnsiCharArray;
  598. TPAnsiCharArray = packed array[0..(Maxint div SizeOf(PAnsiChar))-1] of PAnsiChar;
  599. var
  600. Lps: PServEnt;
  601. Li: Integer;
  602. Lp: PPAnsiCharArray;
  603. begin
  604. Result := TStringList.Create;
  605. Lp := nil;
  606. try
  607. Lps := Libc.getservbyport(htons(APortNumber), nil);
  608. if Lps <> nil then begin
  609. Result.Add(Lps^.s_name);
  610. Li := 0;
  611. Lp := Pointer(Lps^.s_aliases);
  612. while Lp[Li] <> nil do begin
  613. Result.Add(Lp[Li]);
  614. Inc(Li);
  615. end;
  616. end;
  617. except
  618. Result.Free;
  619. raise;
  620. end;
  621. end;
  622. function TIdStackLinux.HostToNetwork(AValue: UInt16): UInt16;
  623. begin
  624. Result := htons(AValue);
  625. end;
  626. function TIdStackLinux.NetworkToHost(AValue: UInt16): UInt16;
  627. begin
  628. Result := ntohs(AValue);
  629. end;
  630. function TIdStackLinux.HostToNetwork(AValue: UInt32): UInt32;
  631. begin
  632. Result := htonl(AValue);
  633. end;
  634. function TIdStackLinux.NetworkToHost(AValue: UInt32): UInt32;
  635. begin
  636. Result := ntohl(AValue);
  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 TIdStackLinux.HostToNetwork(AValue: UInt64): UInt64;
  641. var
  642. LParts: TIdUInt64Parts;//TIdUInt64Words
  643. L: UInt32;
  644. begin
  645. // TODO: enable this?
  646. (*
  647. LParts.LongWords[0] := htonl(UInt32(AValue shr 32));
  648. LParts.LongWords[1] := htonl(UInt32(AValue));
  649. Result := LParts.QuadPart;
  650. *)
  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. end;
  661. function TIdStackLinux.NetworkToHost(AValue: UInt64): UInt64;
  662. var
  663. LParts: TIdUInt64Parts;//TIdUInt64Words
  664. L: UInt32;
  665. begin
  666. // TODO: enable this?
  667. (*
  668. LParts.QuadPart := AValue;
  669. Result := (UInt64(ntohl(LParts.LongWords[0])) shl 32) or UInt64(ntohl(LParts.LongWords[1]));
  670. *)
  671. if (ntohl(1) <> 1) then begin
  672. LParts.QuadPart := AValue;
  673. L := ntohl(LParts.HighPart);
  674. LParts.HighPart := ntohl(LParts.LowPart);
  675. LParts.LowPart := L;
  676. Result := LParts.QuadPart;
  677. end else begin
  678. Result := AValue;
  679. end;
  680. end;
  681. {$IFDEF HAS_getifaddrs}
  682. type
  683. TIdStackLocalAddressAccess = class(TIdStackLocalAddress)
  684. end;
  685. {$ENDIF}
  686. procedure TIdStackLinux.GetLocalAddressList(AAddresses: TIdStackLocalAddressList);
  687. {$IFNDEF HAS_getifaddrs}
  688. type
  689. TaPInAddr = array[0..250] of PInAddr;
  690. PaPInAddr = ^TaPInAddr;
  691. TaPIn6Addr = array[0..250] of PIn6Addr;
  692. PaPIn6Addr = ^TaPIn6Addr;
  693. {$ENDIF}
  694. var
  695. {$IFDEF HAS_getifaddrs}
  696. LAddrList, LAddrInfo: pifaddrs;
  697. LSubNetStr, LBroadcastStr: string;
  698. LAddress: TIdStackLocalAddress;
  699. LName: string;
  700. {$ELSE}
  701. Li: Integer;
  702. LAHost: PHostEnt;
  703. LPAdrPtr: PaPInAddr;
  704. LHostName: AnsiString;
  705. {$ENDIF}
  706. begin
  707. // TODO: Using gethostname() and gethostbyname() like this may not always
  708. // return just the machine's IP addresses. Technically speaking, they will
  709. // return the local hostname, and then return the address(es) to which that
  710. // hostname resolves. It is possible for a machine to (a) be configured such
  711. // that its name does not resolve to an IP, or (b) be configured such that
  712. // its name resolves to multiple IPs, only one of which belongs to the local
  713. // machine. For better results, we should use getifaddrs() on platforms that
  714. // support it...
  715. {$IFDEF HAS_getifaddrs}
  716. if getifaddrs(@LAddrList) = 0 then // TODO: raise an exception if it fails
  717. try
  718. AAddresses.BeginUpdate;
  719. try
  720. LAddrInfo := LAddrList;
  721. repeat
  722. if ((LAddrInfo^.ifa_flags and IFF_LOOPBACK) = 0) and (LAddrInfo^.ifa_addr <> nil) then
  723. begin
  724. LAddress := nil;
  725. case LAddrInfo^.ifa_addr^.sa_family of
  726. Id_PF_INET4: begin
  727. if LAddrInfo^.ifa_netmask <> nil then begin
  728. LSubNetStr := TranslateTInAddrToString(PSockAddr_In(LAddrInfo^.ifa_netmask)^.sin_addr, Id_IPv4);
  729. end else begin
  730. LSubNetStr := '';
  731. end;
  732. if ((LAddrInfo^.ifa_flags and IFF_BROADCAST) <> 0) and (LAddrInfo^.ifa_broadaddr <> nil) then
  733. LBroadcastStr := TranslateTInAddrToString(PSockAddr_In(LAddrInfo^.ifa_broadaddr)^.sin_addr, Id_IPv4);
  734. end else begin
  735. LBroadcastStr := '';
  736. end;
  737. LAddress := TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString(PSockAddr_In(LAddrInfo^.ifa_addr)^.sin_addr, Id_IPv4), LSubNetStr, LBroadcastStr);
  738. end;
  739. Id_PF_INET6: begin
  740. LAddress := TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString(PSockAddr_In6(LAddrInfo^.ifa_addr)^.sin6_addr, Id_IPv6));
  741. end;
  742. end;
  743. if LAddress <> nil then begin
  744. LName := LAddrInfo^.ifa_name;
  745. {$I IdObjectChecksOff.inc}
  746. TIdStackLocalAddressAccess(LAddress).FDescription := LName;
  747. TIdStackLocalAddressAccess(LAddress).FFriendlyName := LName;
  748. TIdStackLocalAddressAccess(LAddress).FInterfaceName := LName;
  749. {$IFDEF HAS_if_nametoindex}
  750. TIdStackLocalAddressAccess(LAddress).FInterfaceIndex := if_nametoindex(LAddrInfo^.ifa_name);
  751. {$ENDIF}
  752. {$I IdObjectChecksOn.inc}
  753. end;
  754. end;
  755. LAddrInfo := LAddrInfo^.ifa_next;
  756. until LAddrInfo = nil;
  757. finally
  758. AAddresses.EndUpdate;
  759. end;
  760. finally
  761. freeifaddrs(LAddrList);
  762. end;
  763. {$ELSE}
  764. // this won't get IPv6 addresses as I didn't find a way
  765. // to enumerate IPv6 addresses on a linux machine
  766. LHostName := HostName;
  767. LAHost := Libc.gethostbyname(PAnsiChar(LHostName));
  768. if LAHost = nil then begin
  769. RaiseLastSocketError;
  770. end;
  771. // gethostbyname() might return other things besides IPv4 addresses, so we
  772. // need to validate the address type before attempting the conversion...
  773. case LAHost^.h_addrtype of
  774. Id_PF_INET4: begin
  775. LPAdrPtr := PAPInAddr(LAHost^.h_addr_list);
  776. Li := 0;
  777. if LPAdrPtr^[Li] <> nil then begin
  778. AAddresses.BeginUpdate;
  779. try
  780. repeat
  781. TIdStackLocalAddressIPv4.Create(AAddresses, TranslateTInAddrToString(LPAdrPtr^[Li]^, Id_IPv4), '', ''); // TODO: SubNetMask and BroadcastIP
  782. Inc(Li);
  783. until LPAdrPtr^[Li] = nil;
  784. finally
  785. AAddresses.EndUpdate;
  786. end;
  787. end;
  788. end;
  789. Id_PF_INET6: begin
  790. LPAdr6Ptr := PAPIn6Addr(LAHost^.h_addr_list);
  791. Li := 0;
  792. if LPAdr6Ptr^[Li] <> nil then begin
  793. AAddresses.BeginUpdate;
  794. try
  795. repeat
  796. TIdStackLocalAddressIPv6.Create(AAddresses, TranslateTInAddrToString(LPAdr6Ptr^[Li]^, Id_IPv6));
  797. Inc(Li);
  798. until LPAdr6Ptr^[Li] = nil;
  799. finally
  800. AAddresses.EndUpdate;
  801. end;
  802. end;
  803. end;
  804. end;
  805. {$ENDIF}
  806. end;
  807. function TIdStackLinux.HostByAddress(const AAddress: string;
  808. const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION): string;
  809. var
  810. LAddr: sockaddr_in6;
  811. LSize: UInt32;
  812. LHostName : array[0..NI_MAXHOST] of TIdAnsiChar;
  813. {$IFDEF USE_MARSHALLED_PTRS}
  814. LHostNamePtr: TPtrWrapper;
  815. {$ENDIF}
  816. LRet : Integer;
  817. LHints: AddrInfo; //The T is no omission - that's what I found in the header
  818. LAddrInfo: PAddrInfo;
  819. begin
  820. FillChar(LAddr, SizeOf(LAddr), 0);
  821. case AIPVersion of
  822. Id_IPv4: begin
  823. with Psockaddr(@LAddr)^ do begin
  824. sin_family := Id_PF_INET4;
  825. TranslateStringToTInAddr(AAddress, sin_addr, Id_IPv4);
  826. end;
  827. LSize := SizeOf(sockaddr);
  828. end;
  829. Id_IPv6: begin
  830. with LAddr do begin
  831. sin6_family := Id_PF_INET6;
  832. TranslateStringToTInAddr(AAddress, sin6_addr, Id_IPv6);
  833. end;
  834. LSize := SizeOf(sockaddr_in6);
  835. end;
  836. else begin
  837. LSize := 0; // avoid warning
  838. IPVersionUnsupported;
  839. end;
  840. end;
  841. FillChar(LHostName[0],Length(LHostName),0);
  842. {$IFDEF USE_MARSHALLED_PTRS}
  843. LHostNamePtr := TPtrWrapper.Create(@LHostName[0]);
  844. {$ENDIF}
  845. LRet := getnameinfo(Psockaddr(@LAddr)^, LSize,
  846. {$IFDEF USE_MARSHALLED_PTRS}
  847. LHostNamePtr.ToPointer
  848. {$ELSE}
  849. LHostName
  850. {$ENDIF},
  851. NI_MAXHOST,nil,0,NI_NAMEREQD );
  852. if LRet <> 0 then begin
  853. if LRet = EAI_SYSTEM then begin
  854. RaiseLastOSError;
  855. end else begin
  856. raise EIdReverseResolveError.CreateFmt(RSReverseResolveError, [AAddress, gai_strerror(LRet), LRet]);
  857. end;
  858. end;
  859. {
  860. IMPORTANT!!!
  861. getnameinfo can return either results from a numeric to text conversion or
  862. results from a DNS reverse lookup. Someone could make a malicous PTR record
  863. such as
  864. 1.0.0.127.in-addr.arpa. IN PTR 10.1.1.1
  865. and trick a caller into beleiving the socket address is 10.1.1.1 instead of
  866. 127.0.0.1. If there is a numeric host in LAddr, than this is the case and
  867. we disregard the result and raise an exception.
  868. }
  869. FillChar(LHints,SizeOf(LHints),0);
  870. LHints.ai_socktype := SOCK_DGRAM; //*dummy*/
  871. LHints.ai_flags := AI_NUMERICHOST;
  872. if getaddrinfo(
  873. {$IFDEF USE_MARSHALLED_PTRS}
  874. LHostNamePtr.ToPointer
  875. {$ELSE}
  876. LHostName
  877. {$ENDIF},
  878. '0', LHints, LAddrInfo) = 0 then
  879. begin
  880. freeaddrinfo(LAddrInfo^);
  881. Result := '';
  882. raise EIdMaliciousPtrRecord.Create(RSMaliciousPtrRecord);
  883. end;
  884. {$IFDEF USE_MARSHALLED_PTRS}
  885. Result := TMarshal.ReadStringAsAnsi(LHostNamePtr);
  886. {$ELSE}
  887. Result := String(LHostName);
  888. {$ENDIF}
  889. (* JMB: I left this in here just in case someone
  890. complains, but the other code works on all
  891. linux systems for all addresses and is thread-safe
  892. variables for it:
  893. Host: PHostEnt;
  894. LAddr: u_long;
  895. Id_IPv4: begin
  896. // GetHostByAddr is thread-safe in Linux.
  897. // It might not be safe in Solaris or BSD Unix
  898. LAddr := inet_addr(PAnsiChar(AAddress));
  899. Host := GetHostByAddr(@LAddr,SizeOf(LAddr),AF_INET);
  900. if (Host <> nil) then begin
  901. Result := Host^.h_name;
  902. end else begin
  903. RaiseSocketError(h_errno);
  904. end;
  905. end;
  906. *)
  907. end;
  908. function TIdStackLinux.WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer;
  909. begin
  910. Result := Libc.shutdown(ASocket, AHow);
  911. end;
  912. procedure TIdStackLinux.Disconnect(ASocket: TIdStackSocketHandle);
  913. begin
  914. // Windows uses Id_SD_Send, Linux should use Id_SD_Both
  915. WSShutdown(ASocket, Id_SD_Both);
  916. // SO_LINGER is false - socket may take a little while to actually close after this
  917. WSCloseSocket(ASocket);
  918. end;
  919. procedure TIdStackLinux.GetPeerName(ASocket: TIdStackSocketHandle;
  920. var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
  921. var
  922. LiSize: UInt32;
  923. LAddr: sockaddr_in6;
  924. begin
  925. LiSize := SizeOf(LAddr);
  926. CheckForSocketError(Libc.getpeername(ASocket, Psockaddr(@LAddr)^, LiSize));
  927. case LAddr.sin6_family of
  928. Id_PF_INET4: begin
  929. with Psockaddr(@LAddr6)^ do begin
  930. VIP := TranslateTInAddrToString(sin_addr, Id_IPv4);
  931. VPort := ntohs(sin_port);
  932. end;
  933. VIPVersion := Id_IPV4;
  934. end;
  935. Id_PF_INET6: begin
  936. with LAddr do begin
  937. VIP := TranslateTInAddrToString(sin6_addr, Id_IPv6);
  938. VPort := Ntohs(sin6_port);
  939. end;
  940. VIPVersion := Id_IPV6;
  941. end;
  942. else begin
  943. IPVersionUnsupported;
  944. end;
  945. end;
  946. end;
  947. procedure TIdStackLinux.GetSocketName(ASocket: TIdStackSocketHandle;
  948. var VIP: string; var VPort: TIdPort; var VIPVersion: TIdIPVersion);
  949. var
  950. LiSize: UInt32;
  951. LAddr: sockaddr_in6;
  952. begin
  953. LiSize := SizeOf(LAddr);
  954. CheckForSocketError(Libc.getsockname(ASocket, Psockaddr(@LAddr)^, LiSize));
  955. case LAddr.sin6_family of
  956. Id_PF_INET4: begin
  957. with Psockaddr(@LAddr6)^ do begin
  958. VIP := TranslateTInAddrToString(sin_addr, Id_IPv4);
  959. VPort := ntohs(sin_port);
  960. end;
  961. VIPVersion := Id_IPV4;
  962. end;
  963. Id_PF_INET6: begin
  964. with LAddr do begin
  965. VIP := TranslateTInAddrToString(sin6_addr, Id_IPv6);
  966. VPort := ntohs(sin6_port);
  967. end;
  968. VIPVersion := Id_IPV6;
  969. end;
  970. else begin
  971. IPVersionUnsupported;
  972. end;
  973. end;
  974. end;
  975. function TIdStackLinux.WouldBlock(const AResult: Integer): Boolean;
  976. begin
  977. // using if-else instead of in..range because EAGAIN and EWOULDBLOCK
  978. // have often the same value and so FPC might report a range error
  979. Result := (AResult = Id_WSAEAGAIN) or
  980. (AResult = Id_WSAEWOULDBLOCK) or
  981. (AResult = Id_WSAEINPROGRESS);
  982. end;
  983. function TIdStackLinux.SupportsIPv4: Boolean;
  984. begin
  985. //In Windows, this does something else. It checks the LSP's installed.
  986. Result := CheckIPVersionSupport(Id_IPv4);
  987. end;
  988. function TIdStackLinux.SupportsIPv6: Boolean;
  989. begin
  990. //In Windows, this does something else. It checks the LSP's installed.
  991. Result := CheckIPVersionSupport(Id_IPv6);
  992. end;
  993. function TIdStackLinux.CheckIPVersionSupport(const AIPVersion: TIdIPVersion): Boolean;
  994. var
  995. LTmpSocket: TIdStackSocketHandle;
  996. begin
  997. // TODO: an alternative would be to check for the existance of the '/proc/net/if_inet6' kernel pseudo-file
  998. LTmpSocket := WSSocket(IdIPFamily[AIPVersion], Id_SOCK_STREAM, Id_IPPROTO_IP );
  999. Result := LTmpSocket <> Id_INVALID_SOCKET;
  1000. if Result then begin
  1001. WSCloseSocket(LTmpSocket);
  1002. end;
  1003. end;
  1004. procedure TIdStackLinux.WriteChecksum(s: TIdStackSocketHandle;
  1005. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  1006. const APort: TIdPort; const AIPVersion: TIdIPVersion);
  1007. begin
  1008. case AIPVersion of
  1009. Id_IPv4 : CopyTIdUInt16(HostToLittleEndian(CalcCheckSum(VBuffer)), VBuffer, AOffset);
  1010. Id_IPv6 : WriteChecksumIPv6(s, VBuffer, AOffset, AIP, APort);
  1011. else
  1012. IPVersionUnsupported;
  1013. end;
  1014. end;
  1015. procedure TIdStackLinux.WriteChecksumIPv6(s: TIdStackSocketHandle;
  1016. var VBuffer: TIdBytes; const AOffset: Integer; const AIP: String;
  1017. const APort: TIdPort);
  1018. begin
  1019. //we simply request that the kernal write the checksum when the data
  1020. //is sent. All of the parameters required are because Windows is bonked
  1021. //because it doesn't have the IPV6CHECKSUM socket option meaning we have
  1022. //to querry the network interface in TIdStackWindows -- yuck!!
  1023. SetSocketOption(s, IPPROTO_IPV6, IPV6_CHECKSUM, AOffset);
  1024. end;
  1025. function TIdStackLinux.IOControl(const s: TIdStackSocketHandle;
  1026. const cmd: UInt32; var arg: UInt32): Integer;
  1027. begin
  1028. Result := ioctl(s, cmd, @arg);
  1029. end;
  1030. { TIdSocketListLinux }
  1031. type
  1032. TIdSocketListLinux = class (TIdSocketList)
  1033. protected
  1034. FCount: integer;
  1035. FFDSet: TFDSet;
  1036. //
  1037. class function FDSelect(AReadSet: PFDSet; AWriteSet: PFDSet; AExceptSet: PFDSet;
  1038. const ATimeout: Integer = IdTimeoutInfinite): integer;
  1039. function GetItem(AIndex: Integer): TIdStackSocketHandle; override;
  1040. public
  1041. procedure Add(AHandle: TIdStackSocketHandle); override;
  1042. procedure Remove(AHandle: TIdStackSocketHandle); override;
  1043. function Count: Integer; override;
  1044. procedure Clear; override;
  1045. function Clone: TIdSocketList; override;
  1046. function ContainsSocket(AHandle: TIdStackSocketHandle): boolean; override;
  1047. procedure GetFDSet(var VSet: TFDSet);
  1048. procedure SetFDSet(var VSet: TFDSet);
  1049. class function Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
  1050. AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  1051. function SelectRead(const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1052. override;
  1053. function SelectReadList(var VSocketList: TIdSocketList;
  1054. const ATimeout: Integer = IdTimeoutInfinite): Boolean; override;
  1055. End;//TIdSocketList
  1056. procedure TIdSocketListLinux.Add(AHandle: TIdStackSocketHandle);
  1057. begin
  1058. Lock;
  1059. try
  1060. if not FD_ISSET(AHandle, FFDSet) then begin
  1061. if AHandle >= __FD_SETSIZE{MaxLongint} then begin
  1062. raise EIdStackSetSizeExceeded.Create(RSSetSizeExceeded);
  1063. end;
  1064. FD_SET(AHandle, FFDSet);
  1065. Inc(FCount);
  1066. end;
  1067. finally
  1068. Unlock;
  1069. end;
  1070. end;//
  1071. procedure TIdSocketListLinux.Clear;
  1072. begin
  1073. Lock;
  1074. try
  1075. FD_ZERO(FFDSet);
  1076. FCount := 0;
  1077. finally
  1078. Unlock;
  1079. end;
  1080. end;
  1081. function TIdSocketListLinux.ContainsSocket(
  1082. AHandle: TIdStackSocketHandle): boolean;
  1083. begin
  1084. Lock;
  1085. try
  1086. Result := FD_ISSET(AHandle, FFDSet);
  1087. finally
  1088. Unlock;
  1089. end;
  1090. end;
  1091. function TIdSocketListLinux.Count: Integer;
  1092. begin
  1093. Lock;
  1094. try
  1095. Result := FCount;
  1096. finally
  1097. Unlock;
  1098. end;
  1099. end;//
  1100. class function TIdSocketListLinux.FDSelect(AReadSet, AWriteSet,
  1101. AExceptSet: PFDSet; const ATimeout: Integer): integer;
  1102. var
  1103. LTime: TTimeVal;
  1104. LTimePtr: PTimeVal;
  1105. begin
  1106. if ATimeout = IdTimeoutInfinite then begin
  1107. LTimePtr := nil;
  1108. end else begin
  1109. LTime.tv_sec := ATimeout div 1000;
  1110. LTime.tv_usec := (ATimeout mod 1000) * 1000;
  1111. LTimePtr := @LTime;
  1112. end;
  1113. // TODO: calculate the actual nfds value based on the Sets provided...
  1114. // TODO: use poll() instead of select() to remove limit on how many sockets can be queried
  1115. Result := Libc.select({__FD_SETSIZE}MaxLongint, AReadSet, AWriteSet, AExceptSet, LTimePtr);
  1116. end;
  1117. procedure TIdSocketListLinux.GetFDSet(var VSet: TFDSet);
  1118. begin
  1119. Lock;
  1120. try
  1121. VSet := FFDSet;
  1122. finally
  1123. Unlock;
  1124. end;
  1125. end;
  1126. function TIdSocketListLinux.GetItem(AIndex: Integer): TIdStackSocketHandle;
  1127. var
  1128. LIndex, i: Integer;
  1129. begin
  1130. Result := 0;
  1131. Lock;
  1132. try
  1133. LIndex := 0;
  1134. //? use FMaxHandle div x
  1135. for i:= 0 to __FD_SETSIZE - 1 do begin
  1136. if FD_ISSET(i, FFDSet) then begin
  1137. if LIndex = AIndex then begin
  1138. Result := i;
  1139. Break;
  1140. end;
  1141. Inc(LIndex);
  1142. end;
  1143. end;
  1144. finally
  1145. Unlock;
  1146. end;
  1147. end;//
  1148. procedure TIdSocketListLinux.Remove(AHandle: TIdStackSocketHandle);
  1149. begin
  1150. Lock;
  1151. try
  1152. if FD_ISSET(AHandle, FFDSet) then begin
  1153. Dec(FCount);
  1154. FD_CLR(AHandle, FFDSet);
  1155. end;
  1156. finally
  1157. Unlock;
  1158. end;
  1159. end;//
  1160. function TIdStackLinux.WSTranslateSocketErrorMsg(const AErr: Integer): string;
  1161. begin
  1162. //we override this function for the herr constants that
  1163. //are returned by the DNS functions
  1164. case AErr of
  1165. Libc.HOST_NOT_FOUND: Result := RSStackHOST_NOT_FOUND;
  1166. Libc.TRY_AGAIN: Result := RSStackTRY_AGAIN;
  1167. Libc.NO_RECOVERY: Result := RSStackNO_RECOVERY;
  1168. Libc.NO_DATA: Result := RSStackNO_DATA;
  1169. else
  1170. Result := inherited WSTranslateSocketErrorMsg(AErr);
  1171. end;
  1172. end;
  1173. procedure TIdSocketListLinux.SetFDSet(var VSet: TFDSet);
  1174. begin
  1175. Lock;
  1176. try
  1177. FFDSet := VSet;
  1178. finally
  1179. Unlock;
  1180. end;
  1181. end;
  1182. class function TIdSocketListLinux.Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
  1183. AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1184. var
  1185. LReadSet: TFDSet;
  1186. LWriteSet: TFDSet;
  1187. LExceptSet: TFDSet;
  1188. LPReadSet: PFDSet;
  1189. LPWriteSet: PFDSet;
  1190. LPExceptSet: PFDSet;
  1191. procedure ReadSet(AList: TIdSocketList; var ASet: TFDSet; var APSet: PFDSet);
  1192. begin
  1193. if AList <> nil then begin
  1194. TIdSocketListLinux(AList).GetFDSet(ASet);
  1195. APSet := @ASet;
  1196. end else begin
  1197. APSet := nil;
  1198. end;
  1199. end;
  1200. begin
  1201. ReadSet(AReadList, LReadSet, LPReadSet);
  1202. ReadSet(AWriteList, LWriteSet, LPWriteSet);
  1203. ReadSet(AExceptList, LExceptSet, LPExceptSet);
  1204. //
  1205. Result := FDSelect(LPReadSet, LPWriteSet, LPExceptSet, ATimeout) >0;
  1206. //
  1207. if AReadList <> nil then begin
  1208. TIdSocketListLinux(AReadList).SetFDSet(LReadSet);
  1209. end;
  1210. if AWriteList <> nil then begin
  1211. TIdSocketListLinux(AWriteList).SetFDSet(LWriteSet);
  1212. end;
  1213. if AExceptList <> nil then begin
  1214. TIdSocketListLinux(AExceptList).SetFDSet(LExceptSet);
  1215. end;
  1216. end;
  1217. function TIdSocketListLinux.SelectRead(const ATimeout: Integer): Boolean;
  1218. var
  1219. LSet: TFDSet;
  1220. begin
  1221. Lock;
  1222. try
  1223. LSet := FFDSet;
  1224. // select() updates this structure on return,
  1225. // so we need to copy it each time we need it
  1226. finally
  1227. Unlock;
  1228. end;
  1229. Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
  1230. end;
  1231. function TIdSocketListLinux.SelectReadList(var VSocketList: TIdSocketList;
  1232. const ATimeout: Integer = IdTimeoutInfinite): Boolean;
  1233. var
  1234. LSet: TFDSet;
  1235. begin
  1236. Lock;
  1237. try
  1238. LSet := FFDSet;
  1239. // select() updates this structure on return,
  1240. // so we need to copy it each time we need it
  1241. finally
  1242. Unlock;
  1243. end;
  1244. Result := FDSelect(@LSet, nil, nil, ATimeout) > 0;
  1245. if Result then begin
  1246. if VSocketList = nil then begin
  1247. VSocketList := TIdSocketList.CreateSocketList;
  1248. end;
  1249. TIdSocketListLinux(VSocketList).SetFDSet(LSet);
  1250. end;
  1251. end;
  1252. procedure TIdStackLinux.SetBlocking(ASocket: TIdStackSocketHandle;
  1253. const ABlocking: Boolean);
  1254. var
  1255. LFlags: Integer;
  1256. //LValue: UInt32;
  1257. begin
  1258. LFlags := CheckForSocketError(Libc.fcntl(ASocket, F_GETFL, 0));
  1259. if ABlocking then begin
  1260. LFlags := LFlags and not O_NONBLOCK;
  1261. end else begin
  1262. LFlags := LFlags or O_NONBLOCK;
  1263. end;
  1264. CheckForSocketError(Libc.fcntl(ASocket, F_SETFL, LFlags));
  1265. {
  1266. LValue := UInt32(not ABlocking);
  1267. CheckForSocketError(Libc.ioctl(ASocket, FIONBIO, @LValue));
  1268. }
  1269. end;
  1270. (*
  1271. Why did I remove this again?
  1272. 1) it sends SIGPIPE even if the socket is created with the no-sigpipe bit set
  1273. that could be solved by blocking sigpipe within this thread
  1274. This is probably a bug in the Linux kernel, but we could work around it
  1275. by blocking that signal for the time of sending the file (just get the
  1276. sigprocmask, see if pipe bit is set, if not set it and remove again after
  1277. sending the file)
  1278. But the more serious reason is another one, which exists in Windows too:
  1279. 2) I think that ServeFile is misdesigned:
  1280. ServeFile does not raise an exception if it didn't send all the bytes.
  1281. Now what happens if I have OnExecute assigned like this
  1282. AThread.Connection.ServeFile('...', True); // <-- true to send via kernel
  1283. is that it will return 0, but notice that in this case I didn't ask for the
  1284. result. Net effect is that the thread will loop in OnExecute even if the
  1285. socket is long gone. This doesn't fit Indy semantics at all, exceptions are
  1286. always raised if the remote end disconnects. Even if I would do
  1287. AThread.Connection.ServeFile('...', False);
  1288. then it would raise an exception.
  1289. I think this is a big flaw in the design of the ServeFile function.
  1290. Maybe GServeFile should only return the bytes sent, but then
  1291. TCPConnection.ServeFile() should raise an exception if GServeFile didn't
  1292. send all the bytes.
  1293. JM Berg, 2002-09-09
  1294. function ServeFile(ASocket: TIdStackSocketHandle; AFileName: string): UInt32;
  1295. var
  1296. LFileHandle: integer;
  1297. offset: integer;
  1298. stat: _stat;
  1299. begin
  1300. LFileHandle := open(PAnsiChar(AFileName), O_RDONLY);
  1301. try
  1302. offset := 0;
  1303. fstat(LFileHandle, stat);
  1304. Result := sendfile(ASocket, LFileHandle, offset, stat.st_size);
  1305. //** if Result = UInt32(-1) then RaiseLastOSError;
  1306. finally libc.__close(LFileHandle); end;
  1307. end;
  1308. *)
  1309. function TIdSocketListLinux.Clone: TIdSocketList;
  1310. begin
  1311. Result := TIdSocketListLinux.Create;
  1312. try
  1313. Lock;
  1314. try
  1315. TIdSocketListLinux(Result).SetFDSet(FFDSet);
  1316. finally
  1317. Unlock;
  1318. end;
  1319. except
  1320. Result.Free;
  1321. raise;
  1322. end;
  1323. end;
  1324. initialization
  1325. GSocketListClass := TIdSocketListLinux;
  1326. end.