Merge pull request #896 from echampet/webresource
[mono.git] / mcs / class / System / Test / System.Net / FtpWebRequestTest.cs
1 //
2 // FtpWebRequestTest.cs - NUnit Test Cases for System.Net.FtpWebRequest
3 //
4 // Authors:
5 //      Carlos Alberto Cortez <calberto.cortez@gmail.com>
6 //      Gonzalo Paniagua Javier <gonzalo@novell.com>
7 //
8 // Copyright (c) 2006,2007,2008 Novell, Inc. (http://www.novell.com)
9 //
10 using NUnit.Framework;
11 using System;
12 using System.Collections.Generic;
13 using System.IO;
14 using System.Net;
15 using System.Net.Sockets;
16 using System.Text;
17 using System.Threading;
18
19 namespace MonoTests.System.Net 
20 {
21         [TestFixture]
22         public class FtpWebRequestTest
23         {
24                 FtpWebRequest defaultRequest;
25                 
26                 private string _tempDirectory;
27                 private string _tempFile;
28
29                 [SetUp]
30                 public void SetUp ()
31                 {
32                         _tempDirectory = Path.Combine (Path.GetTempPath (), "MonoTests.System.Net.FileWebRequestTest");
33                         _tempFile = Path.Combine (_tempDirectory, "FtpWebRequestTest.tmp");
34                         if (!Directory.Exists (_tempDirectory)) {
35                                 Directory.CreateDirectory (_tempDirectory);
36                         } else {
37                                 // ensure no files are left over from previous runs
38                                 string [] files = Directory.GetFiles (_tempDirectory, "*");
39                                 foreach (string file in files)
40                                         File.Delete (file);
41                         }
42                 }
43
44                 [TearDown]
45                 public void TearDown ()
46                 {
47                         if (Directory.Exists (_tempDirectory))
48                                 Directory.Delete (_tempDirectory, true);
49                 }
50
51                 [TestFixtureSetUp]
52                 public void Init ()
53                 {
54                         defaultRequest = (FtpWebRequest) WebRequest.Create ("ftp://www.contoso.com");
55                 }
56                 
57                 [Test]
58                 public void ContentLength ()
59                 {
60                         try {
61                                 long l = defaultRequest.ContentLength;
62                         } catch (NotSupportedException) {
63                                 Assert.Fail ("#1"); // Not overriden
64                         }
65
66                         try {
67                                 defaultRequest.ContentLength = 2;
68                         } catch (NotSupportedException) {
69                                 Assert.Fail ("#2"); // Not overriden
70                         }
71                 }
72
73                 [Test]
74                 public void ContentType ()
75                 {
76                         try {
77                                 string t = defaultRequest.ContentType;
78                                 Assert.Fail ("#1");
79                         } catch (NotSupportedException) {
80                         }
81
82                         try {
83                                 defaultRequest.ContentType = String.Empty;
84                                 Assert.Fail ("#2");
85                         } catch (NotSupportedException) {
86                         }
87                 }
88
89                 [Test]
90                 public void ContentOffset ()
91                 {
92                         try {
93                                 defaultRequest.ContentOffset = -2;
94                                 Assert.Fail ("#1");
95                         } catch (ArgumentOutOfRangeException) {
96                         }
97                 }
98
99                 [Test]
100                 public void Credentials ()
101                 {
102                         try {
103                                 defaultRequest.Credentials = null;
104                                 Assert.Fail ("#1");
105                         } catch (ArgumentNullException) {
106                         }
107
108                 }
109
110                 [Test]
111                 public void Method ()
112                 {
113                         try {
114                                 defaultRequest.Method = null;
115                                 Assert.Fail ("#1");
116                         } catch (ArgumentNullException) {
117                         }
118
119                         try {
120                                 defaultRequest.Method = String.Empty;
121                                 Assert.Fail ("#2");
122                         } catch (ArgumentException) {
123                         }
124
125                         try {
126                                 defaultRequest.Method = "WrongValue";
127                                 Assert.Fail ("#3");
128                         } catch (ArgumentException) {
129                         }
130                 }
131
132                 [Test]
133                 public void PreAuthenticate ()
134                 {
135                         try {
136                                 bool p = defaultRequest.PreAuthenticate;
137                                 Assert.Fail ("#1");
138                         } catch (NotSupportedException) {
139                         }
140
141                         try {
142                                 defaultRequest.PreAuthenticate = true;
143                         } catch (NotSupportedException) {
144                         }
145                 }
146
147                 [Test]
148                 public void ReadWriteTimeout ()
149                 {
150                         try {
151                                 defaultRequest.ReadWriteTimeout = -2;
152                                 Assert.Fail ("#2");
153                         } catch (ArgumentOutOfRangeException) {
154                         }
155                 }
156
157                 [Test]
158                 public void Timeout ()
159                 {
160                         try {
161                                 defaultRequest.Timeout = -2;
162                                 Assert.Fail ("#2");
163                         } catch (ArgumentOutOfRangeException) {
164                         }
165                 }
166                 
167                 [Test]
168                 public void DefaultValues ()
169                 {
170                         FtpWebRequest request = (FtpWebRequest) WebRequest.Create ("ftp://www.contoso.com");
171                         
172                         Assert.AreEqual (0, request.ContentOffset, "ContentOffset");
173                         Assert.AreEqual (false, request.EnableSsl, "EnableSsl");
174                         // FIXME: Disabled this one by now. KeepAlive is not well supported.
175                         // Assert.AreEqual (true, request.KeepAlive, "KeepAlive");
176                         Assert.AreEqual (WebRequestMethods.Ftp.DownloadFile, request.Method, "#1");
177                         Assert.AreEqual (300000, request.ReadWriteTimeout, "ReadWriteTimeout");
178                         Assert.IsNull (request.RenameTo, "RenameTo");
179                         Assert.AreEqual (true, request.UseBinary, "UseBinary");
180                         Assert.AreEqual (100000, request.Timeout, "Timeout");
181                         Assert.AreEqual (true, request.UsePassive, "UsePassive");
182                 }
183
184                 [Test]
185                 public void RenameTo ()
186                 {
187                         try {
188                                 defaultRequest.RenameTo = null;
189                                 Assert.Fail ("#1");
190                         } catch (ArgumentException) {
191                         }
192
193                         try {
194                                 defaultRequest.RenameTo = String.Empty;
195                                 Assert.Fail ("#2");
196                         } catch (ArgumentException) {
197                         }
198                 }
199
200                 [Test]
201                 public void UploadFile1 ()
202                 {
203                         ServerPut sp = new ServerPut ();
204                         sp.Start ();
205                         string uri = String.Format ("ftp://{0}:{1}/uploads/file.txt", sp.IPAddress, sp.Port);
206                         try {
207                                 FtpWebRequest ftp = (FtpWebRequest) WebRequest.Create (uri);
208                                 ftp.KeepAlive = false;
209                                 ftp.Timeout = 5000;
210                                 ftp.Method = WebRequestMethods.Ftp.UploadFile;
211                                 ftp.ContentLength = 10;
212                                 ftp.UseBinary = true;
213                                 Stream stream = ftp.GetRequestStream ();
214                                 for (int i = 0; i < 10; i++)
215                                         stream.WriteByte ((byte)i);
216                                 stream.Close ();
217                                 FtpWebResponse response = (FtpWebResponse) ftp.GetResponse ();
218                                 Assert.IsTrue ((int) response.StatusCode >= 200 && (int) response.StatusCode < 300, "UP#01");
219                                 Assert.AreEqual (10, sp.result.Count, "UP#02");
220                                 response.Close ();
221                         } catch (Exception) {
222                                 if (!String.IsNullOrEmpty (sp.Where))
223                                         throw new Exception (sp.Where);
224                                 throw;
225                         } finally {
226                                 sp.Stop ();
227                         }
228                 }
229
230                 [Test]
231                 public void UploadFile_WebClient ()
232                 {
233                         ServerPut sp = new ServerPut ();
234                         File.WriteAllText (_tempFile, "0123456789");
235                         sp.Start ();
236
237                         using (WebClient m_WebClient = new WebClient())
238                         {
239                                 string uri = String.Format ("ftp://{0}:{1}/uploads/file.txt", sp.IPAddress, sp.Port);
240                                 
241                                 m_WebClient.UploadFile(uri, _tempFile);
242                         }
243                         Assert.AreEqual (10, sp.result.Count, "WebClient/Ftp#01");
244             
245                         sp.Stop ();
246                 }
247
248                 [Test]
249                 public void DownloadFile1 ()
250                 {
251                         DownloadFile (new ServerDownload ());
252                 }
253
254                 void DownloadFile (ServerDownload sp)
255                 {
256                         sp.Start ();
257                         string uri = String.Format ("ftp://{0}:{1}/file.txt", sp.IPAddress, sp.Port);
258                         try {
259                                 FtpWebRequest ftp = (FtpWebRequest) WebRequest.Create (uri);
260                                 ftp.KeepAlive = false;
261                                 ftp.Timeout = 5000;
262                                 ftp.Method = WebRequestMethods.Ftp.DownloadFile;
263                                 ftp.UseBinary = true;
264                                 FtpWebResponse response = (FtpWebResponse) ftp.GetResponse ();
265                                 Assert.IsTrue ((int) response.StatusCode >= 100 && (int) response.StatusCode < 200, "DL#01");
266                                 using (Stream st = response.GetResponseStream ()) {
267                                 }
268                                 // This should be "220 Bye" or similar (no KeepAlive)
269                                 Assert.IsTrue ((int) response.StatusCode >= 200 && (int) response.StatusCode < 300, "DL#02");
270                                 response.Close ();
271                         } catch (Exception) {
272                                 if (!String.IsNullOrEmpty (sp.Where))
273                                         throw new Exception (sp.Where);
274                                 throw;
275                         } finally {
276                                 sp.Stop ();
277                         }
278                 }
279
280                 [Test]
281                 public void DownloadFile2 ()
282                 {
283                         // Some embedded FTP servers in Industrial Automation Hardware report
284                         // the PWD using backslashes, but allow forward slashes for CWD.
285                         DownloadFile (new ServerDownload (@"\Users\someuser", "/Users/someuser/"));
286                 }
287
288                 [Test]
289                 public void DeleteFile1 ()
290                 {
291                         ServerDeleteFile sp = new ServerDeleteFile ();
292                         sp.Start ();
293                         string uri = String.Format ("ftp://{0}:{1}/file.txt", sp.IPAddress, sp.Port);
294                         try {
295                                 FtpWebRequest ftp = (FtpWebRequest) WebRequest.Create (uri);
296                                 Console.WriteLine (ftp.RequestUri);
297                                 ftp.KeepAlive = false;
298                                 ftp.Timeout = 5000;
299                                 ftp.Method = WebRequestMethods.Ftp.DeleteFile;
300                                 ftp.UseBinary = true;
301                                 FtpWebResponse response = (FtpWebResponse) ftp.GetResponse ();
302                                 Assert.IsTrue ((int) response.StatusCode >= 200 && (int) response.StatusCode < 300, "DF#01");
303                                 response.Close ();
304                         } catch (Exception e) {
305                                 Console.WriteLine (e);
306                                 if (!String.IsNullOrEmpty (sp.Where))
307                                         throw new Exception (sp.Where);
308                                 throw;
309                         } finally {
310                                 sp.Stop ();
311                         }
312                 }
313
314                 [Test]
315                 public void ListDirectory1 ()
316                 {
317                         ServerListDirectory sp = new ServerListDirectory ();
318                         sp.Start ();
319                         string uri = String.Format ("ftp://{0}:{1}/somedir/", sp.IPAddress, sp.Port);
320                         try {
321                                 FtpWebRequest ftp = (FtpWebRequest) WebRequest.Create (uri);
322                                 Console.WriteLine (ftp.RequestUri);
323                                 ftp.KeepAlive = false;
324                                 ftp.Timeout = 5000;
325                                 ftp.Method = WebRequestMethods.Ftp.ListDirectoryDetails;
326                                 ftp.UseBinary = true;
327                                 using (FtpWebResponse response = (FtpWebResponse) ftp.GetResponse ()) {
328                                         StreamReader reader = new StreamReader (response.GetResponseStream ());
329                                         string result = reader.ReadToEnd ();
330                                         Assert.IsTrue ((int) response.StatusCode >= 200 && (int) response.StatusCode < 300, "DF#01");
331                                 }
332                         } catch (Exception e) {
333                                 Console.WriteLine (e);
334                                 if (!String.IsNullOrEmpty (sp.Where))
335                                         throw new Exception (sp.Where);
336                                 throw;
337                         } finally {
338                                 sp.Stop ();
339                         }
340                 }
341
342                 class ServerListDirectory : FtpServer {
343                         protected override void Run ()
344                         {
345                                 Socket client = control.Accept ();
346                                 NetworkStream ns = new NetworkStream (client, false);
347                                 StreamWriter writer = new StreamWriter (ns, Encoding.ASCII);
348                                 StreamReader reader = new StreamReader (ns, Encoding.UTF8);
349                                 if (!DoAnonymousLogin (writer, reader)) {
350                                         client.Close ();
351                                         return;
352                                 }
353
354                                 if (!DoInitialDialog (writer, reader, "/home/someuser", "/home/someuser/somedir/")) {
355                                         client.Close ();
356                                         return;
357                                 }
358
359                                 string str = reader.ReadLine ();
360                                 if (str != "PASV") {
361                                         Where = "PASV";
362                                         client.Close ();
363                                         return;
364                                 }
365
366                                 IPEndPoint end_data = (IPEndPoint) data.LocalEndPoint;
367                                 byte [] addr_bytes = end_data.Address.GetAddressBytes ();
368                                 byte [] port = new byte [2];
369                                 port[0] = (byte) ((end_data.Port >> 8) & 255);
370                                 port[1] = (byte) (end_data.Port & 255);
371                                 StringBuilder sb = new StringBuilder ("227 Passive (");
372                                 foreach (byte b in addr_bytes) {
373                                         sb.AppendFormat ("{0},", b);    
374                                 }
375                                 sb.AppendFormat ("{0},", port [0]);     
376                                 sb.AppendFormat ("{0})", port [1]);     
377                                 writer.WriteLine (sb.ToString ());
378                                 writer.Flush ();
379
380                                 str = reader.ReadLine ();
381                                 if (str != "LIST") {
382                                         Where = "LIST - '" + str + "'";
383                                         client.Close ();
384                                         return;
385                                 }
386                                 writer.WriteLine ("150 Here comes the directory listing");
387                                 writer.Flush ();
388
389                                 Socket data_cnc = data.Accept ();
390                                 byte [] dontcare = Encoding.ASCII.GetBytes ("drwxr-xr-x    2 ftp      ftp          4096 Oct 27 20:17 tests");
391                                 data_cnc.Send (dontcare, 1, SocketFlags.None);
392                                 data_cnc.Close ();
393                                 writer.WriteLine ("226 Directory send Ok");
394                                 writer.Flush ();
395                                 if (!EndConversation (writer, reader)) {
396                                         client.Close ();
397                                         return;
398                                 }
399                                 client.Close ();
400                         }
401                 }
402
403                 class ServerDeleteFile : FtpServer {
404                         protected override void Run ()
405                         {
406                                 Socket client = control.Accept ();
407                                 NetworkStream ns = new NetworkStream (client, false);
408                                 StreamWriter writer = new StreamWriter (ns, Encoding.ASCII);
409                                 StreamReader reader = new StreamReader (ns, Encoding.UTF8);
410                                 if (!DoAnonymousLogin (writer, reader)) {
411                                         client.Close ();
412                                         return;
413                                 }
414
415                                 if (!DoInitialDialog (writer, reader, "/home/someuser", "/home/someuser/")) {
416                                         client.Close ();
417                                         return;
418                                 }
419
420                                 string str = reader.ReadLine ();
421                                 if (str.Trim () != "DELE file.txt") {
422                                         Where = "DELE - " + str;
423                                         client.Close ();
424                                         return;
425                                 }
426                                 writer.WriteLine ("250 Delete operation successful");
427                                 writer.Flush ();
428                                 if (!EndConversation (writer, reader)) {
429                                         client.Close ();
430                                         return;
431                                 }
432                                 client.Close ();
433                         }
434                 }
435
436                 class ServerDownload : FtpServer {
437
438                         string Pwd, Cwd;
439
440                         public ServerDownload ()
441                                 : this (null, null)
442                         {
443                         }
444
445                         public ServerDownload (string pwd, string cwd)
446                         {
447                                 Pwd = pwd ?? "/home/someuser";
448                                 Cwd = cwd ?? "/home/someuser/";
449                         }
450
451                         protected override void Run ()
452                         {
453                                 Socket client = control.Accept ();
454                                 NetworkStream ns = new NetworkStream (client, false);
455                                 StreamWriter writer = new StreamWriter (ns, Encoding.ASCII);
456                                 StreamReader reader = new StreamReader (ns, Encoding.UTF8);
457                                 if (!DoAnonymousLogin (writer, reader)) {
458                                         client.Close ();
459                                         return;
460                                 }
461
462                                 if (!DoInitialDialog (writer, reader, Pwd, Cwd)) {
463                                         client.Close ();
464                                         return;
465                                 }
466
467                                 string str = reader.ReadLine ();
468                                 if (str != "PASV") {
469                                         Where = "PASV";
470                                         client.Close ();
471                                         return;
472                                 }
473
474                                 IPEndPoint end_data = (IPEndPoint) data.LocalEndPoint;
475                                 byte [] addr_bytes = end_data.Address.GetAddressBytes ();
476                                 byte [] port = new byte [2];
477                                 port[0] = (byte) ((end_data.Port >> 8) & 255);
478                                 port[1] = (byte) (end_data.Port & 255);
479                                 StringBuilder sb = new StringBuilder ("227 Passive (");
480                                 foreach (byte b in addr_bytes) {
481                                         sb.AppendFormat ("{0},", b);    
482                                 }
483                                 sb.AppendFormat ("{0},", port [0]);     
484                                 sb.AppendFormat ("{0})", port [1]);     
485                                 writer.WriteLine (sb.ToString ());
486                                 writer.Flush ();
487
488                                 str = reader.ReadLine ();
489                                 if (str != "RETR file.txt") {
490                                         Where = "RETR - " + str;
491                                         client.Close ();
492                                         return;
493                                 }
494                                 writer.WriteLine ("150 Opening BINARY mode data connection for blah (n bytes)");
495                                 writer.Flush ();
496
497                                 Socket data_cnc = data.Accept ();
498                                 byte [] dontcare = new byte [1];
499                                 data_cnc.Receive (dontcare, 1, SocketFlags.None);
500                                 data_cnc.Close ();
501                                 writer.WriteLine ("226 File send Ok");
502                                 writer.Flush ();
503                                 if (!EndConversation (writer, reader)) {
504                                         client.Close ();
505                                         return;
506                                 }
507                                 client.Close ();
508                         }
509                 }
510
511                 class ServerPut : FtpServer {
512                         public List<byte> result = new List<byte> ();
513                         
514                         protected override void Run ()
515                         {
516                                 Socket client = control.Accept ();
517                                 NetworkStream ns = new NetworkStream (client, false);
518                                 StreamWriter writer = new StreamWriter (ns, Encoding.ASCII);
519                                 StreamReader reader = new StreamReader (ns, Encoding.UTF8);
520                                 if (!DoAnonymousLogin (writer, reader)) {
521                                         client.Close ();
522                                         return;
523                                 }
524
525                                 if (!DoInitialDialog (writer, reader, "/home/someuser", "/home/someuser/uploads/")) {
526                                         client.Close ();
527                                         return;
528                                 }
529
530                                 string str = reader.ReadLine ();
531                                 if (str != "PASV") {
532                                         Where = "PASV";
533                                         client.Close ();
534                                         return;
535                                 }
536
537                                 IPEndPoint end_data = (IPEndPoint) data.LocalEndPoint;
538                                 byte [] addr_bytes = end_data.Address.GetAddressBytes ();
539                                 byte [] port = new byte [2];
540                                 port[0] = (byte) ((end_data.Port >> 8) & 255);
541                                 port[1] = (byte) (end_data.Port & 255);
542                                 StringBuilder sb = new StringBuilder ("227 Passive (");
543                                 foreach (byte b in addr_bytes) {
544                                         sb.AppendFormat ("{0},", b);    
545                                 }
546                                 sb.AppendFormat ("{0},", port [0]);     
547                                 sb.AppendFormat ("{0})", port [1]);     
548                                 writer.WriteLine (sb.ToString ());
549                                 writer.Flush ();
550
551                                 str = reader.ReadLine ();
552                                 if (str != "STOR file.txt") {
553                                         Where = "STOR - " + str;
554                                         client.Close ();
555                                         return;
556                                 }
557                                 writer.WriteLine ("150 Ok to send data");
558                                 writer.Flush ();
559
560                                 Socket data_cnc = data.Accept ();
561                                 var datastr = new NetworkStream (data_cnc, false);
562                                 int ch;
563                                 while ((ch = datastr.ReadByte ()) != -1){
564                                         result.Add ((byte)ch);
565
566                                 }
567                                 data_cnc.Close ();
568                                 writer.WriteLine ("226 File received Ok");
569                                 writer.Flush ();
570                                 if (!EndConversation (writer, reader)) {
571                                         client.Close ();
572                                         return;
573                                 }
574                                 client.Close ();
575                         }
576                 }
577
578                 abstract class FtpServer {
579                         protected Socket control;
580                         protected Socket data;
581                         protected ManualResetEvent evt;
582                         public string Where = "";
583
584                         public FtpServer ()
585                         {
586                                 control = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
587                                 control.Bind (new IPEndPoint (IPAddress.Loopback, 0));
588                                 control.Listen (1);
589                                 data = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
590                                 data.Bind (new IPEndPoint (IPAddress.Loopback, 0));
591                                 data.Listen (1);
592                         }
593
594                         public void Start ()
595                         {
596                                 evt = new ManualResetEvent (false);
597                                 Thread th = new Thread (new ThreadStart (Run));
598                                 th.Start ();
599                         }
600
601                         public void Stop ()
602                         {
603                                 evt.Set ();
604                                 data.Close ();
605                                 control.Close ();
606                         }
607
608                         // PWD, CWD and TYPE I (type could be moved out of here)
609                         protected bool DoInitialDialog (StreamWriter writer, StreamReader reader, string pwd, string cwd)
610                         {
611                                 string str = reader.ReadLine ();
612                                 if (!str.StartsWith ("OPTS utf8 on")) {
613                                         Where = "OPTS utf8 - " + str;
614                                         return false;
615                                 }
616                                 writer.WriteLine ("200 Always in UTF8 mode"); // vsftpd
617                                 writer.Flush ();
618                                 str = reader.ReadLine ();
619                                 if (!str.StartsWith ("PWD")) {
620                                         Where = "PWD - " + str;
621                                         return false;
622                                 }
623                                 writer.WriteLine ("257 \"{0}\"", pwd);
624                                 writer.Flush ();
625                                 str = reader.ReadLine ();
626                                 if (str != ("CWD " + cwd)) {
627                                         Where = "CWD - " + str;
628                                         return false;
629                                 }
630                                 writer.WriteLine ("250 Directory changed");
631                                 writer.Flush ();
632                                 str = reader.ReadLine ();
633                                 if (str != ("TYPE I")) {
634                                         Where = "TYPE - " + str;
635                                         return false;
636                                 }
637                                 writer.WriteLine ("200 Switching to binary mode");
638                                 writer.Flush ();
639                                 return true;
640                         }
641
642                         protected bool EndConversation (StreamWriter writer, StreamReader reader)
643                         {
644                                 string str = reader.ReadLine ();
645                                 if (str != "QUIT") {
646                                         Where = "QUIT";
647                                         return false;
648                                 }
649                                 writer.WriteLine ("220 Bye");
650                                 writer.Flush ();
651                                 Thread.Sleep (250);
652                                 return true;
653                         }
654
655                         protected bool DoAnonymousLogin (StreamWriter writer, StreamReader reader)
656                         {
657                                 writer.WriteLine ("220 Welcome to the jungle");
658                                 writer.Flush ();
659                                 string str = reader.ReadLine ();
660                                 if (!str.StartsWith ("USER ")) {
661                                         Where = "USER";
662                                         return false;
663                                 }
664                                 writer.WriteLine ("331 Say 'Mellon'");
665                                 writer.Flush ();
666                                 str = reader.ReadLine ();
667                                 if (!str.StartsWith ("PASS ")) {
668                                         Where = "PASS";
669                                         return false;
670                                 }
671                                 writer.WriteLine ("230 Logged in");
672                                 writer.Flush ();
673                                 return true;
674                         }
675                         
676                         public IPAddress IPAddress {
677                                 get { return ((IPEndPoint) control.LocalEndPoint).Address; }
678                         }
679                         
680                         public int Port {
681                                 get { return ((IPEndPoint) control.LocalEndPoint).Port; }
682                         }
683
684                         protected abstract void Run ();
685                 }
686         }
687 }
688
689