IdStackLinux.pas 44 KB

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