IdStackLinux.pas 45 KB

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