MIME::Lite 在包升级后在命令通道错误上抛出意外的 EOF
Posted
技术标签:
【中文标题】MIME::Lite 在包升级后在命令通道错误上抛出意外的 EOF【英文标题】:MIME::Lite throwing unexpected EOF on command channel error after package upgrade 【发布时间】:2017-04-20 10:54:39 【问题描述】:我在 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 $argHost;
print "checked for SSL.\n";
if ($argSSL)
# SSL from start
die $nossl_warn if !$ssl_class;
$argPort ||= 465;
my $hosts = defined $host ? $host : $NetConfigsmtp_hosts;
my $obj;
$argTimeout = 120 if ! defined $argTimeout;
print "set timeout: $argTimeout. \n";
foreach my $h (@ref($hosts) ? $hosts : [$hosts])
print "host: $h, port: $argPort, laddr: $argLocalAddr, lport: $argLocalPort, familyKey: " . ( $argDomain || $argFamily ) . "\n";
$obj = $type->SUPER::new(
PeerAddr => ($host = $h),
PeerPort => $argPort || 'smtp(25)',
LocalAddr => $argLocalAddr,
LocalPort => $argLocalPort,
$family_key => $argDomain || $argFamily,
Proto => 'tcp',
Timeout => $argTimeout
)
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 ($argSSL)
print "setting SSL. \n";
Net::SMTP::_SSL->start_SSL($obj,%arg)
or return;
$obj->autoflush(1);
print "set autoflush.\n";
$obj->debug(exists $argDebug ? $argDebug : undef);
print "setting debug: $argDebug\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 代码?我在这里有点迷失了。但是,感谢您迄今为止的帮助。看来我们几乎要找到问题的根源了。
【问题讨论】:
也许您可以为您的问题添加更多调试输出。但鉴于“连接失败”,可能是您的邮件服务器具有 IPv6 地址和 IPv4 地址,并且 IPv6 设置不正确。但是如果主机名解析为 IPv6 地址,Net::SMTP 3.x(您已安装)将使用 IPv6。 它甚至还没有达到可以发送HELO
的地步。如果这样配置,它甚至无法连接,或者一旦连接就无法建立 SSL 握手。我会编辑Net/SMTP.pm
以获取更多信息。具体来说: 1) 哪个return
导致子new
退出? 2) 在foreach 循环的每一遍中,哪些参数被传递给$type->SUPER::new
?
@Steffen Ullrich - 所以,邮件服务器上没有任何变化。它一直在 IPv4 上。但是感谢您的想法,人们永远不会知道。 @ikegami - 我开始深入研究NET::SMTP
代码。请参阅我上面的编辑。谢谢你的想法。
@FrankRalphBob:这就是我的意思:邮件服务器仅支持 IPv4。但是,如果主机名也解析为 IPv6 地址,它将尝试这个并失败。旧版本的 Net::SMTP 根本不查找 IPv6,因此这些在那种情况下工作。如果邮件服务器是公共的,那么如果您可以提供主机名以便尝试重现问题,那将很有用。
@Steffen Ullrich - 对不起,我误解了你。不幸的是,邮件服务器不是公开的。有什么方法可以确定 Net::SMTP 是否使用 IPv6?还是强制 IPv4?感谢您的帮助!
【参考方案1】:
Net::SMTP 版本 3.x 添加了对 SSL 和 IPv6 的支持。如果安装了必要的包(即 IO::Socket::IP 或 IO::Socket::INET6),它将使用 getaddrinfo 来解析通常在 IPv4 之前首选 IPv6 的名称。
如果一切设置正确,这不是问题。但是在您的情况下,即使没有邮件服务器正在侦听 IPv6 地址或防火墙阻止了连接,邮件服务器的主机名也会解析为 IPv6 和 IPv4 地址。因此导致连接失败,因为 getaddrinfo 返回应使用 IPv6 但 IPv6 无法访问服务器。
正确的解决方法是删除主机的 IPv6 记录或使其可由 IPv6 访问。只要不是这种情况,就可以通过在 Net::SMTP 中明确实施 IPv4 来解决该问题:
use Socket;
Net::SMTP->new(host, Domain => AF_INET,...);
【讨论】:
以上是关于MIME::Lite 在包升级后在命令通道错误上抛出意外的 EOF的主要内容,如果未能解决你的问题,请参考以下文章
使用 smtp 不使用 perl MIME::Lite 发送电子邮件