IdStackUnix.pas 42 KB

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