FtpSocketStream.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743
  1. using System;
  2. using System.IO;
  3. using System.Net.Sockets;
  4. using System.Net.Security;
  5. using System.Security.Authentication;
  6. using System.Security.Cryptography.X509Certificates;
  7. using System.Threading;
  8. using System.Collections.Generic;
  9. using System.Diagnostics;
  10. namespace System.Net.FtpClient {
  11. /// <summary>
  12. /// Event fired if a bad SSL certificate is encountered. This even is used internally; if you
  13. /// don't have a specific reason for using it you are probably looking for FtpSslValidation.
  14. /// </summary>
  15. /// <param name="stream"></param>
  16. /// <param name="e"></param>
  17. public delegate void FtpSocketStreamSslValidation(FtpSocketStream stream, FtpSslValidationEventArgs e);
  18. /// <summary>
  19. /// Event args for the FtpSslValidationError delegate
  20. /// </summary>
  21. public class FtpSslValidationEventArgs : EventArgs {
  22. X509Certificate m_certificate = null;
  23. /// <summary>
  24. /// The certificate to be validated
  25. /// </summary>
  26. public X509Certificate Certificate {
  27. get {
  28. return m_certificate;
  29. }
  30. set {
  31. m_certificate = value;
  32. }
  33. }
  34. X509Chain m_chain = null;
  35. /// <summary>
  36. /// The certificate chain
  37. /// </summary>
  38. public X509Chain Chain {
  39. get {
  40. return m_chain;
  41. }
  42. set {
  43. m_chain = value;
  44. }
  45. }
  46. SslPolicyErrors m_policyErrors = SslPolicyErrors.None;
  47. /// <summary>
  48. /// Validation errors, if any.
  49. /// </summary>
  50. public SslPolicyErrors PolicyErrors {
  51. get {
  52. return m_policyErrors;
  53. }
  54. set {
  55. m_policyErrors = value;
  56. }
  57. }
  58. bool m_accept = false;
  59. /// <summary>
  60. /// Gets or sets a value indicating if this certificate should be accepted. The default
  61. /// value is false. If the certificate is not accepted, an AuthenticationException will
  62. /// be thrown.
  63. /// </summary>
  64. public bool Accept {
  65. get {
  66. return m_accept;
  67. }
  68. set {
  69. m_accept = value;
  70. }
  71. }
  72. }
  73. /// <summary>
  74. /// Stream class used for talking. Used by FtpClient, extended by FtpDataStream
  75. /// </summary>
  76. public class FtpSocketStream : Stream, IDisposable {
  77. /// <summary>
  78. /// Used for tacking read/write activity on the socket
  79. /// to determine if Poll() should be used to test for
  80. /// socket conenctivity. The socket in this class will
  81. /// not know it has been disconnected if the remote host
  82. /// closes the connection first. Using Poll() avoids
  83. /// the exception that would be thrown when trying to
  84. /// read or write to the disconnected socket.
  85. /// </summary>
  86. private DateTime m_lastActivity = DateTime.Now;
  87. private Socket m_socket = null;
  88. /// <summary>
  89. /// The socket used for talking
  90. /// </summary>
  91. protected Socket Socket {
  92. get {
  93. return m_socket;
  94. }
  95. private set {
  96. m_socket = value;
  97. }
  98. }
  99. int m_socketPollInterval = 15000;
  100. /// <summary>
  101. /// Gets or sets the length of time in miliseconds
  102. /// that must pass since the last socket activity
  103. /// before calling Poll() on the socket to test for
  104. /// connectivity. Setting this interval too low will
  105. /// have a negative impact on perfomance. Setting this
  106. /// interval to 0 disables Poll()'ing all together.
  107. /// The default value is 15 seconds.
  108. /// </summary>
  109. public int SocketPollInterval {
  110. get { return m_socketPollInterval; }
  111. set { m_socketPollInterval = value; }
  112. }
  113. /// <summary>
  114. /// Gets the number of available bytes on the socket, 0 if the
  115. /// socket has not been initalized. This property is used internally
  116. /// by FtpClient in an effort to detect disconnections and gracefully
  117. /// reconnect the control connection.
  118. /// </summary>
  119. internal int SocketDataAvailable {
  120. get {
  121. if (m_socket != null)
  122. return m_socket.Available;
  123. return 0;
  124. }
  125. }
  126. /// <summary>
  127. /// Gets a value indicating if this socket stream is connected
  128. /// </summary>
  129. public bool IsConnected {
  130. get {
  131. try {
  132. if (m_socket == null)
  133. return false;
  134. if (!m_socket.Connected) {
  135. Close();
  136. return false;
  137. }
  138. if (!CanRead || !CanWrite) {
  139. Close();
  140. return false;
  141. }
  142. if (m_socketPollInterval > 0 && DateTime.Now.Subtract(m_lastActivity).TotalMilliseconds > m_socketPollInterval) {
  143. FtpTrace.WriteLine("Testing connectivity using Socket.Poll()...");
  144. if (m_socket.Poll(500000, SelectMode.SelectRead) && m_socket.Available == 0) {
  145. Close();
  146. return false;
  147. }
  148. }
  149. }
  150. catch (SocketException sockex) {
  151. Close();
  152. FtpTrace.WriteLine("FtpSocketStream.IsConnected: Caught and discarded SocketException while testing for connectivity: {0}", sockex.ToString());
  153. return false;
  154. }
  155. catch (IOException ioex) {
  156. Close();
  157. FtpTrace.WriteLine("FtpSocketStream.IsConnected: Caught and discarded IOException while testing for connectivity: {0}", ioex.ToString());
  158. return false;
  159. }
  160. return true;
  161. }
  162. }
  163. /// <summary>
  164. /// Gets a value indicating if encryption is being used
  165. /// </summary>
  166. public bool IsEncrypted {
  167. get {
  168. return m_sslStream != null;
  169. }
  170. }
  171. NetworkStream m_netStream = null;
  172. /// <summary>
  173. /// The non-encrypted stream
  174. /// </summary>
  175. private NetworkStream NetworkStream {
  176. get {
  177. return m_netStream;
  178. }
  179. set {
  180. m_netStream = value;
  181. }
  182. }
  183. SslStream m_sslStream = null;
  184. /// <summary>
  185. /// The encrypted stream
  186. /// </summary>
  187. private SslStream SslStream {
  188. get {
  189. return m_sslStream;
  190. }
  191. set {
  192. m_sslStream = value;
  193. }
  194. }
  195. /// <summary>
  196. /// Underlying stream, could be a NetworkStream or SslStream
  197. /// </summary>
  198. protected Stream BaseStream {
  199. get {
  200. if (m_sslStream != null)
  201. return m_sslStream;
  202. else if (m_netStream != null)
  203. return m_netStream;
  204. return null;
  205. }
  206. }
  207. /// <summary>
  208. /// Gets a value indicating if this stream can be read
  209. /// </summary>
  210. public override bool CanRead {
  211. get {
  212. if (m_netStream != null)
  213. return m_netStream.CanRead;
  214. return false;
  215. }
  216. }
  217. /// <summary>
  218. /// Gets a value indicating if this stream if seekable
  219. /// </summary>
  220. public override bool CanSeek {
  221. get {
  222. return false;
  223. }
  224. }
  225. /// <summary>
  226. /// Gets a value indicating if this stream can be written to
  227. /// </summary>
  228. public override bool CanWrite {
  229. get {
  230. if (m_netStream != null)
  231. return m_netStream.CanWrite;
  232. return false;
  233. }
  234. }
  235. /// <summary>
  236. /// Gets the length of the stream
  237. /// </summary>
  238. public override long Length {
  239. get {
  240. return 0;
  241. }
  242. }
  243. /// <summary>
  244. /// Gets the current position of the stream. Trying to
  245. /// set this property throws an InvalidOperationException()
  246. /// </summary>
  247. public override long Position {
  248. get {
  249. if (BaseStream != null)
  250. return BaseStream.Position;
  251. return 0;
  252. }
  253. set {
  254. throw new InvalidOperationException();
  255. }
  256. }
  257. event FtpSocketStreamSslValidation m_sslvalidate = null;
  258. /// <summary>
  259. /// Event is fired when a SSL certificate needs to be validated
  260. /// </summary>
  261. public event FtpSocketStreamSslValidation ValidateCertificate {
  262. add {
  263. m_sslvalidate += value;
  264. }
  265. remove {
  266. m_sslvalidate -= value;
  267. }
  268. }
  269. int m_readTimeout = Timeout.Infinite;
  270. /// <summary>
  271. /// Gets or sets the amount of time to wait for a read operation to complete. Default
  272. /// value is Timeout.Infinite.
  273. /// </summary>
  274. public override int ReadTimeout {
  275. get {
  276. return m_readTimeout;
  277. }
  278. set {
  279. m_readTimeout = value;
  280. }
  281. }
  282. int m_connectTimeout = 30000;
  283. /// <summary>
  284. /// Gets or sets the length of time miliseconds to wait
  285. /// for a connection succeed before giving up. The default
  286. /// is 30000 (30 seconds).
  287. /// </summary>
  288. public int ConnectTimeout {
  289. get {
  290. return m_connectTimeout;
  291. }
  292. set {
  293. m_connectTimeout = value;
  294. }
  295. }
  296. /// <summary>
  297. /// Gets the local end point of the socket
  298. /// </summary>
  299. public IPEndPoint LocalEndPoint {
  300. get {
  301. if (m_socket == null)
  302. return null;
  303. return (IPEndPoint)m_socket.LocalEndPoint;
  304. }
  305. }
  306. /// <summary>
  307. /// Gets the remote end point of the socket
  308. /// </summary>
  309. public IPEndPoint RemoteEndPoint {
  310. get {
  311. if (m_socket == null)
  312. return null;
  313. return (IPEndPoint)m_socket.RemoteEndPoint;
  314. }
  315. }
  316. /// <summary>
  317. /// Fires the SSL certificate validation event
  318. /// </summary>
  319. /// <param name="certificate">Certificate being validated</param>
  320. /// <param name="chain">Certificate chain</param>
  321. /// <param name="errors">Policy errors if any</param>
  322. /// <returns>True if it was accepted, false otherwise</returns>
  323. protected bool OnValidateCertificate(X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) {
  324. FtpSocketStreamSslValidation evt = m_sslvalidate;
  325. if (evt != null) {
  326. FtpSslValidationEventArgs e = new FtpSslValidationEventArgs() {
  327. Certificate = certificate,
  328. Chain = chain,
  329. PolicyErrors = errors,
  330. Accept = (errors == SslPolicyErrors.None)
  331. };
  332. evt(this, e);
  333. return e.Accept;
  334. }
  335. // if the event was not handled then only accept
  336. // the certificate if there were no validation errors
  337. return (errors == SslPolicyErrors.None);
  338. }
  339. /// <summary>
  340. /// Throws an InvalidOperationException
  341. /// </summary>
  342. /// <param name="offset">Ignored</param>
  343. /// <param name="origin">Ignored</param>
  344. /// <returns></returns>
  345. public override long Seek(long offset, SeekOrigin origin) {
  346. throw new InvalidOperationException();
  347. }
  348. /// <summary>
  349. /// Throws an InvalidOperationException
  350. /// </summary>
  351. /// <param name="value">Ignored</param>
  352. public override void SetLength(long value) {
  353. throw new InvalidOperationException();
  354. }
  355. /// <summary>
  356. /// Flushes the stream
  357. /// </summary>
  358. public override void Flush() {
  359. if (!IsConnected)
  360. throw new InvalidOperationException("The FtpSocketStream object is not connected.");
  361. if (BaseStream == null)
  362. throw new InvalidOperationException("The base stream of the FtpSocketStream object is null.");
  363. BaseStream.Flush();
  364. }
  365. /// <summary>
  366. /// Bypass the stream and read directly off the socket.
  367. /// </summary>
  368. /// <param name="buffer">The buffer to read into</param>
  369. /// <returns>The number of bytes read</returns>
  370. internal int RawSocketRead(byte[] buffer) {
  371. int read = 0;
  372. if (m_socket != null && m_socket.Connected) {
  373. read = m_socket.Receive(buffer, buffer.Length, 0);
  374. }
  375. return read;
  376. }
  377. /// <summary>
  378. /// Reads data from the stream
  379. /// </summary>
  380. /// <param name="buffer">Buffer to read into</param>
  381. /// <param name="offset">Where in the buffer to start</param>
  382. /// <param name="count">Number of bytes to be read</param>
  383. /// <returns></returns>
  384. public override int Read(byte[] buffer, int offset, int count) {
  385. IAsyncResult ar = null;
  386. if (BaseStream == null)
  387. return 0;
  388. m_lastActivity = DateTime.Now;
  389. ar = BaseStream.BeginRead(buffer, offset, count, null, null);
  390. if (!ar.AsyncWaitHandle.WaitOne(m_readTimeout, true)) {
  391. Close();
  392. throw new TimeoutException("Timed out trying to read data from the socket stream!");
  393. }
  394. return BaseStream.EndRead(ar);
  395. }
  396. /// <summary>
  397. /// Reads a line from the socket
  398. /// </summary>
  399. /// <returns>A line from the stream, null if there is nothing to read</returns>
  400. public string ReadLine(System.Text.Encoding encoding) {
  401. List<byte> data = new List<byte>();
  402. byte[] buf = new byte[1];
  403. string line = null;
  404. while (Read(buf, 0, buf.Length) > 0) {
  405. data.Add(buf[0]);
  406. if ((char)buf[0] == '\n') {
  407. line = encoding.GetString(data.ToArray()).Trim('\r', '\n');
  408. break;
  409. }
  410. }
  411. return line;
  412. }
  413. /// <summary>
  414. /// Writes data to the stream
  415. /// </summary>
  416. /// <param name="buffer">Buffer to write to stream</param>
  417. /// <param name="offset">Where in the buffer to start</param>
  418. /// <param name="count">Number of bytes to be read</param>
  419. public override void Write(byte[] buffer, int offset, int count) {
  420. if (BaseStream == null)
  421. return;
  422. BaseStream.Write(buffer, offset, count);
  423. m_lastActivity = DateTime.Now;
  424. }
  425. /// <summary>
  426. /// Writes a line to the stream using the specified encoding
  427. /// </summary>
  428. /// <param name="encoding">Encoding used for writing the line</param>
  429. /// <param name="buf">The data to write</param>
  430. public void WriteLine(System.Text.Encoding encoding, string buf) {
  431. byte[] data;
  432. data = encoding.GetBytes(string.Format("{0}\r\n", buf));
  433. Write(data, 0, data.Length);
  434. }
  435. /// <summary>
  436. /// Disposes the stream
  437. /// </summary>
  438. public new void Dispose() {
  439. FtpTrace.WriteLine("Disposing FtpSocketStream...");
  440. Close();
  441. }
  442. /// <summary>
  443. /// Disconnects from server
  444. /// </summary>
  445. public override void Close() {
  446. if (m_socket != null) {
  447. try {
  448. if (m_socket.Connected) {
  449. ////
  450. // Calling Shutdown() with mono causes an
  451. // exception if the remote host closed first
  452. //m_socket.Shutdown(SocketShutdown.Both);
  453. m_socket.Close();
  454. }
  455. #if !NET2
  456. m_socket.Dispose();
  457. #endif
  458. }
  459. catch (SocketException ex) {
  460. FtpTrace.WriteLine("Caught and discarded a SocketException while cleaning up the Socket: {0}", ex.ToString());
  461. }
  462. finally {
  463. m_socket = null;
  464. }
  465. }
  466. if (m_netStream != null) {
  467. try {
  468. m_netStream.Dispose();
  469. }
  470. catch (IOException ex) {
  471. FtpTrace.WriteLine("Caught and discarded an IOException while cleaning up the NetworkStream: {0}", ex.ToString());
  472. }
  473. finally {
  474. m_netStream = null;
  475. }
  476. }
  477. if (m_sslStream != null) {
  478. try {
  479. m_sslStream.Dispose();
  480. }
  481. catch (IOException ex) {
  482. FtpTrace.WriteLine("Caught and discarded an IOException while cleaning up the SslStream: {0}", ex.ToString());
  483. }
  484. finally {
  485. m_sslStream = null;
  486. }
  487. }
  488. }
  489. /// <summary>
  490. /// Sets socket options on the underlying socket
  491. /// </summary>
  492. /// <param name="level">SocketOptionLevel</param>
  493. /// <param name="name">SocketOptionName</param>
  494. /// <param name="value">SocketOptionValue</param>
  495. public void SetSocketOption(SocketOptionLevel level, SocketOptionName name, bool value) {
  496. if (m_socket == null)
  497. throw new InvalidOperationException("The underlying socket is null. Have you established a connection?");
  498. m_socket.SetSocketOption(level, name, value);
  499. }
  500. /// <summary>
  501. /// Connect to the specified host
  502. /// </summary>
  503. /// <param name="host">The host to connect to</param>
  504. /// <param name="port">The port to connect to</param>
  505. /// <param name="ipVersions">Internet Protocol versions to support durring the connection phase</param>
  506. public void Connect(string host, int port, FtpIpVersion ipVersions) {
  507. IAsyncResult ar = null;
  508. IPAddress[] addresses = Dns.GetHostAddresses(host);
  509. if (ipVersions == 0)
  510. throw new ArgumentException("The ipVersions parameter must contain at least 1 flag.");
  511. for (int i = 0; i < addresses.Length; i++) {
  512. #if DEBUG
  513. FtpTrace.WriteLine("{0}: {1}", addresses[i].AddressFamily.ToString(), addresses[i].ToString());
  514. #endif
  515. // we don't need to do this check unless
  516. // a particular version of IP has been
  517. // omitted so we won't.
  518. if (ipVersions != FtpIpVersion.ANY) {
  519. switch (addresses[i].AddressFamily) {
  520. case AddressFamily.InterNetwork:
  521. if ((ipVersions & FtpIpVersion.IPv4) != FtpIpVersion.IPv4) {
  522. #if DEBUG
  523. FtpTrace.WriteLine("SKIPPED!");
  524. #endif
  525. continue;
  526. }
  527. break;
  528. case AddressFamily.InterNetworkV6:
  529. if ((ipVersions & FtpIpVersion.IPv6) != FtpIpVersion.IPv6) {
  530. #if DEBUG
  531. FtpTrace.WriteLine("SKIPPED!");
  532. #endif
  533. continue;
  534. }
  535. break;
  536. }
  537. }
  538. m_socket = new Socket(addresses[i].AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  539. ar = m_socket.BeginConnect(addresses[i], port, null, null);
  540. if (!ar.AsyncWaitHandle.WaitOne(m_connectTimeout, true)) {
  541. Close();
  542. // check to see if we're out of addresses
  543. // and if we are throw a TimeoutException
  544. if (i + 1 == addresses.Length)
  545. throw new TimeoutException("Timed out trying to connect!");
  546. }
  547. else {
  548. m_socket.EndConnect(ar);
  549. // we got a connection, break out
  550. // of the loop.
  551. break;
  552. }
  553. }
  554. // make sure that we actually connected to
  555. // one of the addresses returned from GetHostAddresses()
  556. if (m_socket == null || !m_socket.Connected) {
  557. Close();
  558. throw new IOException("Failed to connect to host.");
  559. }
  560. m_netStream = new NetworkStream(m_socket);
  561. m_lastActivity = DateTime.Now;
  562. }
  563. /// <summary>
  564. /// Activates SSL on this stream using default protocols. Fires the ValidateCertificate event.
  565. /// If this event is not handled and there are SslPolicyErrors present, the certificate will
  566. /// not be accepted.
  567. /// </summary>
  568. /// <param name="targethost">The host to authenticate the certiciate against</param>
  569. public void ActivateEncryption(string targethost) {
  570. ActivateEncryption(targethost, null, SslProtocols.Default);
  571. }
  572. /// <summary>
  573. /// Activates SSL on this stream using default protocols. Fires the ValidateCertificate event.
  574. /// If this event is not handled and there are SslPolicyErrors present, the certificate will
  575. /// not be accepted.
  576. /// </summary>
  577. /// <param name="targethost">The host to authenticate the certiciate against</param>
  578. /// <param name="clientCerts">A collection of client certificates to use when authenticating the SSL stream</param>
  579. public void ActivateEncryption(string targethost, X509CertificateCollection clientCerts) {
  580. ActivateEncryption(targethost, clientCerts, SslProtocols.Default);
  581. }
  582. /// <summary>
  583. /// Activates SSL on this stream using the specified protocols. Fires the ValidateCertificate event.
  584. /// If this event is not handled and there are SslPolicyErrors present, the certificate will
  585. /// not be accepted.
  586. /// </summary>
  587. /// <param name="targethost">The host to authenticate the certiciate against</param>
  588. /// <param name="clientCerts">A collection of client certificates to use when authenticating the SSL stream</param>
  589. /// <param name="sslProtocols">A bitwise parameter for supported encryption protocols.</param>
  590. public void ActivateEncryption(string targethost, X509CertificateCollection clientCerts, SslProtocols sslProtocols) {
  591. if (!IsConnected)
  592. throw new InvalidOperationException("The FtpSocketStream object is not connected.");
  593. if (m_netStream == null)
  594. throw new InvalidOperationException("The base network stream is null.");
  595. if (m_sslStream != null)
  596. throw new InvalidOperationException("SSL Encryption has already been enabled on this stream.");
  597. try {
  598. DateTime auth_start;
  599. TimeSpan auth_time_total;
  600. m_sslStream = new SslStream(NetworkStream, true, new RemoteCertificateValidationCallback(
  601. delegate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
  602. return OnValidateCertificate(certificate, chain, sslPolicyErrors);
  603. }));
  604. auth_start = DateTime.Now;
  605. m_sslStream.AuthenticateAsClient(targethost, clientCerts, sslProtocols, true);
  606. auth_time_total = DateTime.Now.Subtract(auth_start);
  607. FtpTrace.WriteLine("Time to activate encryption: {0}h {1}m {2}s, Total Seconds: {3}.",
  608. auth_time_total.Hours,
  609. auth_time_total.Minutes,
  610. auth_time_total.Seconds,
  611. auth_time_total.TotalSeconds);
  612. }
  613. catch (AuthenticationException ex) {
  614. // authentication failed and in addition it left our
  615. // ssl stream in an unsuable state so cleanup needs
  616. // to be done and the exception can be re-thrown for
  617. // handling down the chain.
  618. Close();
  619. throw ex;
  620. }
  621. }
  622. /// <summary>
  623. /// Instructs this stream to listen for connections on the specified address and port
  624. /// </summary>
  625. /// <param name="address">The address to listen on</param>
  626. /// <param name="port">The port to listen on</param>
  627. public void Listen(IPAddress address, int port) {
  628. if (!IsConnected) {
  629. if (m_socket == null)
  630. m_socket = new Socket(address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  631. m_socket.Bind(new IPEndPoint(address, port));
  632. m_socket.Listen(1);
  633. }
  634. }
  635. /// <summary>
  636. /// Accepts a connection from a listening socket
  637. /// </summary>
  638. public void Accept() {
  639. if (m_socket != null)
  640. m_socket = m_socket.Accept();
  641. }
  642. /// <summary>
  643. /// Asynchronously accepts a connection from a listening socket
  644. /// </summary>
  645. /// <param name="callback"></param>
  646. /// <param name="state"></param>
  647. /// <returns></returns>
  648. public IAsyncResult BeginAccept(AsyncCallback callback, object state) {
  649. if (m_socket != null)
  650. return m_socket.BeginAccept(callback, state);
  651. return null;
  652. }
  653. /// <summary>
  654. /// Completes a BeginAccept() operation
  655. /// </summary>
  656. /// <param name="ar">IAsyncResult returned from BeginAccept</param>
  657. public void EndAccept(IAsyncResult ar) {
  658. if (m_socket != null) {
  659. m_socket = m_socket.EndAccept(ar);
  660. m_netStream = new NetworkStream(m_socket);
  661. }
  662. }
  663. }
  664. }