Mariya Atanasova's Blog

  • FtpWebRequest commands and how they work

    Currently, the user is allowed to specify the following commands: APPE, DELE, RETR, SIZE LIST, NLST, MKD, PWD. RMD, RENAME, STOR, and STOU. This is done by setting the FtpWebRequest.Metod property to one of the values of the WebRequestMetods.Ftp.Members type. The default one is RETR. For more details on this check our documentation:
    http://msdn.microsoft.com/en-us/library/system.net.webrequestmethods.ftp_members(VS.80).aspx
    and
    http://msdn.microsoft.com/en-us/library/system.net.webrequestmethods.ftp.aspx

    Example: FtpWebRequest.Method = WebRequesMethods.Ftp.ListDirectoryDetails will send the LIST ftp command on the wire and return you a detailed list of the contents of the specified folder.

    The result of executing the command on your ftp server will be in the ResponseStream returned by FtpWebResponse, except for the PWD, SIZE and MDTM methods. For the result of thos commands check the corresponding  FtpWebRequest properties as specified here:
    http://blogs.msdn.com/mariya/archive/2006/11/07/ftpwebrequest-and-the-pwd-size-and-mdtm-methods.aspx

  • Forcing HttpWebRequest to use Kerberos authentication

          First, let me state that I am not recommending to only use a certain type of authentication in your applcations. It is not secure to do so. However, in some cases, it is convenient to be able to do so -  mainly for verification or for debugging purposes.


    Example 1: You do not own the server and you are not sure what type of authentication it supports. By using the code snippet below you can verify whether or not it supports Kerberos / whether or not it supports NTLM, whether or not it supports Basic, etc, etc. Of course you can verify this using Netmon or Ethereal traces and looking at the WWW-Authenticate header value the server is sending, but I find this way easier.

    Example 2: You have a custom server which only supports a certain authentication type that you want to test.
    Example 3: You have an intermittent authentication error which you suspect only happens when a certain type of authentication is used (for example Kerberos) but you’re not sure.

     

          Now to the point: forcing HttpWebRequest to use a certain type of authentication like NTLM, Kerberos, Negotiate, Digest, or Basic is very easy – you can achive it by using AuthenticationManager.Unregister() method to unregister all the other authentication modules supported by HttpWebRequest. For example, if you want it to force it to use Kerberos only you unregister Basic, Digest, NTLM and Negotiate modules. If you want to force it to use NTLM only you unregister Basic, Digest, Negotiate and Kerberos, and so on.

     

     

    using System;

    using System.Net;

    using System.IO;

    using System.Text;

    using System.Collections;

    using Microsoft.Win32;

     

     

    namespace Mssc.Services.Authentication

    {

        class TestAuthentication

        {

     

            private static string username, password, domain, uri;

     

            // This method invoked when the user does not enter the required input parameters.

            private static void showusage()

            {

                Console.WriteLine("Attempts to authenticate to a URL");

                Console.WriteLine("\r\nUse one of the following:");

                Console.WriteLine("\URL username password domain");

                Console.WriteLine("\URL username password");

            }

     

            // Display registered authentication modules.

            private static void displayRegisteredModules()

            {

                IEnumerator registeredModules = AuthenticationManager.RegisteredModules;

                Console.WriteLine("\r\nThe following authentication modules are now registered with the system:");

                while (registeredModules.MoveNext())

                {

                    Console.WriteLine("\r \n Module : {0}", registeredModules.Current);

                    IAuthenticationModule currentAuthenticationModule = (IAuthenticationModule)registeredModules.Current;

                    Console.WriteLine("\t  CanPreAuthenticate : {0}", currentAuthenticationModule.CanPreAuthenticate);

                }

            }

     

            // The getPage method accesses the selected page and displays its content

            // on the console.

            private static void getPage(String url)

            {

                try

                {

                    // Create the Web request object.

                    HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);                            

                    req.Credentials = new NetworkCredential(username, password, domain);

                    req.Proxy = null;

     

                    // Issue the request.

                    HttpWebResponse result = (HttpWebResponse)req.GetResponse();

     

                    Console.WriteLine("\nAuthentication Succeeded:");

     

                    // Store the response.

                    Stream sData = result.GetResponseStream();

     

                    // Display the response.

                    displayPageContent(sData);

                }

                catch (WebException e)

                {

                    // Display any errors. In particular, display any protocol-related error.

                    if (e.Status == WebExceptionStatus.ProtocolError)

                    {

                        HttpWebResponse hresp = (HttpWebResponse)e.Response;

                        Console.WriteLine("\nAuthentication Failed, " + hresp.StatusCode);

                        Console.WriteLine("Status Code: " + (int)hresp.StatusCode);

                        Console.WriteLine("Status Description: " + hresp.StatusDescription);

                        return;

                    }

                    Console.WriteLine("Caught Exception: " + e.Message);

                    Console.WriteLine("Stack: " + e.StackTrace);

                }

            }

     

            // The displayPageContent method display the content of the

            // selected page.

            private static void displayPageContent(Stream ReceiveStream)

            {

                // Create an ASCII encoding object.

                Encoding ASCII = Encoding.ASCII;

     

                // Define the byte array to temporarily hold the current read bytes.

                Byte[] read = new Byte[512];

     

                Console.WriteLine("\r\nPage Content...\r\n");

     

                // Read the page content and display it on the console.

                // Read the first 512 bytes.

                int bytes = ReceiveStream.Read(read, 0, 512);

                while (bytes > 0)

                {

                    Console.Write(ASCII.GetString(read, 0, bytes));

                    bytes = ReceiveStream.Read(read, 0, 512);

                }

                Console.WriteLine("");

            }

     

            //Initialize the Uri and the credentials

            private static void Init(string[] uriAndCreds)

            {

                if (uriAndCreds.Length < 3)

                {

                    showusage();

                    return;

                }

     

                // Set the uri and the user credentials and

                uri = uriAndCreds[0];

                username = uriAndCreds[1];

                password = uriAndCreds[2];

     

                if (uriAndCreds.Length == 3)

                    domain = string.Empty;

                else

                    // If the domain exists, store it.

                    // By default the domain name is the name of the server hosting the Internet resource.               

                    domain = uriAndCreds[3];           

            }

     

            public static void Main(string[] args)

            {

                Init(args);

                Console.WriteLine("Listing all authentication modules before unregistering");

                displayRegisteredModules();

               

                // Unregister the standard Basic, NTLM and Negotiate and Digest modules, leaving only Kerberos           

                AuthenticationManager.Unregister("Basic");

                AuthenticationManager.Unregister("NTLM");

                AuthenticationManager.Unregister("Negotiate");

                AuthenticationManager.Unregister("Digest");

                //AuthenticationManager.Unregister("Kerberos");

     

     

                // Display what Authentication modules are left registered

                displayRegisteredModules();

     

                // Read the specified page and display it on the console.

                getPage(uri);

                return;

            } //end Main()

        } //end class TestAuthentication

    }

  • FtpWebRequest and the PWD, SIZE and MDTM methods

    As you may have already noticed, the FtpResponseStream does not contain the result of PWD, SIZE and MDTM methods. You can get those from the FtpWebResponse properties

     

    1. To get the result of the PWD method (WebRequestMethods.Ftp.PrintWorkingDirectory) check the FtpWebResponse.StatusDescription property
    2. To get the result of the SIZE method (WebRequestMethods.Ftp.GetFileSize) check FtpWebResponse.ContentLength property
    3. To get the result of the MDTM method (WebRequestMethods.Ftp.GetDateTimestamp) check  the FtpWebResponse.LastModifiedProperty
  • Using System.Net tracing to determining if SSL connection has been established with the server

    For a detailed blog article on how to use System.Net Tracing go here
    http://blogs.msdn.com/dgorti/archive/2005/09/18/471003.aspx
    Please note that this feature is available in versions of the .Net Framework 2.0 (and above).

    In this concrete example I'll be using HttpWebRequest, but you can use any other System.Net API that supports SSL. As an example I shall use the following post:
    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=340950&SiteID=1

    The customer is attempting to access an https site but is getting a socket error: An existing connection was forcibly closed by the remote host. We enable tracing and the last thing we see in the log is:

    System.Net Information: 0 : [3284] InitializeSecurityContext(credential = System.Net.SafeFreeCredential_SECURITY, context = 197368:1ff9d60, targetName = 212.77.100.18, inFlags = ReplayDetect, SequenceDetect, Confidentiality, AllocateMemory, InitManualCredValidation)

    System.Net Information: 0 : [3284] InitializeSecurityContext(In-Buffer length=9, Out-Buffer length=182, returned code=ContinueNeeded).

    System.Net.Sockets Verbose: 0 : [3284] Socket#49212206::Send()

    System.Net.Sockets Verbose: 0 : [3284] Data from Socket#49212206::Send

                 [ ……Here we receive data: omitting for clarity…..]

    System.Net.Sockets Error: 0 : [3284] Exception in the Socket#49212206::Receive - An existing connection was forcibly closed by the remote host

    System.Net.Sockets Verbose: 0 : [3284] Exiting Socket#49212206::Receive()             -> 0#0

    System.Net.Sockets Verbose: 0 : [3284] Socket#49212206::Dispose()

    System.Net Error: 0 : [3284] Exception in the HttpWebRequest#33574638:: - The underlying connection was closed: An unexpected error occurred on a send.

    System.Net Error: 0 : [3284] Exception in the HttpWebRequest#33574638::EndGetResponse - The underlying connection was closed: An unexpected error occurred on a send.

     

    So how do we know what happened? In most cases we will see certificate errors and it will be easy to determine the cause, but in this case we do not see any. Why? The answer is that we were not able to successfully establish secure connection with the server – the ssl negotiation didn’t succeed and the server closed the connection. This s is the reason why the CertificateValidationCallback was not called at all - the server closed the connection before sending the certificates. In this case the problem was indeed the server: we try to use TLS first and if it doesn’t succeed we try to use SSL3 but the server immediately dropped the connection. So we explicitly set the protocol to be SSL3.
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3; which resolved the problem. Now you could see the certificates in there

     

    System.Net Information: 0 : [0780] Remote certificate: [Version]

      V1

     

    [Subject]

      E=mzielinski@wp-sa.pl, CN=w.wp.pl, OU=Pion Technologii Informatycznej, O=Wirualna Polska S.A., L=Gdansk, S=Pomorskie, C=PL

      Simple Name: w.wp.pl

      Email Name: mzielinski@wp-sa.pl

      DNS Name: w.wp.pl.

     

    [Issuer]

    E=mzielinski@wp-sa.pl, CN=Wirtualna Polska Private Certification Centre Class 2, OU=Pion technologii Informatycznej, O=Wirtualna Polska S.A., L=Gdansk, S=Pomorskie, C=PL

      Simple Name: Wirtualna Polska Private Certification Centre Class 2

      Email Name: mzielinski@wp-sa.pl

      DNS Name: Wirtualna Polska Private Certification Centre Class 2

     [Signature Algorithm]

      md5RSA(1.2.840.113549.1.1.4)

     

    [Public Key]

      Algorithm: RSA

      Length: 1024

      Key Blob: 30 81 89 02 81 81 00 bf ff ab 80 08 bb 39 e1 c0 97 64 75 1e ac ee 5e b8 84 8c eb e9 26 25 a5 77 6d 66 fa d3 dd 71 41 b5 87 8a 1f d4 08 8c ba 40 c....

     

    ………….

     

     

    You can also see the certificate errors clearly logged.

     

    System.Net Information: 0 : [0780] SecureChannel#34576242 - Remote certificate has errors:

    System.Net Information: 0 : [0780] SecureChannel#34576242 -           Certificate name mismatch.

    System.Net Information: 0 : [0780] SecureChannel#34576242 -           A certificate chain could not be built to a trusted root authority.

     

    System.Net Information: 0 : [0780] SecureChannel#34576242 -           A required certificate is not within its validity period when verifying against the current system clock or the timestamp in the signed file.

     

    Now the negotiation won’t succeed because of certificate errors which you could clearly see described in the log.

  • RemoteEndPoint: Identifying the client from the server side

    Variant 1: When using TcpListener class for our server there are 2 ways to get the underlying client

                TcpClient client = listener.AcceptTcpClient();

                IPEndPoint remoteEP = (IPEndPoint) client.Client.RemoteEndPoint;

     

    or

     

                Socket client = listener.AcceptSocket();

                IPEndPoint remoteEP = (IPEndPoint) client.RemoteEndPoint;

     

    Variant 2: When using the Socket class:

                Socket client = socketServer.Accept();

                IPEndPoint remoteEP = (IPEndPoint) client.RemoteEndPoint;

     

     

    Then we can very easy get the IPAddress/Port for the client

                IPAddress ip = remoteEP.Address;

                int port = remoteEP.Port;

  • Sending e-mail using SmtpClient and Gmail

    The sample below used SmtpClient to send e-mail from your gmail account using your gmail username and password.

     

     

    using System;

    using System.Net;

    using System.Net.Mail;

     

    namespace GMailSample

    {

        class SimpleSmtpSend

        {

            static void Main(string[] args)

            {

                SmtpClient client = new SmtpClient("smtp.gmail.com", 587);           

                client.EnableSsl = true;

                MailAddress from = new MailAddress("YourGmailUserName@gmail.com", "[ Your full name here]");           

                MailAddress to = new MailAddress("your recipient e-mail address", "Your recepient name");

                MailMessage message = new MailMessage(from, to);

                message.Body = "This is a test e-mail message sent using gmail as a relay server ";

                message.Subject = "Gmail test email with SSL and Credentials";

                NetworkCredential myCreds = new NetworkCredential("YourGmailUserName@gmail.com", "YourPassword", "");           

                client.Credentials = myCreds;

                try

                {

                    client.Send(message);

                }

                catch (Exception ex)

                {

                    Console.WriteLine("Exception is:" + ex.ToString());

                }

                Console.WriteLine("Goodbye.");

            }

        }

    }

     

    What happens here is that SmtpClient sends your e-mail to a relay server (in this case the gmail web mail server) and then this relay server sends it to its specified destination. Of course you're not limited to just gmail. The relay server can be another mail server, or a SMTP server/service - you can use a third party one or set your own. I'll update the post to include more detail on this later.

     

    You can easily expand this sample to a more elaborate one - with attachments, html content, embeded images, etc. For more detail please refer to our documentation on msdn2 here:

    http://msdn2.microsoft.com/en-us/library/system.net.mail.smtpclient.aspx
    There is also a very good site containig basic and more advanced samples here:
    http://www.systemnetmail.com/faq/3.aspx
    http://www.systemnetmail.com/faq/4.aspx

  • FtpWebRequest: Does the slash matter?

    For those of you who are curious: there is indeed a difference when we use the following uris as parameters for our FtpWebRequest class:

    ftp://server/ParentDirectory

    ftp://server/ParentDirectory/ (Note the slash at the end)

     

    Let’s say we want to create a new directory named “new” in "ParentDirectory". With the first uri (without the slash) we issue:

    MKD ParentDirectory\new 

     

    And with the slash at the end we issue

    CWD ParentDirectory

    MKD new

     

    The same model is applicable for all ftp commands that the FtpWebRequest class supports

     

  • Changing to the root directory with FtpWebRequest

    Many customers ask us how they can use the CWD command with our FtpWebRequest.

    The answer is: you cannot use the command directly, but you can modify the uri parameter to achieve the same result.

    Let's say you're using the following format:

    String uri = "ftp://myFtpUserName:myFtpUserPassword@myFtpUrl";

    FtpWebRequest Request = (FtpWebRequest)WebRequest.Create(uri);

    Request.Method = "LIST";

    The above example will bring you to your user's directory and list all the contents there. Now let's say you want to go 2 directories backwards and list the contents there (provided your user has permissions to do that). You close the previous FtpWebRequest and issue a new one with this uri

    uri = "ftp://myFtpUserName:myFtpUserPassword@myFtpUrl/%2E%2E/%2E%2E";

    This is equivalent to logging in with your user's credentials and then using  cd ../../

    Note: if you try using the ”..”  directly without escaping them the uri class will strip them, so "ftp://myFtpUserName:myFtpUserPassword@myFtpUrl/../.." is equivalent to "ftp://myFtpUserName:myFtpUserPassword@myFtpUrl/" 

    Now let's say you want to go to another user's directory which is one level above the root. If you don't specify a user name and password it's equivalent to logging in as anonymous user. Then you issue a new FtpWebRequest with the following uri

    "ftp://myFtpUrl/%2F/anotherUserDir"

    This is equivalent to logging in as anonymous and then doing

    Cd /

    cd anotherUserDirectory

     


© 2009 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker