MIME::Lite 包升级后在命令通道错误上抛出意外的 EOF

MIME::Lite throwing unexpected EOF on command channel error after package upgrade

我在 Windows 2008 服务器上使用 Strawberry Perl (v5.16.3)。我添加了一些用于脚本的 CPAN 模块,但这似乎已损坏 MIME:Lite(此框上的其他脚本使用了它)。当我运行以下代码时:

use strict;
use warnings;
use v5.16;

use MIME::Lite;

my $msg = MIME::Lite->new(From     => 'person@domain.com',
                          To       => 'person@domain.com',
                          Subject  => "testing",
                          Type     => 'multipart/mixed');
my $dataString = "Trying to figure out why this isn't working.\r\n";
$msg->attach(  Type            => 'TEXT',
                  Data            => $dataString);  
$msg->send('smtp', 'mailhost.com', Debug=>1);

我收到以下错误消息:

MIME::Lite::SMTP>>> MIME::Lite::SMTP
MIME::Lite::SMTP>>>   Net::SMTP(3.10)
MIME::Lite::SMTP>>>     Net::Cmd(3.10)
MIME::Lite::SMTP>>>       Exporter(5.67)
MIME::Lite::SMTP>>>     IO::Socket::INET6(2.69)
MIME::Lite::SMTP>>>       IO::Socket(1.34)
MIME::Lite::SMTP>>>         IO::Handle(1.33)
MIME::Lite::SMTP: Net::Cmd::_is_closed(): unexpected EOF on command channel:  at d:/strawberry/perl/site/lib/MIME/Lite.pm line 2877.
SMTP Failed to connect to mail server: Bad file descriptor.

我在 Email::Simple、Email::Sender、Email::MIME::CreateHTML 和 String::Util 中安装的模块。我相信您知道,还安装了各种依赖项。

我只想将脚本切换到 Email::Sender,但服务器出现 421 错误(这又是一个谜)。我可以通过 telnet 连接到邮件服务器并获得 'HELO' 没有问题。

我担心 Perl 安装现在全部被顶起,但我希望你们中的一位聪明人可能对如何修复它有一些见解。

在此先感谢您的帮助。

编辑:我注意到 perl/lib 中的 Socket.pm 已更新。这可能是导致问题的原因吗?我将 MIME::Lite 更新到最新版本(版本 3.030),但这并没有解决问题。

编辑 #2:根据 ikegami 的回复,将打印语句添加到 NET::SMTP:

sub new {
    print "inside Net::SMTP::new\n";
  my $self = shift;
  my $type = ref($self) || $self;
  my ($host, %arg);
  if (@_ % 2) {
    $host = shift;
    %arg  = @_;
  }
  else {
    %arg  = @_;
    $host = delete $arg{Host};
  }

    print "checked for SSL.\n";
  if ($arg{SSL}) {
    # SSL from start
    die $nossl_warn if !$ssl_class;
    $arg{Port} ||= 465;
  }

  my $hosts = defined $host ? $host : $NetConfig{smtp_hosts};
  my $obj;

  $arg{Timeout} = 120 if ! defined $arg{Timeout};
    print "set timeout:  $arg{Timeout}. \n";

  foreach my $h (@{ref($hosts) ? $hosts : [$hosts]}) {
      print "host: $h, port: $arg{Port}, laddr: $arg{LocalAddr}, lport: $arg{LocalPort}, familyKey: " . ( $arg{Domain} || $arg{Family} ) . "\n";
    $obj = $type->SUPER::new(
      PeerAddr => ($host = $h),
      PeerPort => $arg{Port} || 'smtp(25)',
      LocalAddr => $arg{LocalAddr},
      LocalPort => $arg{LocalPort},
      $family_key => $arg{Domain} || $arg{Family},
      Proto     => 'tcp',
      Timeout   => $arg{Timeout}
      )
      and last;
  }
    print "$obj\n";
  return
    unless defined $obj;

    print "object defined. \n";
  ${*$obj}{'net_smtp_arg'} = \%arg;
  ${*$obj}{'net_smtp_host'} = $host;
    print "set net_smtp_host to $host\n";
  if ($arg{SSL}) {
      print "setting SSL. \n";
    Net::SMTP::_SSL->start_SSL($obj,%arg)
      or return;
  }

  $obj->autoflush(1);
    print "set autoflush.\n";
  $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef);
    print "setting debug: $arg{Debug}\n";
    my $response = $obj->response();
    print "object response: $response (5 == CMD_ERROR)\n";
  unless ($obj->response() == CMD_OK) {
    my $err = ref($obj) . ": " . $obj->code . " " . $obj->message;
    $obj->close();
    $@ = $err;
    print "returning because response was not CMD_OK\n";
    return;
  } ....

这是输出:

D:\strawberry>perl D:\Perl\Src\pmTasks\emailTest.pl
inside Net::SMTP::new
checked for SSL.
set timeout:  120.
Use of uninitialized value in concatenation (.) or string at D:/strawberry/perl/lib/Net/SMTP.pm line 83, <DATA> line 100
3.
Use of uninitialized value in concatenation (.) or string at D:/strawberry/perl/lib/Net/SMTP.pm line 83, <DATA> line 100
3.
Use of uninitialized value in concatenation (.) or string at D:/strawberry/perl/lib/Net/SMTP.pm line 83, <DATA> line 100
3.
Use of uninitialized value in concatenation (.) or string at D:/strawberry/perl/lib/Net/SMTP.pm line 83, <DATA> line 100
3.
host: {correct_mailhost_here}, port: , laddr: , lport: , familyKey:
MIME::Lite::SMTP=GLOB(0x1f36fbc)
object defined.
set net_smtp_host to {correct_mailhost_here}
set autoflush.
MIME::Lite::SMTP>>> MIME::Lite::SMTP
MIME::Lite::SMTP>>>   Net::SMTP(3.10)
MIME::Lite::SMTP>>>     Net::Cmd(3.10)
MIME::Lite::SMTP>>>       Exporter(5.67)
MIME::Lite::SMTP>>>     IO::Socket::INET6(2.69)
MIME::Lite::SMTP>>>       IO::Socket(1.34)
MIME::Lite::SMTP>>>         IO::Handle(1.33)
setting debug: 1
MIME::Lite::SMTP: Net::Cmd::_is_closed(): unexpected EOF on command channel:  at D:/strawberry/perl/site/lib/MIME/Lite.pm line 2877.
object response: 5 (5 == CMD_ERROR)
MIME::Lite::SMTP: Net::Cmd::_is_closed(): unexpected EOF on command channel:  at D:/strawberry/perl/site/lib/MIME/Lite.pm line 2877.
returning because response was not CMD_OK
SMTP Failed to connect to mail server: Bad file descriptor

所以,它返回的原因似乎是 $obj 的响应是 CMD_ERROR。它发送给 $obj=$type->SUPER::new 的唯一参数是主机。 (这是正确的主机,但我不得不在上面将其空白)。

所以,我不确定这是在调用哪个超类。它似乎从 NET::Cmd 返回常量,但没有 new 子例程。 IO::Socket:INET 从 IO::Socket 调用新的,从 IO::Handle 调用新的,似乎在调用 fdopen C 代码?我在这里有点迷路了。但是,感谢您到目前为止的帮助。看来我们几乎要找到问题的根源了。

Net::SMTP 版本 3.x 添加了对 SSL 和 IPv6 的支持。如果安装了必要的包(即 IO::Socket::IP 或 IO::Socket::INET6),它将使用 getaddrinfo 来解析通常优先使用 IPv6 优先于 IPv4 的名称。

如果一切设置正确,这不是问题。但在您的情况下,即使没有邮件服务器侦听 IPv6 地址或防火墙阻止连接,邮件服务器的主机名也会解析为 IPv6 和 IPv4 地址。因此导致连接失败,因为 getaddrinfo returns 应该使用 IPv6 但服务器无法通过 IPv6 访问。

正确的解决方法是删除主机的 IPv6 记录或使其可通过 IPv6 访问。只要不是这种情况,就可以通过在 Net::SMTP:

中明确强制执行 IPv4 来解决该问题
 use Socket;
 Net::SMTP->new(host, Domain => AF_INET,...);