예제 #1
0
        /// <summary>
        /// Resolves a DNS host name or IP to IPAddress[].
        /// </summary>
        /// <param name="hostName_IP">Host name or IP address.</param>
        /// <returns></returns>
        public static IPAddress[] Resolve(string hostName_IP)
        {
            // If hostName_IP is IP
            try{
                return(new IPAddress[] { IPAddress.Parse(hostName_IP) });
            }
            catch {
            }

            // This is probably NetBios name
            if (hostName_IP.IndexOf(".") == -1)
            {
                return(System.Net.Dns.Resolve(hostName_IP).AddressList);
            }
            else
            {
                // hostName_IP must be host name, try to resolve it's IP
                Dns_Client        dns  = new Dns_Client();
                DnsServerResponse resp = dns.Query(hostName_IP, QTYPE.A);
                if (resp.ResponseCode == RCODE.NO_ERROR)
                {
                    A_Record[]  records = resp.GetARecords();
                    IPAddress[] retVal  = new IPAddress[records.Length];
                    for (int i = 0; i < records.Length; i++)
                    {
                        retVal[i] = IPAddress.Parse(records[i].IP);
                    }

                    return(retVal);
                }
                else
                {
                    throw new Exception(resp.ResponseCode.ToString());
                }
            }
        }
예제 #2
0
		/// <summary>
		/// Connects to sepcified host.
		/// </summary>
		/// <param name="localEndpoint">Sets local endpoint. Pass null, to use default.</param>
		/// <param name="host">Host name or IP address.</param>
		/// <param name="port">Port where to connect.</param>
		public void Connect(IPEndPoint localEndpoint,string host,int port)
		{	
			Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.IP);
			s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReceiveTimeout,30000);
			s.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.NoDelay,1);
			
			m_pSocket = new BufferedSocket(s);
			if(localEndpoint != null){
				m_pSocket.Bind(localEndpoint);
			}
			m_pSocket.Activity += new EventHandler(m_pSocket_Activity);
	
			if(SessionLog != null){
				m_pLogger = new SocketLogger(s,SessionLog);
				m_pLogger.SessionID = Guid.NewGuid().ToString();
				m_pSocket.Logger = m_pLogger;
			}
		
			if(host.IndexOf("@") == -1){
				m_pSocket.Connect(new IPEndPoint(Dns_Client.Resolve(host)[0],port));					
			}
			else{
				//---- Parse e-domain -------------------------------//
				string domain = host;

				// eg. Ivx <*****@*****.**>
				if(domain.IndexOf("<") > -1 && domain.IndexOf(">") > -1){
					domain = domain.Substring(domain.IndexOf("<")+1,domain.IndexOf(">") - domain.IndexOf("<")-1);
				}

				if(domain.IndexOf("@") > -1){
					domain = domain.Substring(domain.LastIndexOf("@") + 1);
				}

				if(domain.Trim().Length == 0){
					if(m_pLogger != null){
						m_pLogger.AddTextEntry("Destination address '" + host + "' is invalid, aborting !");
					}
					throw new Exception("Destination address '" + host + "' is invalid, aborting !");
				}

				//--- Get MX record -------------------------------------------//
				Dns_Client dns = new Dns_Client();
				Dns_Client.DnsServers = m_pDnsServers;
				DnsServerResponse dnsResponse = dns.Query(domain,QTYPE.MX);

				switch(dnsResponse.ResponseCode)
				{
					case RCODE.NO_ERROR:
						MX_Record[] mxRecords = dnsResponse.GetMXRecords();

						// Try all available hosts by MX preference order, if can't connect specified host.
						foreach(MX_Record mx in mxRecords){
							try{
								if(m_pLogger != null){
									m_pLogger.AddTextEntry("Connecting with mx record to: " + mx.Host);
								}
								m_pSocket.Connect(new IPEndPoint(Dns_Client.Resolve(mx.Host)[0],port));
								break;
							}
							catch{ // Just skip and let for to try next host.									
								if(m_pLogger != null){
									m_pLogger.AddTextEntry("Failed connect to: " + mx.Host);
								}
							}
						}

						// None of MX didn't connect
						if(mxRecords.Length > 0 && !m_pSocket.Connected){
							throw new Exception("Destination email server is down");
						}

						/* Rfc 2821 5
						 If no MX records are found, but an A RR is found, the A RR is treated as
						 if it was associated with an implicit MX RR, with a preference of 0,
						 pointing to that host.
						*/
						if(mxRecords.Length == 0){
							// Try to connect with A record
							IPAddress[] ipEntry = null;
							try{
								if(m_pLogger != null){
									m_pLogger.AddTextEntry("No mx record, trying to get A record for: " + domain);
								}
								ipEntry = Dns_Client.Resolve(domain);								
							}
							catch{
								if(m_pLogger != null){
									m_pLogger.AddTextEntry("Invalid domain,no MX or A record: " + domain);
								}
								throw new Exception("Invalid domain,no MX or A record: " + domain);
							}

							try{
								if(m_pLogger != null){
									m_pLogger.AddTextEntry("Connecting with A record to:" + domain);
								}
								m_pSocket.Connect(new IPEndPoint(ipEntry[0],port));
							}
							catch{
								if(m_pLogger != null){
									m_pLogger.AddTextEntry("Failed connect to:" + domain);
								}
								throw new Exception("Destination email server is down");
							}
						}
						break;

					case RCODE.NAME_ERROR:
						if(m_pLogger != null){
							m_pLogger.AddTextEntry("Invalid domain,no MX or A record: " + domain);
						}
						throw new Exception("Invalid domain,no MX or A record: " + domain);

					case RCODE.SERVER_FAILURE:
						if(m_pLogger != null){
							m_pLogger.AddTextEntry("Dns server unvailable.");
						}
						throw new Exception("Dns server unvailable.");
					}					
			}
					
			/*
			 * Notes: Greeting may be single or multiline response.
			 *		
			 * Examples:
			 *		220<SP>SMTP server ready<CRLF> 
			 * 
			 *		220-SMTP server ready<CRLF>
			 *		220-Addtitional text<CRLF>
			 *		220<SP>final row<CRLF>
			 * 
			*/

			// Read server response
			string responseLine = m_pSocket.ReadLine(1000);
			while(!responseLine.StartsWith("220 ")){
				// If lisne won't start with 220, then its error response
				if(!responseLine.StartsWith("220")){
					throw new Exception(responseLine);
				}

				responseLine = m_pSocket.ReadLine(1000);
			}

			m_Connected = true;
		}
예제 #3
0
		//---- SMTP implementation ----//

		#region function SendMessageToServer

		private bool SendMessageToServer(string[] to,string reverse_path,Stream message)
		{
			// Get email from to string
			for(int i=0;i<to.Length;i++){
				to[i] = (new InfoControl.Net.Mail.Mime.eAddress(to[i])).Email;
			}

			ArrayList defectiveEmails = new ArrayList();

			Socket so = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
			so.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.SendTimeout,15000);
			BufferedSocket socket = new BufferedSocket(so);

			SocketLogger logger = null;
			if(m_LogCmds && this.SessionLog != null){
				logger = new SocketLogger(so,this.SessionLog);
				logger.SessionID = socket.GetHashCode().ToString();
				socket.Logger = logger;
			}

			try{
				string reply = "";	
				bool supports_SIZE  = false;
				bool supports_8BIT  = false;
				bool supports_BDAT  = false;
										
				if(m_UseSmartHost){
					socket.Connect(new IPEndPoint(System.Net.Dns.Resolve(m_SmartHost).AddressList[0],m_Port));					
				}
				else{
					//---- Parse e-domain -------------------------------//
					string domain = to[0];

					// eg. Ivx <*****@*****.**>
					if(domain.IndexOf("<") > -1 && domain.IndexOf(">") > -1){
						domain = domain.Substring(domain.IndexOf("<")+1,domain.IndexOf(">") - domain.IndexOf("<")-1);
					}

					if(domain.IndexOf("@") > -1){
						domain = domain.Substring(domain.LastIndexOf("@") + 1);
					}

					if(domain.Trim().Length == 0){
						if(logger != null){
							logger.AddTextEntry("Destination address '" + to[0] + "' is invalid, aborting !");
						}
						return false;
					}

					//--- Get MX record -------------------------------------------//
					Dns_Client dns = new Dns_Client();
					Dns_Client.DnsServers = m_DnsServers;
					DnsServerResponse dnsResponse = dns.Query(domain,QTYPE.MX);

					switch(dnsResponse.ResponseCode)
					{
						case RCODE.NO_ERROR:
							MX_Record[] mxRecords = dnsResponse.GetMXRecords();

							// Try all available hosts by MX preference order, if can't connect specified host.
							foreach(MX_Record mx in mxRecords){
								try{
									if(logger != null){
										logger.AddTextEntry("Connecting with mx record to: " + mx.Host);
									}
									socket.Connect(new IPEndPoint(System.Net.Dns.Resolve(mx.Host).AddressList[0],m_Port));
									break;
								}
								catch{ // Just skip and let for to try next host.									
									if(logger != null){
										logger.AddTextEntry("Failed connect to: " + mx.Host);
									}
								}
							}

							/* Rfc 2821 5
							 If no MX records are found, but an A RR is found, the A RR is treated as
							 if it was associated with an implicit MX RR, with a preference of 0,
							 pointing to that host.

							*/
							if(mxRecords.Length == 0){
								// Try to connect with A record
								IPHostEntry ipEntry = null;
								try{
									if(logger != null){
										logger.AddTextEntry("No mx record, trying to get A record for: " + domain);
									}
									ipEntry = System.Net.Dns.Resolve(domain);								
								}
								catch{
									if(logger != null){
										logger.AddTextEntry("Invalid domain,no MX or A record: " + domain);
									}
									OnError(SMTP_ErrorType.InvalidEmailAddress,to,"email domain <" + domain + "> is invalid");
								
									defectiveEmails.AddRange(to);

									if(logger != null){
										logger.Flush();
									}
									return false;
								}

								try{
									if(logger != null){
										logger.AddTextEntry("Connecting with A record to:" + domain);
									}
									socket.Connect(new IPEndPoint(ipEntry.AddressList[0],m_Port));
								}
								catch{
									if(logger != null){
										logger.AddTextEntry("Failed connect to:" + domain);
									}
								}
							}
							break;

						case RCODE.NAME_ERROR:
							if(logger != null){
								logger.AddTextEntry("Invalid domain,no MX or A record: " + domain);
							}
							OnError(SMTP_ErrorType.InvalidEmailAddress,to,"email domain <" + domain + "> is invalid");
								
							defectiveEmails.AddRange(to);

							if(logger != null){
								logger.Flush();
							}
							return false;

						case RCODE.SERVER_FAILURE:
							if(logger != null){
								logger.AddTextEntry("Dns server unvailable.");
							}
							OnError(SMTP_ErrorType.UnKnown,to,"Dns server unvailable.");
								
							defectiveEmails.AddRange(to);

							if(logger != null){
								logger.Flush();
							}
							return false;
					}					
				}

				if(!socket.Connected){
					OnError(SMTP_ErrorType.UnKnown,to,"Unable connect to server !");

					if(logger != null){
						logger.Flush();
					}
					return false;
				}
										

				#region Get 220 reply from server 
				/* NOTE: reply may be multiline
				   220 xx ready
				    or
				   220-someBull
				   200 xx
				*/ 

				// Server must reply 220 - Server OK
				reply = socket.ReadLine();				
				if(!IsReplyCode("220",reply)){
					OnError(SMTP_ErrorType.UnKnown,to,reply);
					socket.SendLine("QUIT");

					if(logger != null){
						logger.Flush();
					}
					return false;
				}
				else{
					// 220-xxx<CRLF>
					// 220 aa<CRLF> - means end
					// reply isn't complete, get more
					while(reply.IndexOf("220 ") == -1){
						reply += socket.ReadLine();
					}
				}

				#endregion


				#region cmd EHLO/HELO
	
				// Send greeting to server
				socket.SendLine("EHLO " + m_HostName);

				reply = socket.ReadLine();
				if(!IsReplyCode("250",reply)){
					// EHLO failed, mayby server doesn't support it, try HELO
					socket.SendLine("HELO " + m_HostName);
					reply = socket.ReadLine();
					if(!IsReplyCode("250",reply)){
						OnError(SMTP_ErrorType.UnKnown,to,reply);
						socket.SendLine("QUIT");

						defectiveEmails.AddRange(to);

						if(logger != null){
							logger.Flush();
						}
						return false;
					}
				//	else{
				//		supports_ESMTP = false;
				//	}
				}
				else{
					// 250-xxx<CRLF>
					// 250 aa<CRLF> - means end
					// reply isn't complete, get more
					while(reply.IndexOf("250 ") == -1){
						reply += socket.ReadLine();
					}

					// Check if SIZE argument is supported
					if(reply.ToUpper().IndexOf("SIZE") > -1){
						supports_SIZE = true;
					}

					// Check if 8BITMIME argument is supported
					if(reply.ToUpper().IndexOf("8BITMIME") > -1){
						supports_8BIT = true;
					}
					
					// Check if CHUNKING argument is supported
					if(reply.ToUpper().IndexOf("CHUNKING") > -1){
						supports_BDAT = true;
					}
				}
				
				#endregion
	
                //*** All server today support 8-bit, just skip it.

				// If server doesn't support 8bit mime, check if message is 8bit.
				// If is we MAY NOT send this message or loss of data
		/*		if(!supports_8BIT){
					if(Is8BitMime(message)){
						OnError(SMTP_ErrorType.NotSupported,to,"Message is 8-Bit mime and server doesn't support it.");
						socket.SendLine("QUIT");

						if(logger != null){
							logger.Flush();
						}
						return false;	
					}
				}*/


				#region cmd AUTH
	
				if (this.m_Username != null && m_Username.Length > 0 && m_Password != null && m_Password.Length > 0){
					if(reply.ToUpper().IndexOf("AUTH") > -1){
						if(reply.ToUpper().IndexOf("LOGIN") > -1){
							socket.SendLine("AUTH LOGIN");
							reply = socket.ReadLine();
							if(!IsReplyCode("334",reply)){
								OnError(SMTP_ErrorType.NotAuthenticated,to,"Failed to authenticate");
								socket.SendLine("QUIT");

								if(logger != null){
									logger.Flush();
								}
								return false;
							}

							socket.SendLine(Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(m_Username.ToCharArray())));
							reply = socket.ReadLine();
							if(!IsReplyCode("334",reply)){
								OnError(SMTP_ErrorType.NotAuthenticated,to,"Failed to authenticate");
								socket.SendLine("QUIT");

								if(logger != null){
									logger.Flush();
								}
								return false;
							}

							socket.SendLine(Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes(m_Password.ToCharArray())));
							reply = socket.ReadLine();
							if(!IsReplyCode("235",reply)){
								OnError(SMTP_ErrorType.NotAuthenticated,to,"Failed to authenticate");
								socket.SendLine("QUIT");

								if(logger != null){
									logger.Flush();
								}
								return false;
							}
						}
//						if(reply.ToUpper().IndexOf("CRAM-MD5") > -1)
//						{
//							socket.SendLine("AUTH CRAM-MD5");
//							reply = socket.ReadLine();
//							if (IsReplyCode("334",auth))
//							{
//								socket.SendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(m_Username.ToCharArray())));
//								socket.SendLine(Convert.ToBase64String(Encoding.ASCII.GetBytes(m_Password.ToCharArray())));
//							}
//						}
					}
					else{
						//server did not support AUTH
					}
				}

				#endregion

				#region cmd MAIL
				// NOTE: Syntax:{MAIL FROM:<*****@*****.**> [SIZE=msgSize]<CRLF>}
								
				// Send Mail From
				if(supports_SIZE){
					socket.SendLine("MAIL FROM:<" + reverse_path + "> SIZE=" + (message.Length - message.Position));
				}
				else{
					socket.SendLine("MAIL FROM:<" + reverse_path + ">");
				}

				reply = socket.ReadLine();
				if(!IsReplyCode("250",reply)){
					// To Do: Check if size exceeded error:

					OnError(SMTP_ErrorType.UnKnown,to,reply);
					socket.SendLine("QUIT");

					defectiveEmails.AddRange(to);

					if(logger != null){
						logger.Flush();
					}
					return false;
				}

				#endregion

				#region cmd RCPT
				// NOTE: Syntax:{RCPT TO:<*****@*****.**><CRLF>}
				
				bool isAnyValidEmail = false;
				foreach(string rcpt in to){
					// Send Mail To
					socket.SendLine("RCPT TO:<" + rcpt + ">");

					reply = socket.ReadLine();
					if(!IsReplyCode("250",reply)){
						// Is unknown user
						if(IsReplyCode("550",reply)){							
							OnError(SMTP_ErrorType.InvalidEmailAddress,new string[]{rcpt},reply);
						}
						else{
							OnError(SMTP_ErrorType.UnKnown,new string[]{rcpt},reply);
						}

						defectiveEmails.Add(rcpt);
					}
					else{
						isAnyValidEmail = true;
					}
				}

				// If there isn't any valid email - quit.
				if(!isAnyValidEmail){
					socket.SendLine("QUIT");

					if(logger != null){
						logger.Flush();
					}
					return false;
				}
				//---------------------------------------------//

				#endregion


				#region cmd DATA

				if(!(supports_BDAT && m_UseBDAT)){

					// Notify Data Start
					socket.SendLine("DATA");

					reply = socket.ReadLine();
					if(!IsReplyCode("354",reply)){
						OnError(SMTP_ErrorType.UnKnown,to,reply);
						socket.SendLine("QUIT");

						defectiveEmails.AddRange(to);

						if(logger != null){
							logger.Flush();
						}
						return false;
					}
								
					//------- Do period handling -----------------------------------------//
					// If line starts with '.', add additional '.'.(Read rfc for more info)
					MemoryStream msgStrmPeriodOk = Core.DoPeriodHandling(message,true,false);
					//--------------------------------------------------------------------//
					
					// Check if message ends with <CRLF>, if not add it. -------//
					if(msgStrmPeriodOk.Length >= 2){
						byte[] byteEnd = new byte[2];
						msgStrmPeriodOk.Position = msgStrmPeriodOk.Length - 2;
						msgStrmPeriodOk.Read(byteEnd,0,2);

						if(byteEnd[0] != (byte)'\r' && byteEnd[1] != (byte)'\n'){
							msgStrmPeriodOk.Write(new byte[]{(byte)'\r',(byte)'\n'},0,2);
						}
					}
					msgStrmPeriodOk.Position = 0;
					//-----------------------------------------------------------//

					//---- Send message --------------------------------------------//
					long totalSent   = 0;
					long totalLength = msgStrmPeriodOk.Length; 
					while(totalSent < totalLength){
						byte[] dataBuf = new byte[4000];
						int nCount = msgStrmPeriodOk.Read(dataBuf,0,dataBuf.Length);
						int countSended = socket.Send(dataBuf,0,nCount,SocketFlags.None);					
						totalSent += countSended;

						if(countSended != nCount){
							msgStrmPeriodOk.Position = totalSent;
						}

						OnPartOfMessageIsSent(countSended,totalSent,totalLength);
					}
					//-------------------------------------------------------------//
					msgStrmPeriodOk.Close();
			
					// Notify End of Data
					socket.SendLine(".");

					reply = socket.ReadLine();
					if(!IsReplyCode("250",reply)){
						OnError(SMTP_ErrorType.UnKnown,to,reply);
						socket.SendLine("QUIT");

						defectiveEmails.AddRange(to);

						if(logger != null){
							logger.Flush();
						}
						return false;
					}
				}

				#endregion

				#region cmd BDAT

				if(supports_BDAT && m_UseBDAT){
					socket.SendLine("BDAT " + (message.Length - message.Position) + " LAST");

					//---- Send message --------------------------------------------//
					long totalSent   = 0;
					long totalLength = message.Length - message.Position;
					while(totalSent < totalLength){
						byte[] dataBuf = new byte[4000];
						int nCount = message.Read(dataBuf,0,dataBuf.Length);
						int countSended = socket.Send(dataBuf,0,nCount,SocketFlags.None);					
						totalSent += countSended;

						if(countSended != nCount){
							message.Position = totalSent;
						}

						OnPartOfMessageIsSent(countSended,totalSent,totalLength);
					}
					//-------------------------------------------------------------//

					// Get store result
					reply = socket.ReadLine();
					if(!reply.StartsWith("250")){
						OnError(SMTP_ErrorType.UnKnown,to,reply);
						socket.SendLine("QUIT");

						defectiveEmails.AddRange(to);

						if(logger != null){
							logger.Flush();
						}
						return false;
					}
				}

				#endregion

				#region cmd QUIT

				// Notify server - server can exit now
				socket.SendLine("QUIT");

			//	reply = socket.ReadLine();

				#endregion
				
			}
			catch(Exception x){
				OnError(SMTP_ErrorType.UnKnown,to,x.Message);

				defectiveEmails.AddRange(to);

				if(logger != null){
					logger.Flush();
				}
				return false;
			}
			finally{
				// Raise event
				OnSendJobCompleted(Thread.CurrentThread.GetHashCode().ToString(),to,defectiveEmails);
			}

			if(logger != null){
				logger.Flush();
			}

			return true;
		}
예제 #4
0
		/// <summary>
		/// Resolves a DNS host name or IP to IPAddress[].
		/// </summary>
		/// <param name="hostName_IP">Host name or IP address.</param>
		/// <returns></returns>
		public static IPAddress[] Resolve(string hostName_IP)
		{
			// If hostName_IP is IP
			try{
				return new IPAddress[]{IPAddress.Parse(hostName_IP)};
			}
			catch{
			}

			// This is probably NetBios name
			if(hostName_IP.IndexOf(".") == -1){
				return System.Net.Dns.Resolve(hostName_IP).AddressList;
			}
			else{
				// hostName_IP must be host name, try to resolve it's IP
				Dns_Client dns = new Dns_Client();
				DnsServerResponse resp = dns.Query(hostName_IP,QTYPE.A);
				if(resp.ResponseCode == RCODE.NO_ERROR){
					A_Record[] records = resp.GetARecords();
					IPAddress[] retVal = new IPAddress[records.Length];
					for(int i=0;i<records.Length;i++){
						retVal[i] = IPAddress.Parse(records[i].IP);
					}

					return retVal;
				}
				else{
					throw new Exception(resp.ResponseCode.ToString());
				}
			}
		}