IdStackLinux.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. { $HDR$}
  2. {**********************************************************************}
  3. { Unit archived using Team Coherence }
  4. { Team Coherence is Copyright 2002 by Quality Software Components }
  5. { }
  6. { For further information / comments, visit our WEB site at }
  7. { http://www.TeamCoherence.com }
  8. {**********************************************************************}
  9. {}
  10. { $Log: 10347: IdStackLinux.pas
  11. {
  12. { Rev 1.1 4/20/03 1:50:42 PM RLebeau
  13. { Updated TranslateStringToTInAddr() to use new TIdSTack::GetIPInfo() method.
  14. }
  15. {
  16. { Rev 1.0 2002.11.12 10:53:30 PM czhower
  17. }
  18. unit IdStackLinux;
  19. interface
  20. uses
  21. Classes,
  22. Libc,
  23. IdStack, IdStackConsts;
  24. type
  25. TIdSocketListLinux = class (TIdSocketList)
  26. protected
  27. FFDSet: TFDSet;
  28. FMaxHandle: TIdStackSocketHandle;
  29. //
  30. function GetItem(AIndex: Integer): TIdStackSocketHandle; override;
  31. public
  32. procedure Add(AHandle: TIdStackSocketHandle); override;
  33. procedure Remove(AHandle: TIdStackSocketHandle); override;
  34. function Count: Integer; override;
  35. End;//TIdSocketList
  36. TIdStackLinux = class(TIdStack)
  37. protected
  38. procedure PopulateLocalAddresses; override;
  39. function WSGetLocalAddress: string; override;
  40. function WSGetLocalAddresses: TStrings; override;
  41. public
  42. function TInAddrToString(var AInAddr): string; override;
  43. procedure TranslateStringToTInAddr(AIP: string; var AInAddr); override;
  44. function WSTranslateSocketErrorMsg(const AErr: integer): string; override;
  45. //
  46. function WSAccept(ASocket: TIdStackSocketHandle; var VIP: string; var VPort: Integer)
  47. : TIdStackSocketHandle; override;
  48. function WSBind(ASocket: TIdStackSocketHandle; const AFamily: Integer;
  49. const AIP: string; const APort: Integer): Integer; override;
  50. function WSCloseSocket(ASocket: TIdStackSocketHandle): Integer; override;
  51. function WSConnect(const ASocket: TIdStackSocketHandle; const AFamily: Integer;
  52. const AIP: string; const APort: Integer): Integer; override;
  53. function WSGetHostByAddr(const AAddress: string): string; override;
  54. function WSGetHostByName(const AHostName: string): string; override;
  55. function WSGetHostName: string; override;
  56. function WSGetLastError: Integer; override;
  57. function WSGetServByName(const AServiceName: string): Integer; override;
  58. function WSGetServByPort(const APortNumber: Integer): TStrings; override;
  59. function WSGetSockOpt(ASocket: TIdStackSocketHandle; Alevel, AOptname: Integer; AOptval: PChar;
  60. var AOptlen: Integer): Integer; override;
  61. procedure WSGetPeerName(ASocket: TIdStackSocketHandle;
  62. var VFamily: Integer; var VIP: string; var VPort: Integer); override;
  63. procedure WSGetSockName(ASocket: TIdStackSocketHandle;
  64. var VFamily: Integer; var VIP: string; var VPort: Integer); override;
  65. function WSHToNs(AHostShort: Word): Word; override;
  66. function WSListen(ASocket: TIdStackSocketHandle; ABackLog: Integer): Integer; override;
  67. function WSNToHs(ANetShort: Word): Word; override;
  68. function WSHToNL(AHostLong: LongWord): LongWord; override;
  69. function WSNToHL(ANetLong: LongWord): LongWord; override;
  70. function WSRecv(ASocket: TIdStackSocketHandle; var ABuffer; const ABufferLength, AFlags: Integer)
  71. : integer; override;
  72. function WSRecvFrom(const ASocket: TIdStackSocketHandle; var ABuffer;
  73. const ALength, AFlags: Integer; var VIP: string; var VPort: Integer): Integer; override;
  74. function WSSelect(ARead, AWrite, AErrors: TList; ATimeout: Integer): Integer; override;
  75. function WSSend(ASocket: TIdStackSocketHandle; var ABuffer;
  76. const ABufferLength, AFlags: Integer): Integer; override;
  77. function WSSendTo(ASocket: TIdStackSocketHandle; var ABuffer;
  78. const ABufferLength, AFlags: Integer; const AIP: string; const APort: integer): Integer;
  79. override;
  80. function WSSetSockOpt(ASocket: TIdStackSocketHandle; ALevel, AOptName: Integer; AOptVal: PChar;
  81. AOptLen: Integer): Integer; override;
  82. function WSSocket(AFamily, AStruct, AProtocol: Integer): TIdStackSocketHandle; override;
  83. function WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer; override;
  84. end;
  85. TLinger = record
  86. l_onoff: Word;
  87. l_linger: Word;
  88. end;
  89. TIdLinger = TLinger;
  90. implementation
  91. uses
  92. IdException,
  93. IdGlobal, IdResourceStrings,
  94. SysUtils;
  95. const
  96. Id_MSG_NOSIGNAL = MSG_NOSIGNAL;
  97. Id_WSAEPIPE = EPIPE;
  98. function TIdStackLinux.TInAddrToString(var AInAddr): string;
  99. begin
  100. with TInAddr(AInAddr).S_un_b do begin
  101. Result := IntToStr(Ord(s_b1)) + '.' + IntToStr(Ord(s_b2)) + '.' + IntToStr(Ord(s_b3)) + '.' {Do not Localize}
  102. + IntToStr(Ord(s_b4));
  103. end;
  104. end;
  105. function TIdStackLinux.WSAccept(ASocket: TIdStackSocketHandle;
  106. var VIP: string; var VPort: Integer): TIdStackSocketHandle;
  107. var
  108. i: Cardinal;
  109. LAddr: SockAddr;
  110. begin
  111. i := SizeOf(LAddr);
  112. Result := Accept(ASocket, @LAddr, @i);
  113. if Result <> SOCKET_ERROR then begin
  114. VIP := TInAddrToString(LAddr.sin_addr);
  115. VPort := NToHs(LAddr.sin_port);
  116. end else begin
  117. if GetLastError = EBADF then begin
  118. SetLastError(EINTR);
  119. end;
  120. end;
  121. end;
  122. function TIdStackLinux.WSBind(ASocket: TIdStackSocketHandle;
  123. const AFamily: Integer; const AIP: string;
  124. const APort: Integer): Integer;
  125. var
  126. Addr: SockAddr;
  127. begin
  128. Addr.sin_family := AFamily;
  129. if length(AIP) = 0 then begin
  130. Addr.sin_addr.s_addr := INADDR_ANY;
  131. end else begin
  132. TranslateStringToTInAddr(AIP, Addr.sin_addr);
  133. end;
  134. Addr.sin_port := HToNs(APort);
  135. Result := Bind(ASocket, addr, SizeOf(Addr));
  136. end;
  137. function TIdStackLinux.WSCloseSocket(ASocket: TIdStackSocketHandle): Integer;
  138. begin
  139. Result := Libc.__close(ASocket);
  140. end;
  141. function TIdStackLinux.WSConnect(const ASocket: TIdStackSocketHandle;
  142. const AFamily: Integer; const AIP: string;
  143. const APort: Integer): Integer;
  144. var
  145. Addr: SockAddr;
  146. begin
  147. Addr.sin_family := AFamily;
  148. TranslateStringToTInAddr(AIP, Addr.sin_addr);
  149. Addr.sin_port := HToNs(APort);
  150. Result := Connect(ASocket, Addr, SizeOf(Addr));
  151. end;
  152. function TIdStackLinux.WSGetHostByName(const AHostName: string): string;
  153. var
  154. pa: PChar;
  155. sa: TInAddr;
  156. Host: PHostEnt;
  157. begin
  158. //we don't use _r functions because they are depreciated and the non-r's are safe in Linux.
  159. //They could be problematic in Sun Solorus and BSD.
  160. Host := GethostByName(PChar(AHostName));
  161. if (Host <> nil) then
  162. begin
  163. pa := Host^.h_addr_list^;
  164. sa.S_un_b.s_b1 := Ord(pa[0]);
  165. sa.S_un_b.s_b2 := Ord(pa[1]);
  166. sa.S_un_b.s_b3 := Ord(pa[2]);
  167. sa.S_un_b.s_b4 := Ord(pa[3]);
  168. Result := TInAddrToString(sa);
  169. end
  170. else
  171. begin
  172. RaiseSocketError(h_errno);
  173. end;
  174. end;
  175. function TIdStackLinux.WSGetHostName: string;
  176. begin
  177. SetLength(Result, 250);
  178. GetHostName(PChar(Result), Length(Result));
  179. Result := String(PChar(Result));
  180. end;
  181. function TIdStackLinux.WSListen(ASocket: TIdStackSocketHandle;
  182. ABackLog: Integer): Integer;
  183. begin
  184. Result := Listen(ASocket, ABacklog);
  185. end;
  186. function TIdStackLinux.WSRecv(ASocket: TIdStackSocketHandle; var ABuffer;
  187. const ABufferLength, AFlags: Integer): integer;
  188. begin
  189. Result := Recv(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
  190. end;
  191. function TIdStackLinux.WSRecvFrom(const ASocket: TIdStackSocketHandle;
  192. var ABuffer; const ALength, AFlags: Integer; var VIP: string;
  193. var VPort: Integer): Integer;
  194. var
  195. iSize: Cardinal;
  196. Addr: sockaddr;
  197. begin
  198. iSize := SizeOf(Addr);
  199. Result := RecvFrom(ASocket, ABuffer, ALength, AFlags or Id_MSG_NOSIGNAL, @Addr, @iSize);
  200. VIP := TInAddrToString(Addr.sin_addr);
  201. VPort := NToHs(Addr.sin_port);
  202. end;
  203. function TIdStackLinux.WSSelect(ARead, AWrite, AErrors: TList; ATimeout: Integer): Integer;
  204. var
  205. tmTo: TTimeVal;
  206. FDRead, FDWrite, FDError: TFDSet;
  207. LMaxHandle: TIdStackSocketHandle;
  208. { TODO : Optimize and cache these routines }
  209. procedure GetFDSet(AList: TList; var ASet: TFDSet);
  210. var
  211. i: Integer;
  212. begin
  213. if assigned( AList ) then
  214. begin
  215. AList.Clear;
  216. for i := 0 to __FD_SETSIZE - 1 do
  217. begin
  218. if FD_ISSET(i, ASet) then
  219. begin
  220. AList.Add(TObject(i));
  221. end;
  222. end;
  223. end;
  224. end;
  225. procedure SetFDSet(AList: TList; var ASet: TFDSet);
  226. var
  227. i: integer;
  228. begin
  229. if AList <> nil then begin
  230. if AList.Count > __FD_SETSIZE then begin
  231. raise EIdSetSizeExceeded.Create(RSSetSizeExceeded);
  232. end;
  233. for i := 0 to AList.Count - 1 do begin
  234. FD_SET(TIdStackSocketHandle(AList[i]), ASet);
  235. LMaxHandle := Max(LMaxHandle, TIdStackSocketHandle(AList[i]) + 1);
  236. end;
  237. end;
  238. end;
  239. begin
  240. LMaxHandle := 0;
  241. FD_ZERO(FDRead);
  242. FD_ZERO(FDWrite);
  243. FD_ZERO(FDError);
  244. SetFDSet(ARead, FDRead);
  245. SetFDSet(AWrite, FDWrite);
  246. SetFDSet(AErrors, FDError);
  247. if ATimeout = IdTimeoutInfinite then begin
  248. Result := Select(LMaxHandle, @FDRead, @FDWrite, @FDError, nil);
  249. end else begin
  250. tmTo.tv_sec := ATimeout div 1000;
  251. tmTo.tv_usec := (ATimeout mod 1000) * 1000;
  252. Result := Select(LMaxHandle, @FDRead, @FDWrite, @FDError, @tmTO);
  253. end;
  254. GetFDSet(ARead, FDRead);
  255. GetFDSet(AWrite, FDWrite);
  256. GetFDSet(AErrors, FDError);
  257. end;
  258. function TIdStackLinux.WSSend(ASocket: TIdStackSocketHandle;
  259. var ABuffer; const ABufferLength, AFlags: Integer): Integer;
  260. begin
  261. Result := Send(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL);
  262. end;
  263. function TIdStackLinux.WSSendTo(ASocket: TIdStackSocketHandle;
  264. var ABuffer; const ABufferLength, AFlags: Integer; const AIP: string;
  265. const APort: integer): Integer;
  266. var
  267. Addr: SockAddr;
  268. begin
  269. FillChar(Addr, SizeOf(Addr), 0);
  270. with Addr do
  271. begin
  272. sin_family := Id_PF_INET;
  273. TranslateStringToTInAddr(AIP, sin_addr);
  274. sin_port := HToNs(APort);
  275. end;
  276. Result := SendTo(ASocket, ABuffer, ABufferLength, AFlags or Id_MSG_NOSIGNAL, Addr, SizeOf(Addr));
  277. end;
  278. function TIdStackLinux.WSSetSockOpt(ASocket: TIdStackSocketHandle;
  279. ALevel, AOptName: Integer; AOptVal: PChar; AOptLen: Integer): Integer;
  280. begin
  281. Result := SetSockOpt(ASocket, ALevel, AOptName, AOptVal, AOptLen);
  282. end;
  283. function TIdStackLinux.WSGetLastError: Integer;
  284. begin
  285. Result := System.GetLastError;
  286. if Result = Id_WSAEPIPE then
  287. begin
  288. Result := Id_WSAECONNRESET;
  289. end;
  290. end;
  291. function TIdStackLinux.WSSocket(AFamily, AStruct, AProtocol: Integer): TIdStackSocketHandle;
  292. begin
  293. Result := Socket(AFamily, AStruct, AProtocol);
  294. end;
  295. function TIdStackLinux.WSHToNs(AHostShort: Word): Word;
  296. begin
  297. Result := HToNs(AHostShort);
  298. end;
  299. function TIdStackLinux.WSNToHs(ANetShort: Word): Word;
  300. begin
  301. Result := NToHs(ANetShort);
  302. end;
  303. function TIdStackLinux.WSGetLocalAddresses: TStrings;
  304. begin
  305. if FLocalAddresses = nil then
  306. begin
  307. FLocalAddresses := TStringList.Create;
  308. end;
  309. PopulateLocalAddresses;
  310. Result := FLocalAddresses;
  311. end;
  312. function TIdStackLinux.WSGetServByName(const AServiceName: string): Integer;
  313. var
  314. ps: PServEnt;
  315. begin
  316. ps := GetServByName(PChar(AServiceName), nil);
  317. if ps <> nil then begin
  318. Result := Ntohs(ps^.s_port);
  319. end else begin
  320. try
  321. Result := StrToInt(AServiceName);
  322. except
  323. on EConvertError do raise EIdInvalidServiceName.CreateFmt(RSInvalidServiceName, [AServiceName]);
  324. end;
  325. end;
  326. end;
  327. function TIdStackLinux.WSGetServByPort(const APortNumber: Integer): TStrings;
  328. var
  329. ps: PServEnt;
  330. i: integer;
  331. p: array of PChar;
  332. begin
  333. Result := TStringList.Create;
  334. p := nil;
  335. try
  336. ps := GetServByPort(HToNs(APortNumber), nil);
  337. if ps <> nil then
  338. begin
  339. Result.Add(ps^.s_name);
  340. i := 0;
  341. p := pointer(ps^.s_aliases);
  342. while p[i] <> nil do
  343. begin
  344. Result.Add(PChar(p[i]));
  345. inc(i);
  346. end;
  347. end;
  348. except
  349. Result.Free;
  350. Result := nil;
  351. end;
  352. end;
  353. function TIdStackLinux.WSHToNL(AHostLong: LongWord): LongWord;
  354. begin
  355. Result := HToNL(AHostLong);
  356. end;
  357. function TIdStackLinux.WSNToHL(ANetLong: LongWord): LongWord;
  358. begin
  359. Result := NToHL(ANetLong);
  360. end;
  361. procedure TIdStackLinux.PopulateLocalAddresses;
  362. type
  363. TaPInAddr = Array[0..250] of PInAddr;
  364. PaPInAddr = ^TaPInAddr;
  365. var
  366. i: integer;
  367. AHost: PHostEnt;
  368. PAdrPtr: PaPInAddr;
  369. begin
  370. FLocalAddresses.Clear ;
  371. AHost := GetHostByName(PChar(WSGetHostName));
  372. if AHost = nil then
  373. begin
  374. CheckForSocketError(SOCKET_ERROR);
  375. end
  376. else
  377. begin
  378. PAdrPtr := PAPInAddr(AHost^.h_addr_list);
  379. i := 0;
  380. while PAdrPtr^[i] <> nil do
  381. begin
  382. FLocalAddresses.Add(TInAddrToString(PAdrPtr^[I]^));
  383. Inc(I);
  384. end;
  385. end;
  386. end;
  387. function TIdStackLinux.WSGetLocalAddress: string;
  388. begin
  389. Result := LocalAddresses[0];
  390. end;
  391. procedure TIdStackLinux.TranslateStringToTInAddr(AIP: string; var AInAddr);
  392. begin
  393. with TInAddr(AInAddr).S_un_b do
  394. begin
  395. if not GetIPInfo(AIP, @s_b1, @s_b2, @s_b3, @s_b4) then
  396. begin
  397. raise EIdInvalidIPAddress.CreateFmt(RSStackInvalidIP, [AIP]);
  398. end;
  399. end;
  400. end;
  401. function TIdStackLinux.WSGetHostByAddr(const AAddress: string): string;
  402. //GetHostByAddr is thread-safe in Linux. It might not be safe in Solorus or BSD Unix
  403. var
  404. Host: PHostEnt;
  405. LAddr: u_long;
  406. begin
  407. LAddr := inet_addr(PChar(AAddress));
  408. Host := GetHostByAddr(@LAddr,SizeOf(LAddr),AF_INET);
  409. if (Host <> nil) then
  410. begin
  411. Result := Host^.h_name;
  412. end
  413. else
  414. begin
  415. RaiseSocketError(h_errno);
  416. end;
  417. end;
  418. function TIdStackLinux.WSShutdown(ASocket: TIdStackSocketHandle; AHow: Integer): Integer;
  419. begin
  420. Result := Shutdown(ASocket, AHow);
  421. end;
  422. procedure TIdStackLinux.WSGetPeerName(ASocket: TIdStackSocketHandle;
  423. var VFamily: Integer; var VIP: string; var VPort: Integer);
  424. var
  425. i: Cardinal;
  426. LAddr: TSockAddrIn;
  427. begin
  428. i := SizeOf(LAddr);
  429. CheckForSocketError(GetPeerName(ASocket, LAddr, i));
  430. VFamily := LAddr.sin_family;
  431. VIP := TInAddrToString(LAddr.sin_addr);
  432. VPort := Ntohs(LAddr.sin_port);
  433. end;
  434. procedure TIdStackLinux.WSGetSockName(ASocket: TIdStackSocketHandle;
  435. var VFamily: Integer; var VIP: string; var VPort: Integer);
  436. var
  437. i: Cardinal;
  438. LAddr: TSockAddrIn;
  439. begin
  440. i := SizeOf(LAddr);
  441. CheckForSocketError(GetSockName(ASocket, LAddr, i));
  442. VFamily := LAddr.sin_family;
  443. VIP := TInAddrToString(LAddr.sin_addr);
  444. VPort := Ntohs(LAddr.sin_port);
  445. end;
  446. function TIdStackLinux.WSGetSockOpt(ASocket: TIdStackSocketHandle; Alevel, AOptname: Integer; AOptval: PChar; var AOptlen: Integer): Integer;
  447. begin
  448. Result := libc.GetSockOpt(ASocket, ALevel, AOptname, AOptval, Cardinal(AOptlen));
  449. end;
  450. { TIdSocketListLinux }
  451. procedure TIdSocketListLinux.Add(AHandle: TIdStackSocketHandle);
  452. Begin
  453. FD_SET(AHandle, FFDSet);
  454. FMaxHandle := Max(FMaxHandle, AHandle + 1);
  455. End;//
  456. function TIdSocketListLinux.Count: Integer;
  457. var
  458. I: Integer;
  459. Begin
  460. Result := 0;
  461. for i:= 0 to __FD_SETSIZE - 1 do begin //? use FMaxHandle div x
  462. if FD_ISSET(i, FFDSet) then begin
  463. inc(Result);
  464. end;
  465. end;
  466. End;//
  467. function TIdSocketListLinux.GetItem(AIndex: Integer): TIdStackSocketHandle;
  468. var
  469. LIndex, i: Integer;
  470. Begin
  471. Result := 0;
  472. LIndex := 0;
  473. for i:= 0 to __FD_SETSIZE - 1 do begin //? use FMaxHandle div x
  474. if FD_ISSET(i, FFDSet) then begin
  475. if LIndex = AIndex then begin
  476. Result := i;
  477. Break;
  478. end else begin
  479. inc(LIndex);
  480. end;
  481. end;//if item
  482. end;
  483. End;//
  484. procedure TIdSocketListLinux.Remove(AHandle: TIdStackSocketHandle);
  485. var
  486. i: Integer;
  487. Begin
  488. FD_CLR(AHandle, FFDSet);
  489. if AHandle+1 >= FMaxHandle then begin
  490. for i:=__FD_SETSIZE - 1 downto 0 do begin
  491. if FD_ISSET(i, FFDSet) then begin
  492. FMaxHandle := i + 1;
  493. Break;
  494. end;
  495. end;
  496. end;
  497. End;//
  498. function TIdStackLinux.WSTranslateSocketErrorMsg(
  499. const AErr: integer): string;
  500. //we override this function for the herr constants that
  501. //are returned by the DNS functions
  502. begin
  503. case AErr of
  504. libc.HOST_NOT_FOUND : Result := RSStackHOST_NOT_FOUND;
  505. libc.TRY_AGAIN : Result := RSStackTRY_AGAIN;
  506. libc.NO_RECOVERY : Result := RSStackNO_RECOVERY;
  507. libc.NO_DATA : Result := RSStackNO_DATA;
  508. else
  509. Result := inherited WSTranslateSocketErrorMsg(AErr);
  510. end;
  511. end;
  512. INITIALIZATION
  513. GSocketListClass := TIdSocketListLinux;
  514. end.