SmtpClient и проблема с отправкой почты

Столкнулся с проблемой отправки почты при помощи класса SmtpClient, входящего в состав .NET

Казалось бы, чего проще … пишем вот такой код:

SmtpClient client = new SmtpClient("smtp.server.name");
MailMessage message = new MailMessage(
  "sender@domain.com",
  "receiver@domain.com",
  "Test message", "body content");
client.Send(message);

Запускаем и получаем вот такую ошибку:

The message was rejected because of a forged HELO name – MyPCName

Происходит это из за того, что мой SMTP сервер в целях борьбы со спамом требует наличия корректного имени или адреса в команде HELO. Причем адрес с которого установлено соединение должен совпадать с адресом, указанным в команде. То есть сервер ожидает что то вроде “HELO client.domain.com” а вместо этого получает “HELO MyPCName” и не может определить адрес клиента. MyPCName – сетевое имя компьютера, который пытается отправить сообщение.

К сожалению, SmtpClient не поддерживает изменение имени домена отправителя, которое попадает в HELO. То есть стандартными методами проблему решить не удастся.

В сети не так много информации на эту тему. Описание проблемы найти можно, описание причины проблемы тоже. Но найти решение, работающее в .NET приложении оказалось не так просто. Внутри SmtpClient нужное нам значение хранится в одной из переменных класса. Однако эта переменная объявлена как private и напрямую изменить ее значение невозможно.

Вот по этой ссылке Richard Deeming предлагает свое решение, основанное на Reflection. Решение хорошее, но приведенный там код не может работать без изменений, так как в моем случае переменная в классе SmtpClient по какой то причине сменила свое название. После небольшой доработки мне все таки удалось добиться отправки сообщений через мой SMTP.

Модифицированный код теперь выглядит так:

using System;
using System.Net.Mail;
using System.Net.NetworkInformation;
using System.Reflection;
namespace Trinet.Net.Mail
{
  /// <summary>
  /// An extended <see cref="SmtpClient"/> which sends the
  /// FQDN of the local machine in the EHLO/HELO command.
  /// </summary>
  public class SmtpClientEx : SmtpClient
  {
      #region Private Data
      private static readonly FieldInfo localHostName = GetLocalHostNameField();
      #endregion
      #region Constructor
      /// <summary>
      /// Initializes a new instance of the <see cref="SmtpClientEx"/> class
      /// that sends e-mail by using the specified SMTP server and port.
      /// </summary>
      /// <param name="host">
      /// A <see cref="String"/> that contains the name or 
      /// IP address of the host used for SMTP transactions.
      /// </param>
      /// <param name="port">
      /// An <see cref="Int32"/> greater than zero that 
      /// contains the port to be used on host.
      /// </param>
      /// <exception cref="ArgumentNullException">
      /// <paramref name="port"/> cannot be less than zero.
      /// </exception>
      public SmtpClientEx(string host, int port) : base(host, port)
      {
          Initialize();
      }
      /// <summary>
      /// Initializes a new instance of the <see cref="SmtpClientEx"/> class
      /// that sends e-mail by using the specified SMTP server.
      /// </summary>
      /// <param name="host">
      /// A <see cref="String"/> that contains the name or 
      /// IP address of the host used for SMTP transactions.
      /// </param>
      public SmtpClientEx(string host) : base(host)
      {
          Initialize();
      }
      /// <summary>
      /// Initializes a new instance of the <see cref="SmtpClientEx"/> class
      /// by using configuration file settings.
      /// </summary>
      public SmtpClientEx()
      {
          Initialize();
      }
      #endregion
      #region Properties
      /// <summary>
      /// Gets or sets the local host name used in SMTP transactions.
      /// </summary>
      /// <value>
      /// The local host name used in SMTP transactions.
      /// This should be the FQDN of the local machine.
      /// </value>
      /// <exception cref="ArgumentNullException">
      /// The property is set to a value which is
      /// <see langword="null"/> or <see cref="String.Empty"/>.
      /// </exception>
      public string LocalHostName
      {
           get
           {
              if (null == localHostName) return null;
              return (string)localHostName.GetValue(this);
          }
          set
          {
              if (string.IsNullOrEmpty(value))
              {
                  throw new ArgumentNullException("value");
              }
              if (null != localHostName)
              {
                  localHostName.SetValue(this, value);
              }
          }
      }
      #endregion
      #region Methods
      /// <summary>
      /// Returns the price "localHostName" field.
      /// </summary>
      /// <returns>
      /// The <see cref="FieldInfo"/> for the private
      /// "localHostName" field.
      /// </returns>
      private static FieldInfo GetLocalHostNameField()
      {
          BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
          return typeof(SmtpClient).GetField("clientDomain", flags);
      }
      /// <summary>
      /// Initializes the local host name to 
      /// the FQDN of the local machine.
      /// </summary>
      private void Initialize()
      {
          IPGlobalProperties ip = IPGlobalProperties.GetIPGlobalProperties();
          if (!string.IsNullOrEmpty(ip.HostName) && !string.IsNullOrEmpty(ip.DomainName))
          {
              this.LocalHostName = ip.HostName + "." + ip.DomainName;
          }
      }
      #endregion
  }
}

Код отправки почты также немного изменился:

SmtpClientEx client = new SmtpClientEx("smtp.server.name");
client.LocalHostName = "client.domain.com";
MailMessage message = new MailMessage(
     "sender@domain.com",
     "receiver@domain.com",
     "Test message", "body content");
client.Send(message);

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *