public void ReadMessageTest() { var responses = new string[] { "From: \"Bob Example\" < *****@*****.** >", "To: \"Alice Example\" < *****@*****.** >", "Cc: theboss @example.com", "Date: Tue, 15 January 2008 16:02:43 - 0500", "Subject: Test message", "", "Hello Alice.", "This is a test message with 5 header fields and 4 lines in the message body.", "Your friend,", "Bob", "." }; SetResponses(responses); var provider = new MailMessageReadProvider(); var context = new SmtpSessionContext() { Transport = _transportMock.Object }; var result = provider.RunAsync(context).Result as MessageProcessingResult; Assert.IsNotNull(result); }
public void Setup() { SmtpSessionContext context = new SmtpSessionContext(); Mock <ITransport> transportMock = new Mock <ITransport>(MockBehavior.Loose); _handshakeProviderMock = CreateProvider(context, new Result { StatusCode = SmtpStatusCode.OK }); _senderValidationProviderMock = CreateProvider(context, new Result { StatusCode = SmtpStatusCode.OK }); _recipientValidationProviderMock = CreateProvider(context, new Result { StatusCode = SmtpStatusCode.OK }); _messageHandlerMock = CreateProvider(context, new Result { StatusCode = SmtpStatusCode.OK }); _authProviderMock = CreateProvider(context, new Result { StatusCode = SmtpStatusCode.OK }); _session = new SmtpSession(transportMock.Object) { HandshakeProvider = _handshakeProviderMock.Object, SenderValidationProvider = _senderValidationProviderMock.Object, RecipientValidationProvider = _recipientValidationProviderMock.Object, MessageReaderProvider = _messageHandlerMock.Object, AuthProvider = _authProviderMock.Object }; _session.StartSession().Wait(); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> internal override async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { switch (Method) { case AuthenticationMethod.Plain: if (await TryPlainAsync(context, cancellationToken).ReturnOnAnyThread() == false) { await context.Client.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ReturnOnAnyThread(); return; } break; case AuthenticationMethod.Login: if (await TryLoginAsync(context, cancellationToken).ReturnOnAnyThread() == false) { await context.Client.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ReturnOnAnyThread(); return; } break; } if (await Options.UserAuthenticator.AuthenticateAsync(_user, _password).ReturnOnAnyThread() == false) { await context.Client.ReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ReturnOnAnyThread(); return; } await context.Client.ReplyAsync(SmtpResponse.AuthenticationSuccessful, cancellationToken).ReturnOnAnyThread(); context.RaiseSessionAuthenticated(); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> internal override async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { if (context.Transaction.To.Count == 0) { await context.Client.ReplyAsync(SmtpResponse.NoValidRecipientsGiven, cancellationToken).ConfigureAwait(false); return; } await context.Client.ReplyAsync(new SmtpResponse(SmtpReplyCode.StartMailInput, "end with <CRLF>.<CRLF>"), cancellationToken).ConfigureAwait(false); context.Transaction.Message = await ReadMessageAsync(context, cancellationToken).ConfigureAwait(false); try { // store the transaction using (var container = new DisposableContainer <IMessageStore>(Options.MessageStoreFactory.CreateInstance(context))) { var response = await container.Instance.SaveAsync(context, context.Transaction, cancellationToken).ConfigureAwait(false); await context.Client.ReplyAsync(response, cancellationToken).ConfigureAwait(false); } } catch (Exception) { await context.Client.ReplyAsync(new SmtpResponse(SmtpReplyCode.TransactionFailed), cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { using (var container = new DisposableContainer <IMailboxFilter>(Options.MailboxFilterFactory.CreateInstance(context))) { switch (await container.Instance.CanDeliverToAsync(context, Address, context.Transaction.From, cancellationToken).ConfigureAwait(false)) { case MailboxFilterResult.Yes: context.Transaction.To.Add(Address); await context.NetworkClient.ReplyAsync(SmtpResponse.Ok, cancellationToken).ConfigureAwait(false); return(true); case MailboxFilterResult.NoTemporarily: await context.NetworkClient.ReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken).ConfigureAwait(false); return(false); case MailboxFilterResult.NoPermanently: await context.NetworkClient.ReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken).ConfigureAwait(false); return(false); } } throw new NotSupportedException("The Acceptance state is not supported."); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var mailboxFilter = context.ServiceProvider.GetService <IMailboxFilterFactory, IMailboxFilter>(context, MailboxFilter.Default); using var container = new DisposableContainer <IMailboxFilter>(mailboxFilter); switch (await container.Instance.CanDeliverToAsync(context, Address, context.Transaction.From, cancellationToken).ConfigureAwait(false)) { case MailboxFilterResult.Yes: context.Transaction.To.Add(Address); await context.Pipe.Output.WriteReplyAsync(SmtpResponse.Ok, cancellationToken).ConfigureAwait(false); return(true); case MailboxFilterResult.NoTemporarily: await context.Pipe.Output.WriteReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken).ConfigureAwait(false); return(false); case MailboxFilterResult.NoPermanently: await context.Pipe.Output.WriteReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken).ConfigureAwait(false); return(false); } throw new NotSupportedException("The Acceptance state is not supported."); }
public IResult Run(SmtpSessionContext context) { var transport = context.Transport; transport.SendFormat("220 {0} SMTP server ready.", context.ServerName); string response = transport.Read(); if (!response.StartsWith("HELO") && !response.StartsWith("EHLO")) { var errorResult = new HandshakeResult { StatusCode = SmtpStatusCode.UNKNOWN_COMMAND, StatusReason = "Unknow Command" }; return(errorResult); } string client = response.Replace("HELO", string.Empty).Replace("EHLO", string.Empty).Trim(); transport.Send("250-localhost"); var result = new HandshakeResult { StatusCode = SmtpStatusCode.OK, RemoteClient = client }; return(result); }
public void BadUsernameOrPassword() { var testUsername = "******"; var testPassword = "******"; SetResponses(testUsername, testPassword); ResetCounter(); SetupTransport(); SetupAuthProvider(false); SmtpSessionContext context = new SmtpSessionContext { AuthProvider = _authProvider.Object, Transport = _transportMock.Object }; PlainTextLoginProvider provider = new PlainTextLoginProvider(); var result = provider.RunAsync(context).Result; var authresult = result as UserAuthenticationResult; Assert.IsNotNull(authresult); Assert.AreEqual(SmtpStatusCode.MAILBOX_NOT_FOUND, authresult.StatusCode); Assert.AreEqual(testUsername, authresult.Username); }
public IResult Run(SmtpSessionContext context) { var transport = context.Transport; int counter = 0; StringBuilder message = new StringBuilder(); string response; while ((response = transport.Read().Trim()) != ".") { message.AppendLine(response); counter++; if (counter == 1000000) { transport.Send(SmtpStatusCode.INSUFFICIENT_STORAGE, "MESSAGE TO LARGE"); var errorResult = new MessageProcessingResult { StatusCode = SmtpStatusCode.LOCAL_PROCESSING_ERROR, StatusReason = "Message size exceeds limit" }; return(errorResult); } } transport.Send(SmtpStatusCode.OK, "OK"); var result = new MessageProcessingResult { StatusCode = SmtpStatusCode.OK, StatusReason = "Message recieved" }; return(result); }
public void RecipientValidationSucessTest() { var provider = new RecipientValidationProvider(); var transportMoq = new Mock <ITransport>(MockBehavior.Loose); const string recipientAddress = "*****@*****.**"; SetResponses(string.Format("RCPT TO: {0}", recipientAddress), "DATA"); Mock <IMessageRecipientValidator> validatorMock = new Mock <IMessageRecipientValidator>(); validatorMock.Setup(s => s.ValidateRecipient(It.IsAny <string>())).Returns(true); var context = new SmtpSessionContext() { Transport = _transportMock.Object, RecipientValidator = validatorMock.Object }; var result = provider.RunAsync(context).Result as RecipientValidationResult; Assert.IsNotNull(result); Assert.AreEqual(SmtpStatusCode.OK, result.StatusCode); Assert.IsNotNull(result.Recipients); List <string> reciientList = new List <string>(result.Recipients); Assert.IsTrue(reciientList.Count == 1); Assert.AreEqual(recipientAddress, reciientList[0]); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { context.Transaction.Reset(); await context.NetworkClient.ReplyAsync(SmtpResponse.Ok, cancellationToken).ConfigureAwait(false); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { await context.NetworkClient.ReplyAsync(SmtpResponse.ServiceReady, cancellationToken).ConfigureAwait(false); await context.NetworkClient.Stream.UpgradeAsync(Options.ServerCertificate, Options.SupportedSslProtocols, cancellationToken).ConfigureAwait(false); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var response = new SmtpResponse(SmtpReplyCode.Ok, GetGreeting(context)); await context.Pipe.Output.WriteReplyAsync(response, cancellationToken).ConfigureAwait(false); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { context.IsQuitRequested = true; await context.NetworkClient.ReplyAsync(SmtpResponse.ServiceClosingTransmissionChannel, cancellationToken).ReturnOnAnyThread(); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var response = new SmtpResponse(SmtpReplyCode.Ok, $"Hello {DomainOrAddress}, haven't we met before?"); await context.NetworkClient.ReplyAsync(response, cancellationToken).ReturnOnAnyThread(); return(true); }
/// <inheritdoc /> internal override Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { context.Properties.Add(ProxySourceEndpointKey, SourceEndpoint); context.Properties.Add(ProxyDestinationEndpointKey, DestinationEndpoint); // Do not transition smtp protocol state for these commands. return(Task.FromResult(false)); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> internal override async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { await context.Client.ReplyAsync(SmtpResponse.ServiceReady, cancellationToken); await context.Client.UpgradeAsync(Options.ServerCertificate, Options.SupportedSslProtocols, cancellationToken); context.IsSecure = true; }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { context.IsQuitRequested = true; await context.Pipe.Output.WriteReplyAsync(SmtpResponse.ServiceClosingTransmissionChannel, cancellationToken).ConfigureAwait(false); return(true); }
/// <summary> /// Returns a value indicating whether or not plain login is allowed. /// </summary> /// <param name="session">The current session.</param> /// <returns>true if plain login is allowed for the session, false if not.</returns> bool IsPlainLoginAllowed(SmtpSessionContext session) { if (Options.UserAuthenticatorFactory == null) { return(false); } return(session.IsSecure || Options.AllowUnsecureAuthentication); }
/// <summary> /// Returns a value indicating whether or not plain login is allowed. /// </summary> /// <param name="session">The current session.</param> /// <returns>true if plain login is allowed for the session, false if not.</returns> bool IsPlainLoginAllowed(SmtpSessionContext session) { if (Options.UserAuthenticatorFactory == null) { return(false); } return(session.NetworkClient.IsSecure || session.EndpointDefinition.AllowUnsecureAuthentication); }
private static Mock <ISmtpSessionProvider> CreateProvider(SmtpSessionContext ItAny, IResult result) { Mock <ISmtpSessionProvider> providerMock = new Mock <ISmtpSessionProvider>(); providerMock.Setup(s => s.RunAsync(It.IsAny <SmtpSessionContext>(), It.IsAny <CancellationToken>())) .Returns(Task.FromResult <IResult>(result)) .Verifiable("Was not called"); return(providerMock); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { await context.Pipe.Output.WriteReplyAsync(SmtpResponse.ServiceReady, cancellationToken).ConfigureAwait(false); var certificate = context.EndpointDefinition.ServerCertificate; var protocols = context.EndpointDefinition.SupportedSslProtocols; await context.Pipe.UpgradeAsync(certificate, protocols, cancellationToken).ConfigureAwait(false); return(true); }
/// <summary> /// Attempt a LOGIN login sequence. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>true if the LOGIN login sequence worked, false if not.</returns> async Task <bool> TryLoginAsync(SmtpSessionContext context, CancellationToken cancellationToken) { await context.Client.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, "VXNlcm5hbWU6"), cancellationToken); _user = await ReadBase64EncodedLineAsync(context.Client, cancellationToken).ReturnOnAnyThread(); await context.Client.ReplyAsync(new SmtpResponse(SmtpReplyCode.ContinueWithAuth, "UGFzc3dvcmQ6"), cancellationToken); _password = await ReadBase64EncodedLineAsync(context.Client, cancellationToken).ReturnOnAnyThread(); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { if (context.EndpointDefinition.AuthenticationRequired && context.Authentication.IsAuthenticated == false) { await context.Pipe.Output.WriteReplyAsync(SmtpResponse.AuthenticationRequired, cancellationToken).ConfigureAwait(false); return(false); } context.Transaction.Reset(); context.Transaction.Parameters = Parameters; // check if a size has been defined var size = GetMessageSize(); // check against the server supplied maximum if (context.ServerOptions.MaxMessageSize > 0 && size > context.ServerOptions.MaxMessageSize) { await context.Pipe.Output.WriteReplyAsync(SmtpResponse.SizeLimitExceeded, cancellationToken).ConfigureAwait(false); return(false); } var mailboxFilter = context.ServiceProvider.GetService <IMailboxFilterFactory, IMailboxFilter>(context, MailboxFilter.Default); using var container = new DisposableContainer <IMailboxFilter>(mailboxFilter); switch (await container.Instance.CanAcceptFromAsync(context, Address, size, cancellationToken).ConfigureAwait(false)) { case MailboxFilterResult.Yes: context.Transaction.From = Address; await context.Pipe.Output.WriteReplyAsync(SmtpResponse.Ok, cancellationToken).ConfigureAwait(false); return(true); case MailboxFilterResult.NoTemporarily: await context.Pipe.Output.WriteReplyAsync(SmtpResponse.MailboxUnavailable, cancellationToken).ConfigureAwait(false); return(false); case MailboxFilterResult.NoPermanently: await context.Pipe.Output.WriteReplyAsync(SmtpResponse.MailboxNameNotAllowed, cancellationToken).ConfigureAwait(false); return(false); case MailboxFilterResult.SizeLimitExceeded: await context.Pipe.Output.WriteReplyAsync(SmtpResponse.SizeLimitExceeded, cancellationToken).ConfigureAwait(false); return(false); } throw new SmtpResponseException(SmtpResponse.TransactionFailed); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occur, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { context.Authentication = AuthenticationContext.Unauthenticated; switch (Method) { case AuthenticationMethod.Plain: if (await TryPlainAsync(context, cancellationToken).ConfigureAwait(false) == false) { await context.Pipe.Output.WriteReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return(false); } break; case AuthenticationMethod.Login: if (await TryLoginAsync(context, cancellationToken).ConfigureAwait(false) == false) { await context.Pipe.Output.WriteReplyAsync(SmtpResponse.AuthenticationFailed, cancellationToken).ConfigureAwait(false); return(false); } break; } var userAuthenticator = context.ServiceProvider.GetService <IUserAuthenticatorFactory, IUserAuthenticator>(context, UserAuthenticator.Default); using (var container = new DisposableContainer <IUserAuthenticator>(userAuthenticator)) { if (await container.Instance.AuthenticateAsync(context, _user, _password, cancellationToken).ConfigureAwait(false) == false) { var remaining = context.ServerOptions.MaxAuthenticationAttempts - ++context.AuthenticationAttempts; var response = new SmtpResponse(SmtpReplyCode.AuthenticationFailed, $"authentication failed, {remaining} attempt(s) remaining."); await context.Pipe.Output.WriteReplyAsync(response, cancellationToken).ConfigureAwait(false); if (remaining <= 0) { throw new SmtpResponseException(SmtpResponse.ServiceClosingTransmissionChannel, true); } return(false); } } await context.Pipe.Output.WriteReplyAsync(SmtpResponse.AuthenticationSuccessful, cancellationToken).ConfigureAwait(false); context.Authentication = new AuthenticationContext(_user); context.RaiseSessionAuthenticated(); return(true); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>A task which asynchronously performs the execution.</returns> internal override async Task ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var greeting = $"{Options.ServerName} Hello {DomainOrAddress}, haven't we met before?"; var output = new[] { greeting }.Union(GetExtensions(context)).ToArray(); for (var i = 0; i < output.Length - 1; i++) { await context.Client.WriteLineAsync($"250-{output[i]}", cancellationToken); } await context.Client.WriteLineAsync($"250 {output[output.Length - 1]}", cancellationToken); await context.Client.FlushAsync(cancellationToken); }
/// <summary> /// Execute the command. /// </summary> /// <param name="context">The execution context to operate on.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Returns true if the command executed successfully such that the transition to the next state should occurr, false /// if the current state is to be maintained.</returns> internal override async Task <bool> ExecuteAsync(SmtpSessionContext context, CancellationToken cancellationToken) { var output = new[] { GetGreeting(context) }.Union(GetExtensions(context)).ToArray(); for (var i = 0; i < output.Length - 1; i++) { context.Pipe.Output.WriteLine($"250-{output[i]}"); } context.Pipe.Output.WriteLine($"250 {output[output.Length - 1]}"); await context.Pipe.Output.FlushAsync(cancellationToken).ConfigureAwait(false); return(true); }
public void SenderValidationBadRequest() { var provider = new SenderValidationProvider(); var transportMoq = new Mock <ITransport>(MockBehavior.Loose); var context = new SmtpSessionContext() { Transport = transportMoq.Object }; transportMoq.Setup(m => m.Read()).Returns("BAD COMMAND"); var result = provider.RunAsync(context).Result as SenderValidationResult; Assert.IsNotNull(result, "Response is of the incorrect type"); Assert.AreEqual(SmtpStatusCode.UNKNOWN_COMMAND, result.StatusCode); }
public void BasicHandshakeBadResponse() { var provider = new HandshakeProvider(); var transportMoq = new Mock <ITransport>(); var context = new SmtpSessionContext() { ServerName = "localhost", Transport = transportMoq.Object }; transportMoq.Setup(m => m.Read()).Returns("HELLO"); var result = provider.RunAsync(context).Result; Assert.AreEqual(SmtpStatusCode.UNKNOWN_COMMAND, result.StatusCode); }
public async Task <IResult> RunAsync(SmtpSessionContext context, CancellationToken cancellationToken = new CancellationToken()) { var transport = context.Transport; var senderMessage = transport.Read(); if (!senderMessage.StartsWith("MAIL FROM:")) { transport.Send(SmtpStatusCode.UNKNOWN_COMMAND, "UNKNOW COMMAND"); var errorResult = new SenderValidationResult { StatusCode = SmtpStatusCode.UNKNOWN_COMMAND, StatusReason = "Unknown Command" }; return(errorResult); } else { var sender = senderMessage.Replace("MAIL FROM:", string.Empty).Trim(); var validationResult = context.SenderValidator.IsAuthorizedSender(sender); if (validationResult) { transport.Send(SmtpStatusCode.OK, "GO AHEAD"); var result = new SenderValidationResult { StatusCode = SmtpStatusCode.OK, StatusReason = "Sender Accepted", Sender = sender }; return(result); } else { transport.Send(SmtpStatusCode.MAILBOX_NOT_FOUND, "UNAUTHORIZED SENDER"); var result = new SenderValidationResult { StatusCode = SmtpStatusCode.MAILBOX_NOT_FOUND, StatusReason = "Sender Not Authorized", Sender = sender }; return(result); } } }