[c# winform] telnet, smtp server auth.

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • piercy
    New Member
    • Aug 2007
    • 77

    [c# winform] telnet, smtp server auth.

    I've already got some code in place which allows me to connect to a smtp server and verify the ip(just checks for a timeout.). anyway, i now need the app to check that the user and password its given has access to the smtp to send email. the code i have is as follows.
    Code:
    public ScriptingTelnet(string Address, int Port, int CommandTimeout)
    		{
    			address = Address;
    			port = Port;
    			timeout = CommandTimeout;
    		}
    
    
    public bool Connect()
    		{
    			string []aliases;
    			IPAddress[] addr;
    			try
    			{
    				IPHostEntry IPHost = Dns.Resolve(address); 
    				aliases = IPHost.Aliases; 
    				addr = IPHost.AddressList; 
    			}
    			catch
    			{
    				return false;
    			}
    		
    			try
    			{
    				// Try a blocking connection to the server
    				s				= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    				iep				= new IPEndPoint(addr[0],port);  
    				s.Connect(iep) ;	
    
    
    				// If the connect worked, setup a callback to start listening for incoming data
    				AsyncCallback recieveData = new AsyncCallback( OnRecievedData );
    				s.BeginReceive( m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData , s );
    		
    				// All is good
    				return true;
    			}
    			catch
    			{
    				// Something failed
    				return false;
    			}
    			
    		}
    so to do my test i call something like:
    Code:
    			objActiveSolutions.ScriptingTelnet telnet = new objActiveSolutions.ScriptingTelnet(this.tbSMTPIP.Text, 25, 10);
    			if(telnet.Connect())
    			{
    				//do something
    			}

    Anyone know who i can modify this to allow me to authenticate the user and pass?

    Many Thanks,
    Piercy
  • Plater
    Recognized Expert Expert
    • Apr 2007
    • 7872

    #2
    If you are talking to the SMTP server, look up the SMTP commands.
    Giving the "EHLO <your computer identity>"+[enter] should give you a response back telling you what AUTH types are available.
    You can then send the correct sequence of commands to AUTH the user.

    The rfc for the AUTH command extension can be found here

    Comment

    • piercy
      New Member
      • Aug 2007
      • 77

      #3
      Originally posted by Plater
      If you are talking to the SMTP server, look up the SMTP commands.
      Giving the "EHLO <your computer identity>"+[enter] should give you a response back telling you what AUTH types are available.
      You can then send the correct sequence of commands to AUTH the user.

      The rfc for the AUTH command extension can be found here
      yes, i know the commands i need to use or i cna look them up. but im just not sure how i would send the commands to the server from the program.

      im basically doing a check for a smtp server, my users use the application to send emails to people in a database.

      now on settings up the app they admin will need to put in the smtp ip, username and password if required. the code i have above check the ip address responds. now i need it to authenticate and check it responds at the same time.

      any ideas on how to send the commands from the app? just and ehlo or something then i can work out the auth and stuff myself.

      Thanks once again,
      Piercy

      Comment

      • Plater
        Recognized Expert Expert
        • Apr 2007
        • 7872

        #4
        In the program I made for smtp, I had an active socket connection the smtp server. (I presume that this is a real smtp server yes?)
        On the socket I send the commands as ASCII strings

        Format of EHLO:
        Code:
        string computername="PlaterComputer";
        string domainname="mydomain.com";
        string ehlostring= "EHLO "+computername+"."+domainname+"\r\n";
        Then the response back should follow the SMPT standards. It's possible the server will NOT list the available ATUH types (I believe it is supposed to though)

        That link I send in the past message has example communications, i.e.:
        C: EHLO mycomputer.mydo main.com
        S: 250-mail.mydomain.c om
        S: 250 AUTH CRAM-MD5 DIGEST-MD5
        That server will accept CRAM-MD5 and DIGEST-MD5 auth types.

        Was that what you were looking for or were you looking for the actual how to send it using code thing?

        Comment

        • piercy
          New Member
          • Aug 2007
          • 77

          #5
          Originally posted by Plater
          In the program I made for smtp, I had an active socket connection the smtp server. (I presume that this is a real smtp server yes?)
          On the socket I send the commands as ASCII strings

          Format of EHLO:
          Code:
          string computername="PlaterComputer";
          string domainname="mydomain.com";
          string ehlostring= "EHLO "+computername+"."+domainname+"\r\n";
          Then the response back should follow the SMPT standards. It's possible the server will NOT list the available ATUH types (I believe it is supposed to though)

          That link I send in the past message has example communications, i.e.:

          That server will accept CRAM-MD5 and DIGEST-MD5 auth types.

          Was that what you were looking for or were you looking for the actual how to send it using code thing?

          It doesn thave to use the code i provided. it would just be helpful if it did. im playing around with it now and the smtp server im using (yes it's real)
          Code:
          250 - AUTH GSSAPI NTLM LOGIN
          250 - AUTH =LOGIN
          thats part of what my server responds when ehlo it using cmd prmpt.

          would you be able to give me the exact piece of code to send to the server. ie, i think the coding im using does this...

          Socket s = new socket........
          so the how would i send the commands soemthing like s.command?

          Comment

          • Plater
            Recognized Expert Expert
            • Apr 2007
            • 7872

            #6
            250 - AUTH GSSAPI NTLM LOGIN
            250 - AUTH =LOGIN

            So your server would prefer that use "LOGIN", but allows those other two types as well.

            Well once you have you Socket created and connected (You can use the TcpClient object if you prefer)
            So consider the following:
            I have a function:
            Code:
            private void Write(TcpClient tcpc, byte[] buff)
                    {
                        try
                        {
                            tcpc.GetStream().Write(buff, 0, buff.Length);
                        }
                        catch (IOException ioe)
                        {
                            bool ignoreme = (ioe.Message == "");
                            tcpc.Close();
                        }
                        catch (SocketException se)
                        {
                            bool ignoreme = (se.Message == "");
                            tcpc.Close();
                        }
                    }
            Then I could use:
            Code:
            string authstring="AUTH LOGIN\r\n";
            byte[] authbytes=Encoding.ASCII.GetBytes(authstring);
            //you need a byte[] to send on the socket
            // Encoding.ASCII is your friend
            
            write(mytcpclient, authbytes);//send the command string
            
            //you will then have to wait and read the response back
            //my actual "read" function used to read the response is pretty big
            //but I will include a little bit to get you started maybe:
            private string GetResponse(TcpClient tcpc)
            {
               string gotin = "";
               while (tcpc.GetStream().DataAvailable)
               {
                  gotin += ((char)tcpc.GetStream().ReadByte());
               }
               return gotin;
            }
            You would then use the write/read functions to send you commands and verify the response.

            Comment

            • piercy
              New Member
              • Aug 2007
              • 77

              #7
              Originally posted by Plater
              250 - AUTH GSSAPI NTLM LOGIN
              250 - AUTH =LOGIN

              So your server would prefer that use "LOGIN", but allows those other two types as well.

              Well once you have you Socket created and connected (You can use the TcpClient object if you prefer)
              So consider the following:
              I have a function:
              Code:
              private void Write(TcpClient tcpc, byte[] buff)
                      {
                          try
                          {
                              tcpc.GetStream().Write(buff, 0, buff.Length);
                          }
                          catch (IOException ioe)
                          {
                              bool ignoreme = (ioe.Message == "");
                              tcpc.Close();
                          }
                          catch (SocketException se)
                          {
                              bool ignoreme = (se.Message == "");
                              tcpc.Close();
                          }
                      }
              Then I could use:
              Code:
              string authstring="AUTH LOGIN\r\n";
              byte[] authbytes=Encoding.ASCII.GetBytes(authstring);
              //you need a byte[] to send on the socket
              // Encoding.ASCII is your friend
              
              write(mytcpclient, authbytes);//send the command string
              
              //you will then have to wait and read the response back
              //my actual "read" function used to read the response is pretty big
              //but I will include a little bit to get you started maybe:
              private string GetResponse(TcpClient tcpc)
              {
                 string gotin = "";
                 while (tcpc.GetStream().DataAvailable)
                 {
                    gotin += ((char)tcpc.GetStream().ReadByte());
                 }
                 return gotin;
              }
              You would then use the write/read functions to send you commands and verify the response.

              right, im feeling like an idiot at the moment... lol... anyway, this is the code i had and it seems to contain all the information i need.. not definate but i think it does.

              Code:
              using System;
              using System.Net;
              using System.Net.Sockets;
              using System.Text;
              using System.IO;
              using System.Threading ;
              
              namespace objActiveSolutions
              {
              	/// <summary>
              	/// Summary description for clsScriptingTelnet.
              	/// </summary>
              	public class ScriptingTelnet
              	{
              		private IPEndPoint iep ;
              		private AsyncCallback callbackProc;
              		private string address ;
              		private int port ;
              		private int timeout;
              		private Socket s ;
              		Byte[] m_byBuff = new Byte[32767];
              		private string strWorkingData = "";					// Holds everything received from the server since our last processing
              		private string strFullLog = "";
              
              		public ScriptingTelnet(string Address, int Port, int CommandTimeout)
              		{
              			address = Address;
              			port = Port;
              			timeout = CommandTimeout;
              		}
              		
              		
              		private void OnRecievedData( IAsyncResult ar )
              		{
              			// Get The connection socket from the callback
              			Socket sock = (Socket)ar.AsyncState;
              
              			// Get The data , if any
              			int nBytesRec = sock.EndReceive( ar );		
              			
              			if( nBytesRec > 0 )
              			{
              				// Decode the received data
              				string sRecieved = CleanDisplay(Encoding.ASCII.GetString( m_byBuff, 0, nBytesRec ));
              
              				// Write out the data
              				if (sRecieved.IndexOf("[c") != -1) Negotiate(1);
              				if (sRecieved.IndexOf("[6n") != -1) Negotiate(2);
              				
              				Console.WriteLine(sRecieved);
              				strWorkingData += sRecieved;
              				strFullLog += sRecieved;
              				
              
              				// Launch another callback to listen for data
              				AsyncCallback recieveData = new AsyncCallback(OnRecievedData);
              				sock.BeginReceive( m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData , sock );
              				
              			}
              			else
              			{
              				// If no data was recieved then the connection is probably dead
              				Console.WriteLine( "Disconnected", sock.RemoteEndPoint );
              				sock.Shutdown( SocketShutdown.Both );
              				sock.Close();
              				//Application.Exit();
              			}
              		}
              	
              		private void DoSend(string strText)
              		{
              			try
              			{
              				Byte[] smk = new Byte[strText.Length];
              				for ( int i=0; i < strText.Length ; i++)
              				{
              					Byte ss = Convert.ToByte(strText[i]);
              					smk[i] = ss ;
              				}
              
              				s.Send(smk,0 , smk.Length , SocketFlags.None);
              			}
              			catch
              			{
              				//MessageBox.Show("ERROR IN RESPOND OPTIONS");
              			}
              		}
              
              		private void Negotiate(int WhichPart)
              		{
              			StringBuilder x;
              			string neg;
              			if (WhichPart == 1)
              			{
              				x = new StringBuilder();
              				x.Append ((char)27);
              				x.Append ((char)91);
              				x.Append ((char)63);
              				x.Append ((char)49);
              				x.Append ((char)59);
              				x.Append ((char)50);
              				x.Append ((char)99);
              				neg =  x.ToString();
              			}
              			else
              			{
              
              				x = new StringBuilder();
              				x.Append ((char)27);
              				x.Append ((char)91);
              				x.Append ((char)50);
              				x.Append ((char)52);
              				x.Append ((char)59);
              				x.Append ((char)56);
              				x.Append ((char)48);
              				x.Append ((char)82);
              				neg = x.ToString();
              			}
              			SendMessage(neg,true);
              		}
              
              		private string CleanDisplay(string input)
              		{
              			
              			input = input.Replace("(0x (B","|");
              			input = input.Replace("(0 x(B","|");
              			input = input.Replace(")0=>","");
              			input = input.Replace(">","");
              			input = input.Replace("7","[");
              			input = input.Replace("*8","]");
              			input = input.Replace("","");
              			return input;
              		}
              
              
              		
              		
              		/// <summary>
              		/// Connects to the telnet server.
              		/// </summary>
              		/// <returns>True upon connection, False if connection fails</returns>
              		public bool Connect()
              		{
              			string []aliases;
              			IPAddress[] addr;
              			try
              			{
              				IPHostEntry IPHost = Dns.Resolve(address); 
              				aliases = IPHost.Aliases; 
              				addr = IPHost.AddressList; 
              			}
              			catch
              			{
              				return false;
              			}
              		
              			try
              			{
              				// Try a blocking connection to the server
              				s				= new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
              				iep				= new IPEndPoint(addr[0],port);  
              				s.Connect(iep) ;	
              
              
              				// If the connect worked, setup a callback to start listening for incoming data
              				AsyncCallback recieveData = new AsyncCallback( OnRecievedData );
              				s.BeginReceive( m_byBuff, 0, m_byBuff.Length, SocketFlags.None, recieveData , s );
              		
              				// All is good
              				return true;
              			}
              			catch
              			{
              				// Something failed
              				return false;
              			}
              			
              		}
              
              		
              		public void Disconnect()
              		{
              			if (s.Connected) s.Close();
              		}
              
              		/// <summary>
              		/// Waits for a specific string to be found in the stream from the server
              		/// </summary>
              		/// <param name="DataToWaitFor">The string to wait for</param>
              		/// <returns>Always returns 0 once the string has been found</returns>
              		public int WaitFor(string DataToWaitFor)
              		{
              			// Get the starting time
              			long lngStart = DateTime.Now.AddSeconds(this.timeout).Ticks;
              			long lngCurTime = 0;
              
              			while (strWorkingData.ToLower().IndexOf(DataToWaitFor.ToLower()) == -1)
              			{
              				// Timeout logic
              				lngCurTime = DateTime.Now.Ticks;
              				if (lngCurTime > lngStart)
              				{
              					throw new Exception("Timed Out waiting for : " + DataToWaitFor);
              				}
              				Thread.Sleep(1);
              			}
              			strWorkingData = "";
              			return 0;
              		}
              		
              		
              		/// <summary>
              		/// Waits for one of several possible strings to be found in the stream from the server
              		/// </summary>
              		/// <param name="DataToWaitFor">A delimited list of strings to wait for</param>
              		/// <param name="BreakCharacters">The character to break the delimited string with</param>
              		/// <returns>The index (zero based) of the value in the delimited list which was matched</returns>
              		public int WaitFor(string DataToWaitFor,string BreakCharacter)
              		{
              			// Get the starting time
              			long lngStart = DateTime.Now.AddSeconds(this.timeout).Ticks;
              			long lngCurTime = 0;
              
              			string[] Breaks = DataToWaitFor.Split(BreakCharacter.ToCharArray());
              			int intReturn = -1;
              
              			while (intReturn == -1)
              			{
              				// Timeout logic
              				lngCurTime = DateTime.Now.Ticks;
              				if (lngCurTime > lngStart)
              				{
              					throw new Exception("Timed Out waiting for : " + DataToWaitFor);
              				}
              				
              				Thread.Sleep(1);
              				for (int i = 0 ; i < Breaks.Length ; i++)
              				{
              					if (strWorkingData.ToLower().IndexOf(Breaks[i].ToLower()) != -1)
              					{
              						intReturn = i ;
              					}
              				}
              			}
              			return intReturn;
              
              		}
              
              
              		/// <summary>
              		/// Sends a message to the server
              		/// </summary>
              		/// <param name="Message">The message to send to the server</param>
              		/// <param name="SuppressCarriageReturn">True if you do not want to end the message with a carriage return</param>
              		public void SendMessage(string Message, bool SuppressCarriageReturn)
              		{
              			strFullLog += "\r\nSENDING DATA ====> " + Message.ToUpper() + "\r\n";
              			Console.WriteLine("SENDING DATA ====> " + Message.ToUpper());
              
              			if (! SuppressCarriageReturn)
              			{
              				DoSend(Message + "\r");
              			}
              			else
              			{
              				DoSend(Message);
              			}
              		}
              
              
              		/// <summary>
              		/// Sends a message to the server, automatically appending a carriage return to it
              		/// </summary>
              		/// <param name="Message">The message to send to the server</param>
              		public void SendMessage(string Message)
              		{
              			strFullLog += "\r\nSENDING DATA ====> " + Message.ToUpper() + "\r\n";
              			Console.WriteLine("SENDING DATA ====> " + Message.ToUpper());
              
              			DoSend(Message + "\r");
              		}
              
              
              		/// <summary>
              		/// Waits for a specific string to be found in the stream from the server.
              		/// Once that string is found, sends a message to the server
              		/// </summary>
              		/// <param name="WaitFor">The string to be found in the server stream</param>
              		/// <param name="Message">The message to send to the server</param>
              		/// <returns>Returns true once the string has been found, and the message has been sent</returns>
              		public void WaitAndSend(string WaitFor,string Message)
              		{
              			this.WaitFor(WaitFor);
              			SendMessage(Message);
              			
              			
              		}
              
              
              		/// <summary>
              		/// Sends a message to the server, and waits until the designated
              		/// response is received
              		/// </summary>
              		/// <param name="Message">The message to send to the server</param>
              		/// <param name="WaitFor">The response to wait for</param>
              		/// <returns>True if the process was successful</returns>
              		public string SendAndWait(string Message, string WaitFor)
              		{
              			
              			SendMessage(Message);
              			this.WaitFor(WaitFor);
              			return strWorkingData;
              		}
              			
              		
              
              		public int SendAndWait(string Message, string WaitFor, string BreakCharacter)
              		{
              			SendMessage(Message);
              			int t = this.WaitFor(WaitFor,BreakCharacter);
              			return t;
              		}
              
              
              		/// <summary>
              		/// A full log of session activity
              		/// </summary>
              		public string SessionLog
              		{
              			get 
              			{
              				return strFullLog;
              			}
              		}
              
              
              		/// <summary>
              		/// Clears all data in the session log
              		/// </summary>
              		public void ClearSessionLog()
              		{
              			strFullLog = "";
              		}
              
              
              		/// <summary>
              		/// Searches for two strings in the session log, and if both are found, returns
              		/// all the data between them.
              		/// </summary>
              		/// <param name="StartingString">The first string to find</param>
              		/// <param name="EndingString">The second string to find</param>
              		/// <param name="ReturnIfNotFound">The string to be returned if a match is not found</param>
              		/// <returns>All the data between the end of the starting string and the beginning of the end string</returns>
              		public string FindStringBetween(string StartingString, string EndingString, string ReturnIfNotFound)
              		{
              			int intStart;
              			int intEnd;
              
              			intStart = strFullLog.ToLower().IndexOf(StartingString.ToLower());
              			if (intStart == -1)
              			{
              				return ReturnIfNotFound;
              			}
              			intStart += StartingString.Length;
              
              			intEnd = strFullLog.ToLower().IndexOf(EndingString.ToLower(),intStart);
              
              			if (intEnd == -1)
              			{
              				// The string was not found
              				return ReturnIfNotFound;
              			}
              
              			// The string was found, let's clean it up and return it
              			return strFullLog.Substring(intStart, intEnd-intStart).Trim();
              		}
              
              
              	}
              }
              i know this is a lot of code to paste, i did look for a online code dump to make it easier but couldnt find one.

              would you be able to give me a quick example (using this if you can) on how to AUTH to a server preferably in the login encoding and/or a way to check for the auth the server requires and auth usign that(because thats the eventual need).

              Thanks you very much, youve been a big help... even though i havent got my result yet im undertsanding it alot more.

              Thanks,
              Piercy

              Comment

              • Plater
                Recognized Expert Expert
                • Apr 2007
                • 7872

                #8
                Well I think you can do this:
                Code:
                string authstart="AUTH LOGIN\r\n";
                SendMessage(authstart,true);
                //you need a "\r\n" I think, maybe not, whatever though
                Now the problem is, you don't know what the response will be that you're waiting for, other then it will start with a 3-digit number and end with a "\r" or "\r\n".

                It didn't look like you had a way to read a response and process it. The AUTH types require you to examine the reply to determine your response.

                Appendix B: A LOGIN SMTP AUTH Conversation
                A LOGIN SMTP Authentication conversation looks something like the following (after the mail server has indicated its support for the LOGIN mechanism):


                C: AUTH LOGIN
                S: 334 VXNlcm5hbWU6
                C: d2VsZG9u
                S: 334 UGFzc3dvcmQ6
                C: dzNsZDBu
                S: 235 2.0.0 OK Authenticated

                Lines 2-5 of the conversation contain base64-encoded information. The same conversation, with base64 strings decoded, reads:


                C: AUTH LOGIN
                S: 334 Username:
                C: weldon
                S: 334 Password:
                C: w3ld0n
                S: 235 2.0.0 OK Authenticated
                And the Convert object has .ToBase64String () ability for you.

                Comment

                • piercy
                  New Member
                  • Aug 2007
                  • 77

                  #9
                  Originally posted by Plater
                  Well I think you can do this:
                  Code:
                  string authstart="AUTH LOGIN\r\n";
                  SendMessage(authstart,true);
                  //you need a "\r\n" I think, maybe not, whatever though
                  Now the problem is, you don't know what the response will be that you're waiting for, other then it will start with a 3-digit number and end with a "\r" or "\r\n".

                  It didn't look like you had a way to read a response and process it. The AUTH types require you to examine the reply to determine your response.



                  And the Convert object has .ToBase64String () ability for you.
                  you still need to use the econding .ascii.getbytes because Convet.ToBase64 String needs it in byte[] form. anyway, thats a greta help. tells em what to do really well. im still stuck with how i verify that connection. after all thats what its all about. some sort fo command i would need to be auth for for it to work? would i still need to be abel to return something from the server to verify the conenction?

                  or any other possible ways to verify the username and password against the ipaddress?

                  Comment

                  • Plater
                    Recognized Expert Expert
                    • Apr 2007
                    • 7872

                    #10
                    Some servers have a VRFY command, but it's been my experiance that very few implement it.

                    Other than connecting to the smtp server and running the AUTH command sequence, I don't know of any way to validate username and passwords against a mail server.

                    Comment

                    • piercy
                      New Member
                      • Aug 2007
                      • 77

                      #11
                      Originally posted by Plater
                      Some servers have a VRFY command, but it's been my experiance that very few implement it.

                      Other than connecting to the smtp server and running the AUTH command sequence, I don't know of any way to validate username and passwords against a mail server.

                      ok, looks like my client will have to go without the verification feature. ill verify theres a smtp server there then its there own job to make sure they give my the correct username and password.

                      Thanks very much for your help. even though id dint get the result i wanted i learnt a lot while doing it.

                      Thanks once again,
                      Piercy

                      Comment

                      • Plater
                        Recognized Expert Expert
                        • Apr 2007
                        • 7872

                        #12
                        Are you not allowed to modify the code you provided to me?
                        It might be possible for you to simple "add in" the response reading functions that you need.

                        Comment

                        • piercy
                          New Member
                          • Aug 2007
                          • 77

                          #13
                          Originally posted by Plater
                          Are you not allowed to modify the code you provided to me?
                          It might be possible for you to simple "add in" the response reading functions that you need.
                          i can modify it all i wish. also, the s3xy person next to me(lol).. recomended calling a hidden command prompt using the system.diagnost ics namespace.

                          have you got any ideas for modifying that code then?

                          THanks,
                          Piercy

                          Comment

                          • Plater
                            Recognized Expert Expert
                            • Apr 2007
                            • 7872

                            #14
                            Well what you would need is a "readline" from the response stream.and then have access to everything up till that point.
                            All server responses start with 3digits and end with the newline terminator.
                            If you could find a way to get at that it might work.

                            Comment

                            • piercy
                              New Member
                              • Aug 2007
                              • 77

                              #15
                              edit: going to post ina enw thread as its a totally different problem now.. thanks for your help.

                              Comment

                              Working...