diff options
-rw-r--r-- | .gitignore | 15 | ||||
-rw-r--r-- | Changes | 33 | ||||
-rw-r--r-- | Config.eg | 62 | ||||
-rwxr-xr-x | Configure | 56 | ||||
-rw-r--r-- | MANIFEST | 22 | ||||
-rw-r--r-- | Makefile.PL | 254 | ||||
-rw-r--r-- | Net/Cmd.pm | 3 | ||||
-rw-r--r-- | Net/Config.pm | 8 | ||||
-rw-r--r-- | Net/POP3.pm | 93 | ||||
-rw-r--r-- | Net/SMTP.pm | 129 | ||||
-rw-r--r-- | Net/Time.pm | 6 | ||||
-rw-r--r-- | README | 12 | ||||
-rw-r--r-- | t/config.t | 44 | ||||
-rw-r--r-- | t/datasend.t | 6 | ||||
-rw-r--r-- | t/external/pop3-ssl.t | 54 | ||||
-rw-r--r-- | t/external/smtp-ssl.t | 53 | ||||
-rw-r--r-- | t/ftp.t | 8 | ||||
-rw-r--r-- | t/hostname.t | 6 | ||||
-rw-r--r-- | t/libnet_t.pl | 42 | ||||
-rw-r--r-- | t/netrc.t | 100 | ||||
-rw-r--r-- | t/nntp.t | 8 | ||||
-rw-r--r-- | t/pop3_ipv6.t | 57 | ||||
-rw-r--r-- | t/pop3_ssl.t | 122 | ||||
-rw-r--r-- | t/require.t | 6 | ||||
-rw-r--r-- | t/smtp.t | 8 | ||||
-rw-r--r-- | t/smtp_ipv6.t | 59 | ||||
-rw-r--r-- | t/smtp_ssl.t | 124 | ||||
-rw-r--r-- | t/time.t | 82 |
28 files changed, 1107 insertions, 365 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f82529a --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Files generated by Makefile.PL
+libnet.cfg
+Makefile
+MYMETA.json
+MYMETA.yml
+
+# Files generated by *make
+blib/
+pm_to_blib
+
+# Files generated by *make clean
+Makefile.old
+
+# Files generated by *make dist
+libnet-*.tar.gz
@@ -1,3 +1,36 @@ +libnet 1.28 -- TODO + + * Change Net::SMTP::auth() so that it uses the SMTP AUTH mechanism(s) + specified in the Authen::SASL object if one is provided instead of a + username. If a plain text username is specified then use the first + reported SMTP AUTH method supported, as usual. [Ewen McNeill; resolves CPAN + RT#58002] + * Add support for IPv6 and SSL to Net::SMTP and Net::POP3. These features are + only available if the user has + + a recent IO::Socket::SSL for SSL support + a recent IO::Socket::IP or an older IO::Socket::INET6 for IPv6 support + + If no SSL module is available it will work as before, but attempts to use + the SSL functionality will result in an error message. If no IPv6 modules + are available it will just use IPv4 as before. With IPv6 modules installed + one can of course still access IPv4 hosts. + [Steffen Ullrich; resolves CPAN RT#93823] + +libnet 1.27 -- Fri May 30 2014 + + * Simplified Makefile.PL requirements. + +libnet 1.26 -- Fri May 30 2014 + + * Set minimum required ExtUtils::MakeMaker version to 6.64 to ensure that all + parameters used are supported, to save jumping through hoops to support + earlier versions. (This should not be a problem since ExtUtils::MakeMaker + 6.64 is easily installed into Perl 5.8.1 and above, that being the whole + point of the new choice of minimum supported Perl version.) + * Set minimum required Perl version to 5.8.1. This is in line with the + minimum requirement of the "Perl Toolchain". + libnet 1.25 -- Tue Feb 04 2014 * Fix Net::FTP::pasv_wait() not handling errors from Net::Cmd::reponse() @@ -13,37 +13,37 @@ use strict; # Below this line is auto-generated, *ANY* changes will be lost %NetConfig = ( - # the followinf parameters are all lists of hosts for the - # respective protocols. - nntp_hosts => [], - snpp_hosts => [], - pop3_hosts => [], - smtp_hosts => [], - ph_hosts => [], - daytime_hosts => [], - time_hosts => [], - - # your internet domain - inet_domain => undef, - - # If you have an ftp proxy firewall (not an http firewall) - # then set this to the name of the firewall - ftp_firewall => undef, - - # set if all connections done via the firewall should use - # passive data connections - ftp_ext_passive => 0, - - # set if all connections not done via the firewall should use - # passive data connections - ftp_int_passive => 0, - - # If set the make test will attempt to connect to the hosts above - test_hosts => 0, - - # Used during Configure (which you are not using) to do - # DNS lookups to ensure hosts exist - test_exist => 0, + # the followinf parameters are all lists of hosts for the + # respective protocols. + nntp_hosts => [], + snpp_hosts => [], + pop3_hosts => [], + smtp_hosts => [], + ph_hosts => [], + daytime_hosts => [], + time_hosts => [], + + # your internet domain + inet_domain => undef, + + # If you have an ftp proxy firewall (not an http firewall) + # then set this to the name of the firewall + ftp_firewall => undef, + + # set if all connections done via the firewall should use + # passive data connections + ftp_ext_passive => 0, + + # set if all connections not done via the firewall should use + # passive data connections + ftp_int_passive => 0, + + # If set the make test will attempt to connect to the hosts above + test_hosts => 0, + + # Used during Configure (which you are not using) to do + # DNS lookups to ensure hosts exist + test_exist => 0, ); 1; @@ -114,10 +114,10 @@ sub get_hostname my $ans = Prompt($prompt,$def); $host = ($ans =~ /(\S*)/)[0]; last - if(!length($host) || valid_host($host)); + if(!length($host) || valid_host($host)); $def ="" - if $def eq $host; + if $def eq $host; print <<"EDQ"; @@ -129,8 +129,8 @@ EDQ } length $host - ? $host - : undef; + ? $host + : undef; } ## @@ -221,7 +221,7 @@ sub default_hostname if(defined($host) && valid_host($host)) { return $host - unless wantarray; + unless wantarray; push(@host,$host); } } @@ -236,7 +236,7 @@ sub default_hostname getopts('do:'); $libnet_cfg = "libnet.cfg" - unless(defined($libnet_cfg = $opt_o)); + unless(defined($libnet_cfg = $opt_o)); my %oldcfg = (); @@ -268,7 +268,7 @@ Do you want to modify/update your configuration (y|n) ? EDQ $opt_d = 1 - unless get_bool($msg,0); + unless get_bool($msg,0); } #--------------------------------------------------------------------------- @@ -313,7 +313,7 @@ EDQ $msg = 'Enter a list of available NNTP hosts :'; $def = $oldcfg{'nntp_hosts'} || - [ default_hostname($ENV{NNTPSERVER},$ENV{NEWSHOST},'news') ]; + [ default_hostname($ENV{NNTPSERVER},$ENV{NEWSHOST},'news') ]; $cfg{'nntp_hosts'} = get_host_list($msg,$def); @@ -322,7 +322,7 @@ $cfg{'nntp_hosts'} = get_host_list($msg,$def); $msg = 'Enter a list of available SMTP hosts :'; $def = $oldcfg{'smtp_hosts'} || - [ default_hostname(split(/:/,$ENV{SMTPHOSTS} || ""), 'mailhost') ]; + [ default_hostname(split(/:/,$ENV{SMTPHOSTS} || ""), 'mailhost') ]; $cfg{'smtp_hosts'} = get_host_list($msg,$def); @@ -347,7 +347,7 @@ $cfg{'snpp_hosts'} = get_host_list($msg,$def); $msg = 'Enter a list of available PH Hosts :' ; $def = $oldcfg{'ph_hosts'} || - [ default_hostname('dirserv') ]; + [ default_hostname('dirserv') ]; $cfg{'ph_hosts'} = get_host_list($msg,$def); @@ -456,7 +456,7 @@ if(ref($oldcfg{'local_netmask'})) { $def = $oldcfg{'local_netmask'}; print "Your current netmasks are :\n\n\t", - join("\n\t",@{$def}),"\n\n"; + join("\n\t",@{$def}),"\n\n"; } print " @@ -481,9 +481,9 @@ current list and an empty line to continue with Configure. ###EDQ ### ###$def = $cfg{'socks_hosts'} || -### [ default_hostname($ENV{SOCKS5_SERVER}, -### $ENV{SOCKS_SERVER}, -### $ENV{SOCKS4_SERVER}) ]; +### [ default_hostname($ENV{SOCKS5_SERVER}, +### $ENV{SOCKS_SERVER}, +### $ENV{SOCKS4_SERVER}) ]; ### ###$cfg{'socks_hosts'} = get_host_list($msg,$def); @@ -553,7 +553,7 @@ What host can I use : EDQ $cfg{'ftp_testhost'} = get_hostname($msg,$oldcfg{'ftp_testhost'}) - if $cfg{'test_hosts'}; + if $cfg{'test_hosts'}; print "\n"; @@ -561,7 +561,7 @@ print "\n"; #--------------------------------------------------------------------------- my $fh = IO::File->new($libnet_cfg, "w") or - die "Cannot create `$libnet_cfg': $!"; + die "Cannot create `$libnet_cfg': $!"; print "Writing $libnet_cfg\n"; @@ -571,22 +571,22 @@ my $key; foreach $key (keys %cfg) { my $val = $cfg{$key}; if(!defined($val)) { - $val = "undef"; + $val = "undef"; } elsif(ref($val)) { - $val = '[' . join(",", - map { - my $v = "undef"; - if(defined $_) { - ($v = $_) =~ s/'/\'/sog; - $v = "'" . $v . "'"; - } - $v; - } @$val ) . ']'; + $val = '[' . join(",", + map { + my $v = "undef"; + if(defined $_) { + ($v = $_) =~ s/'/\'/sog; + $v = "'" . $v . "'"; + } + $v; + } @$val ) . ']'; } else { - $val =~ s/'/\'/sog; - $val = "'" . $val . "'" if $val =~ /\D/; + $val =~ s/'/\'/sog; + $val = "'" . $val . "'" if $val =~ /\D/; } print $fh "\t'",$key,"' => ",$val,",\n"; } @@ -1,23 +1,23 @@ Changes Config.eg Configure -Hostname.pm.eg Example replacement for Hostname.pm +Hostname.pm.eg Example replacement for Hostname.pm MANIFEST Makefile.PL Net/Cmd.pm Net/Config.pm -Net/Domain.pm DNS Domain name lookup -Net/FTP.pm File Transfer Protocol Client +Net/Domain.pm DNS Domain name lookup +Net/FTP.pm File Transfer Protocol Client Net/FTP/A.pm Net/FTP/E.pm Net/FTP/I.pm Net/FTP/L.pm Net/FTP/dataconn.pm -Net/NNTP.pm Network News Transfer Protocol -Net/Netrc.pm .netrc lookup routines -Net/POP3.pm Post Office Protocol -Net/SMTP.pm Simple Mail Transfer Protocol Client -Net/Time.pm time & nettime protocols +Net/NNTP.pm Network News Transfer Protocol +Net/Netrc.pm .netrc lookup routines +Net/POP3.pm Post Office Protocol +Net/SMTP.pm Simple Mail Transfer Protocol Client +Net/Time.pm time & nettime protocols Net/libnetFAQ.pod README demos/ftp @@ -37,4 +37,10 @@ t/netrc.t t/nntp.t t/require.t t/smtp.t +t/smtp_ssl.t +t/smtp_ipv6.t +t/pop3_ssl.t +t/pop3_ipv6.t t/time.t +t/external/smtp-ssl.t +t/external/pop3-ssl.t diff --git a/Makefile.PL b/Makefile.PL index 95feb8a..66c3df1 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -1,121 +1,163 @@ -# This -*- perl -*- script makes the Makefile - -#--- Distribution section --- - -$NAME = 'Net'; -$DISTNAME = "libnet"; -$VERSION = "1.25"; - -BEGIN { require 5.002 } - -use ExtUtils::MakeMaker; -use ExtUtils::Manifest qw(maniread); - -#--- Installation check - -sub chk_version -{ - my($pkg,$wanted,$msg) = @_; - - local($|) = 1; - print "Checking for $pkg..."; - - eval { my $p; ($p = $pkg . ".pm") =~ s#::#/#g; require $p; }; - - my $vstr = ${"${pkg}::VERSION"} ? "found v" . ${"${pkg}::VERSION"} - : "not found"; - my $vnum = ${"${pkg}::VERSION"} || 0; - - print $vnum >= $wanted ? "ok\n" : " " . $vstr . "\n"; - - $vnum >= $wanted; +#!perl +#=============================================================================== +# +# Makefile.PL +# +# DESCRIPTION +# Makefile creation script. +# +# COPYRIGHT +# Copyright (C) 2014 Steve Hay. All rights reserved. +# +# LICENCE +# You may distribute under the terms of either the GNU General Public License +# or the Artistic License, as specified in the LICENCE file. +# +#=============================================================================== + +use 5.008001; + +use strict; +use warnings; + +use ExtUtils::MakeMaker 6.64; +use ExtUtils::MakeMaker qw(WriteMakefile); + +#=============================================================================== +# INITIALIZATION +#=============================================================================== + +our($CfgFile, $CfgPath); + +BEGIN { + $CfgFile = 'libnet.cfg'; + $CfgPath = "Net/$CfgFile"; } -sub MY::post_initialize -{ - my ($self) = @_; +#=============================================================================== +# MAIN PROGRAM +#=============================================================================== + +MAIN: { + my %prereq_pms = (); + $prereq_pms{'Convert::EBCDIC'} = '0.06' if $^O eq 'os390'; + + my $xt = prompt("Should I do external tests?\n" . + "These tests will fail if there is no internet connection or if a firewall\n" . + "blocks or modifies some traffic.\n" . + "[y/N]", 'n'); + + my $tests = 't/*.t'; + $tests .= ' t/external/*.t' if $xt =~ m/^y/io; + + WriteMakefile( + NAME => 'Net', + DISTNAME => 'libnet', + ABSTRACT => 'Collection of network protocol modules', + AUTHOR => 'Graham Barr <gbarr@pobox.com>, Steve Hay <shay@cpan.org>', + LICENSE => 'perl_5', + VERSION => '1.28', + + META_MERGE => { + 'meta-spec' => { + version => 2 + }, + + resources => { + repository => { + type => 'git', + url => 'https://github.com/steve-m-hay/perl-libnet.git' + } + }, + + optional_features => { + APOP => { + description => 'APOP support', + prereqs => { + runtime => { + requires => { + 'Digest::MD5' => '0' + } + } + } + }, + + AUTH => { + description => 'AUTH support', + prereqs => { + runtime => { + requires => { + 'Authen::SASL' => '0', + 'MIME::Base64' => '0' + } + } + } + } + } + }, - #--- Create Net::Config + MIN_PERL_VERSION => '5.008001', - my $config_pm = "Net/libnet.cfg"; - my $libnet_cfg = "libnet.cfg"; + CONFIGURE_REQUIRES => { + 'ExtUtils::MakeMaker' => '6.64', + 'Getopt::Std' => '0', + 'IO:File' => '0', + 'perl' => '5.008001', + 'strict' => '0', + 'vars' => '0', + 'warnings' => '0' + }, - # Use %INC and ExtUtils::MakeMaker to determine how this machine - # maps package names to path names + TEST_REQUIRES => { + 'Cwd' => '0' + }, - foreach (keys %INC) { - last if ($config_pm = $_) =~ s/^ExtUtils(.)MakeMaker.pm/Net${1}libnet.cfg/; - } + PREREQ_PM => { + %prereq_pms, + 'Carp' => '0', + 'Errno' => '0', + 'Exporter' => '0', + 'Fcntl' => '0', + 'File::Basename' => '0', + 'FileHandle' => '0', + 'IO::Select' => '0', + 'IO::Socket' => '1.05', + 'POSIX' => '0', + 'Socket' => '1.3', + 'Symbol' => '0', + 'Time::Local' => '0', + 'strict' => '0', + 'vars' => '0' + }, - system(($^O eq 'VMS' ? 'mcr ': ()),$^X, 'Configure') - unless -f $libnet_cfg; + INSTALLDIRS => 'perl', - $self->{PM}->{$libnet_cfg} = $self->catfile('$(INST_LIBDIR)',$config_pm); + realclean => { + FILES => $CfgFile + }, - ""; -} + test => { + TESTS => $tests + }, -#--- Check for Socket - -chk_version(Socket => '1.30') or - warn "\n" - . "*** For Net::Cmd to work you require version 1.30, or later, of\n" - . " Socket.pm from CPAN/modules/by-module/Socket/Socket-x.x.tar.gz\n\n"; - -chk_version(IO::Socket => '1.05') or - warn "\n" - . "*** For Net::Cmd to work you require version 1.05, or later, of\n" - . " IO/Socket.pm from CPAN/modules/by-module/IO/IO-x.x.tar.gz\n\n"; - -if ($^O eq 'os390') -{ - chk_version(Convert::EBCDIC => '0.06') or - warn "\n" - . "*** For Net::Cmd to work on $^O version 0.06, or later, of\n" - . " Convert::EBCDIC is required, which can be found at" - . " CPAN/modules/by-module/Convert/Convert-EBCDIC-x.x.tar.gz\n\n"; + dist => { + PREOP => 'find $(DISTVNAME) -type d -print|xargs chmod 0755 && ' . + 'find $(DISTVNAME) -type f -print|xargs chmod 0644', + TO_UNIX => 'find $(DISTVNAME) -type f -print|xargs dos2unix' + } + ); } -#--- Write the Makefile - -my @ppd; +#=============================================================================== +# MAKEMAKER OVERRIDES +#=============================================================================== -if ($] >= 5.00503) { - @ppd = ( - AUTHOR => 'Graham Barr <gbarr@pobox.com>', - ABSTRACT => 'Collection of Network protocol modules', - ); +sub MY::post_initialize { + my $self = shift; + return '' if $self->{PERL_CORE}; + system(($^O eq 'VMS' ? 'mcr ': ()), $^X, 'Configure') unless -f $CfgFile; + $self->{PM}{$CfgFile} = $self->catfile('$(INST_LIBDIR)',$CfgPath); + return ''; } -WriteMakefile( - INSTALLDIRS => ($] >= 5.008 ? 'perl' : 'site'), - VERSION => $VERSION, - DISTNAME => $DISTNAME, - NAME => $NAME, - 'realclean' => {FILES => $config_pm}, - PREREQ_PM => { - Socket => 1.3, - IO::Socket => 1.05 - }, - dist => { DIST_DEFAULT => 'mydist', }, - (eval { ExtUtils::MakeMaker->VERSION(6.21) } ? (LICENSE => 'perl') : ()), - ( eval { ExtUtils::MakeMaker->VERSION(6.46) } ? ( - META_MERGE => { - resources => { ## - repository => 'http://github.com/steve-m-hay/perl-libnet', - }, - } - ) - : () - ), - @ppd, -); - -sub MY::postamble { - return <<'POSTAMBLE'; - -mydist : distmeta tardist - -POSTAMBLE - -} +#=============================================================================== @@ -37,7 +37,7 @@ BEGIN { } } -$VERSION = "2.30"; +$VERSION = "2.31"; @ISA = qw(Exporter); @EXPORT = qw(CMD_INFO CMD_OK CMD_MORE CMD_REJECT CMD_ERROR CMD_PENDING); @@ -53,7 +53,6 @@ my %debug = (); my $tr = $^O eq 'os390' ? Convert::EBCDIC->new() : undef; - sub toebcdic { my $cmd = shift; diff --git a/Net/Config.pm b/Net/Config.pm index ba16332..f9d04e1 100644 --- a/Net/Config.pm +++ b/Net/Config.pm @@ -13,7 +13,7 @@ use strict; @EXPORT = qw(%NetConfig); @ISA = qw(Net::LocalCfg Exporter); -$VERSION = "1.13"; +$VERSION = "1.14"; eval { local $SIG{__DIE__}; require Net::LocalCfg }; @@ -49,9 +49,9 @@ my %nc = ( ftp_ext_passive => \$InternetConfig{"646F676F\xA5UsePassiveMode"} || 0, ftp_int_passive => \$InternetConfig{"646F676F\xA5UsePassiveMode"} || 0, socks_hosts => - \$InternetConfig{ kICUseSocks() } ? [ \$InternetConfig{ kICSocksHost() } ] : [], + \$InternetConfig{ kICUseSocks() } ? [ \$InternetConfig{ kICSocksHost() } ] : [], ftp_firewall => - \$InternetConfig{ kICUseFTPProxy() } ? [ \$InternetConfig{ kICFTPProxyHost() } ] : [], + \$InternetConfig{ kICUseFTPProxy() } ? [ \$InternetConfig{ kICFTPProxyHost() } ] : [], ); \@NetConfig{keys %nc} = values %nc; } @@ -141,7 +141,7 @@ For example # .libnetrc { nntp_hosts => [ "my_preferred_host" ], - ph_hosts => [ "my_ph_server" ], + ph_hosts => [ "my_ph_server" ], } __END__ diff --git a/Net/POP3.pm b/Net/POP3.pm index 4b94a11..f51f0cf 100644 --- a/Net/POP3.pm +++ b/Net/POP3.pm @@ -13,9 +13,34 @@ use Net::Cmd; use Carp; use Net::Config; -$VERSION = "2.31"; +$VERSION = "2.32"; @ISA = qw(Net::Cmd IO::Socket::INET); +# Code for detecting if we can use SSL +my $ssl_class = eval { + require IO::Socket::SSL; + # first version with default CA on most platforms + IO::Socket::SSL->VERSION(1.968); +} && 'IO::Socket::SSL'; +my $nossl_warn = !$ssl_class && + 'To use SSL please install IO::Socket::SSL with version>=1.968'; + +# Code for detecting if we can use IPv6 +my $inet6_class = + eval { + require IO::Socket::IP; + IO::Socket::IP->VERSION(0.20); + } && 'IO::Socket::IP' || + eval { + require IO::Socket::INET6; + IO::Socket::INET6->VERSION(2.62); + } && 'IO::Socket::INET6'; + +sub can_ssl { $ssl_class }; +sub can_inet6 { $inet6_class }; + + +@ISA = ( 'Net::Cmd', $inet6_class || 'IO::Socket::INET' ); sub new { @@ -34,6 +59,14 @@ sub new { my $obj; my @localport = exists $arg{ResvPort} ? (LocalPort => $arg{ResvPort}) : (); + if ($arg{SSL}) { + # SSL from start + die $nossl_warn if !$ssl_class; + $arg{Port} ||= 995; + } + + $arg{Timeout} = 120 if ! defined $arg{Timeout}; + my $h; foreach $h (@{$hosts}) { $obj = $type->SUPER::new( @@ -41,9 +74,7 @@ sub new { PeerPort => $arg{Port} || 'pop3(110)', Proto => 'tcp', @localport, - Timeout => defined $arg{Timeout} - ? $arg{Timeout} - : 120 + Timeout => $arg{Timeout}, ) and last; } @@ -51,6 +82,13 @@ sub new { return undef unless defined $obj; + ${*$obj}{'net_pop3_arg'} = \%arg; + if ($arg{SSL}) { + Net::POP3::_SSLified->start_SSL($obj, + SSL_verifycn_name => $host,%arg + ) or return; + } + ${*$obj}{'net_pop3_host'} = $host; $obj->autoflush(1); @@ -93,6 +131,16 @@ sub login { and $me->pass($pass); } +sub starttls { + my $self = shift; + $ssl_class or die $nossl_warn; + $self->_STLS or return; + Net::POP3::_SSLified->start_SSL($self, + %{ ${*$self}{'net_pop3_arg'} }, # (ssl) args given in new + @_ # more (ssl) args + ) or return; + return 1; +} sub apop { @_ >= 1 && @_ <= 3 or croak 'usage: $pop3->apop( USER, PASS )'; @@ -323,6 +371,7 @@ sub _PING { shift->command('PING', $_[0])->response() == CMD_OK } sub _RPOP { shift->command('RPOP', $_[0])->response() == CMD_OK } sub _LAST { shift->command('LAST' )->response() == CMD_OK } sub _CAPA { shift->command('CAPA' )->response() == CMD_OK } +sub _STLS { shift->command("STLS", )->response() == CMD_OK } sub quit { @@ -520,6 +569,24 @@ sub banner { return ${*$this}{'net_pop3_banner'}; } +{ + package Net::POP3::_SSLified; + our @ISA = ( $ssl_class ? ($ssl_class):(), 'Net::POP3' ); + sub starttls { die "POP3 connection is already in SSL mode" } + sub start_SSL { + my ($class,$pop3,%arg) = @_; + delete @arg{ grep { !m{^SSL_} } keys %arg }; + ( $arg{SSL_verifycn_name} ||= $pop3->host ) + =~s{(?<!:):[\w()]+$}{}; # strip port + $arg{SSL_verifycn_scheme} ||= 'pop3'; + my $ok = $class->SUPER::start_SSL($pop3,%arg); + $@ = $ssl_class->errstr if !$ok; + return $ok; + } +} + + + 1; __END__ @@ -535,6 +602,7 @@ Net::POP3 - Post Office Protocol 3 Client class (RFC1939) # Constructors $pop = Net::POP3->new('pop3host'); $pop = Net::POP3->new('pop3host', Timeout => 60); + $pop = Net::POP3->new('pop3host', SSL => 1, Timeout => 60); if ($pop->login($username, $password) > 0) { my $msgnums = $pop->list; # hashref of msgnum => size @@ -580,6 +648,14 @@ the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to an array with hosts to try in turn. The L</host> method will return the value which was used to connect to the host. +B<Port> - port to connect to. +Default - 110 for plain POP3 and 995 for POP3s (direct SSL). + +B<SSL> - If the connection should be done from start with SSL, contrary to later +upgrade with C<starttls>. +You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will +usually use the right arguments already. + B<ResvPort> - If given then the socket for the C<Net::POP3> object will be bound to the local port given using C<bind> when the socket is created. @@ -629,6 +705,12 @@ will give a true value in a boolean context, but zero in a numeric context. If there was an error authenticating the user then I<undef> will be returned. +=item starttls ( SSLARGS ) + +Upgrade existing plain connection to SSL. +You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will +usually use the right arguments already. + =item apop ( [ USER [, PASS ]] ) Authenticate with the server identifying as C<USER> with password C<PASS>. @@ -729,7 +811,8 @@ means that any messages marked to be deleted will not be. =head1 SEE ALSO L<Net::Netrc>, -L<Net::Cmd> +L<Net::Cmd>, +L<IO::Socket::SSL> =head1 AUTHOR diff --git a/Net/SMTP.pm b/Net/SMTP.pm index cf5e3c1..51e66d3 100644 --- a/Net/SMTP.pm +++ b/Net/SMTP.pm @@ -16,9 +16,33 @@ use IO::Socket; use Net::Cmd; use Net::Config; -$VERSION = "2.33"; +$VERSION = "2.35"; + +# Code for detecting if we can use SSL +my $ssl_class = eval { + require IO::Socket::SSL; + # first version with default CA on most platforms + IO::Socket::SSL->VERSION(1.968); +} && 'IO::Socket::SSL'; +my $nossl_warn = !$ssl_class && + 'To use SSL please install IO::Socket::SSL with version>=1.968'; + +# Code for detecting if we can use IPv6 +my $inet6_class = + eval { + require IO::Socket::IP; + IO::Socket::IP->VERSION(0.20); + } && 'IO::Socket::IP' || + eval { + require IO::Socket::INET6; + IO::Socket::INET6->VERSION(2.62); + } && 'IO::Socket::INET6'; + +sub can_ssl { $ssl_class }; +sub can_inet6 { $inet6_class }; + -@ISA = qw(Net::Cmd IO::Socket::INET); +@ISA = ( 'Net::Cmd', $inet6_class || 'IO::Socket::INET' ); sub new { @@ -33,9 +57,18 @@ sub new { %arg = @_; $host = delete $arg{Host}; } + + 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}; + my $h; foreach $h (@{ref($hosts) ? $hosts : [$hosts]}) { $obj = $type->SUPER::new( @@ -44,9 +77,7 @@ sub new { LocalAddr => $arg{LocalAddr}, LocalPort => $arg{LocalPort}, Proto => 'tcp', - Timeout => defined $arg{Timeout} - ? $arg{Timeout} - : 120 + Timeout => $arg{Timeout} ) and last; } @@ -54,6 +85,12 @@ sub new { return undef unless defined $obj; + ${*$obj}{'net_smtp_arg'} = \%arg; + if ($arg{SSL}) { + Net::SMTP::_SSLified->start_SSL($obj,SSL_verifycn_name => $host,%arg) + or return; + } + $obj->autoflush(1); $obj->debug(exists $arg{Debug} ? $arg{Debug} : undef); @@ -128,7 +165,10 @@ sub auth { if (ref($username) and UNIVERSAL::isa($username, 'Authen::SASL')) { $sasl = $username; - $sasl->mechanism($mechanisms); + my $requested_mechanisms = $sasl->mechanism(); + if (! defined($requested_mechanisms) || $requested_mechanisms eq '') { + $sasl->mechanism($mechanisms); + } } else { die "auth(username, password)" if not length $username; @@ -204,11 +244,25 @@ sub hello { } return undef unless $ok; + ${*$me}{net_smtp_hello_domain} = $domain; $msg[0] =~ /\A\s*(\S+)/; return ($1 || " "); } +sub starttls { + my $self = shift; + $ssl_class or die $nossl_warn; + $self->_STARTTLS or return; + Net::SMTP::_SSLified->start_SSL($self, + %{ ${*$self}{'net_smtp_arg'} }, # (ssl) args given in new + @_ # more (ssl) args + ) or return; + + # another hello after starttls to read new ESMTP capabilities + return $self->hello(${*$self}{net_smtp_hello_domain}); +} + sub supports { my $self = shift; @@ -546,6 +600,26 @@ sub _BDAT { shift->command("BDAT", @_) } sub _TURN { shift->unsupported(@_); } sub _ETRN { shift->command("ETRN", @_)->response() == CMD_OK } sub _AUTH { shift->command("AUTH", @_)->response() == CMD_OK } +sub _STARTTLS { shift->command("STARTTLS")->response() == CMD_OK } + + +{ + package Net::SMTP::_SSLified; + our @ISA = ( $ssl_class ? ($ssl_class):(), 'Net::SMTP' ); + sub starttls { die "SMTP connection is already in SSL mode" } + sub start_SSL { + my ($class,$smtp,%arg) = @_; + delete @arg{ grep { !m{^SSL_} } keys %arg }; + ( $arg{SSL_verifycn_name} ||= $smtp->host ) + =~s{(?<!:):[\w()]+$}{}; # strip port + $arg{SSL_verifycn_scheme} ||= 'smtp'; + my $ok = $class->SUPER::start_SSL($smtp,%arg); + $@ = $ssl_class->errstr if !$ok; + return $ok; + } +} + + 1; @@ -636,9 +710,15 @@ B<Host> - SMTP host to connect to. It may be a single scalar (hostname[:port]), as defined for the C<PeerAddr> option in L<IO::Socket::INET>, or a reference to an array with hosts to try in turn. The L</host> method will return the value which was used to connect to the host. +Format - C<PeerHost> from L<IO::Socket::INET> new method. -B<Port> - port to connect to. Format - C<PeerHost> from L<IO::Socket::INET> new method. -Default - 25. +B<Port> - port to connect to. +Default - 25 for plain SMTP and 465 for immediate SSL. + +B<SSL> - If the connection should be done from start with SSL, contrary to later +upgrade with C<starttls>. +You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will +usually use the right arguments already. B<LocalAddr> and B<LocalPort> - These parameters are passed directly to IO::Socket to allow binding the socket to a local port. @@ -657,24 +737,32 @@ Example: $smtp = Net::SMTP->new('mailhost', - Hello => 'my.mail.domain', - Timeout => 30, + Hello => 'my.mail.domain', + Timeout => 30, Debug => 1, - ); + ); # the same $smtp = Net::SMTP->new( - Host => 'mailhost', + Host => 'mailhost', + Hello => 'my.mail.domain', + Timeout => 30, + Debug => 1, + ); + + # the same with direct SSL + $smtp = Net::SMTP->new('mailhost', Hello => 'my.mail.domain', Timeout => 30, - Debug => 1, + Debug => 1, + SSL => 1, ); # Connect to the default server from Net::config $smtp = Net::SMTP->new( - Hello => 'my.mail.domain', - Timeout => 30, - ); + Hello => 'my.mail.domain', + Timeout => 30, + ); =back @@ -717,6 +805,12 @@ to connect to the host. Request a queue run for the DOMAIN given. +=item starttls ( SSLARGS ) + +Upgrade existing plain connection to SSL. +You can use SSL arguments as documented in L<IO::Socket::SSL>, but it will +usually use the right arguments already. + =item auth ( USERNAME, PASSWORD ) Attempt SASL authentication. Requires Authen::SASL module. @@ -883,7 +977,8 @@ accept the address surrounded by angle brackets. =head1 SEE ALSO -L<Net::Cmd> +L<Net::Cmd>, +L<IO::Socket::SSL> =head1 AUTHOR diff --git a/Net/Time.pm b/Net/Time.pm index 6f1dd04..6b3b641 100644 --- a/Net/Time.pm +++ b/Net/Time.pm @@ -17,7 +17,7 @@ use IO::Select; @ISA = qw(Exporter); @EXPORT_OK = qw(inet_time inet_daytime); -$VERSION = "2.10"; +$VERSION = "2.11"; $TIMEOUT = 120; @@ -107,11 +107,11 @@ Net::Time - time and daytime network client interface use Net::Time qw(inet_time inet_daytime); - print inet_time(); # use default host from Net::Config + print inet_time(); # use default host from Net::Config print inet_time('localhost'); print inet_time('localhost', 'tcp'); - print inet_daytime(); # use default host from Net::Config + print inet_daytime(); # use default host from Net::Config print inet_daytime('localhost'); print inet_daytime('localhost', 'tcp'); @@ -10,12 +10,12 @@ point look at: The RFC implemented in this distribution are -Net::FTP RFC959 File Transfer Protocol -Net::SMTP RFC821 Simple Mail Transfer Protocol -Net::Time RFC867 Daytime Protocol -Net::Time RFC868 Time Protocol -Net::NNTP RFC977 Network News Transfer Protocol -Net::POP3 RFC1939 Post Office Protocol 3 +Net::FTP RFC959 File Transfer Protocol +Net::SMTP RFC821 Simple Mail Transfer Protocol +Net::Time RFC867 Daytime Protocol +Net::Time RFC868 Time Protocol +Net::NNTP RFC977 Network News Transfer Protocol +Net::POP3 RFC1939 Post Office Protocol 3 AVAILABILITY @@ -2,11 +2,11 @@ BEGIN { if ($ENV{PERL_CORE}) { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } undef *{Socket::inet_aton}; undef *{Socket::inet_ntoa}; @@ -19,29 +19,29 @@ BEGIN { package Socket; sub import { - my $pkg = caller(); - no strict 'refs'; - *{ $pkg . '::inet_aton' } = \&inet_aton; - *{ $pkg . '::inet_ntoa' } = \&inet_ntoa; + my $pkg = caller(); + no strict 'refs'; + *{ $pkg . '::inet_aton' } = \&inet_aton; + *{ $pkg . '::inet_ntoa' } = \&inet_ntoa; } my $fail = 0; my %names; sub set_fail { - $fail = shift; + $fail = shift; } sub inet_aton { - return if $fail; - my $num = unpack('N', pack('C*', split(/\./, $_[0]))); - $names{$num} = $_[0]; - return $num; + return if $fail; + my $num = unpack('N', pack('C*', split(/\./, $_[0]))); + $names{$num} = $_[0]; + return $num; } sub inet_ntoa { - return if $fail; - return $names{$_[0]}; + return if $fail; + return $names{$_[0]}; } package main; @@ -59,29 +59,29 @@ ok( keys %NetConfig, '%NetConfig should be imported' ); Socket::set_fail(1); undef $NetConfig{'ftp_firewall'}; is( Net::Config->requires_firewall(), 0, - 'requires_firewall() should return 0 without ftp_firewall defined' ); + 'requires_firewall() should return 0 without ftp_firewall defined' ); $NetConfig{'ftp_firewall'} = 1; is( Net::Config->requires_firewall('a.host.not.there'), -1, - '... should return -1 without a valid hostname' ); + '... should return -1 without a valid hostname' ); Socket::set_fail(0); delete $NetConfig{'local_netmask'}; is( Net::Config->requires_firewall('127.0.0.1'), 0, - '... should return 0 without local_netmask defined' ); + '... should return 0 without local_netmask defined' ); $NetConfig{'local_netmask'} = '127.0.0.1/24'; is( Net::Config->requires_firewall('127.0.0.1'), 0, - '... should return false if host is within netmask' ); + '... should return false if host is within netmask' ); is( Net::Config->requires_firewall('192.168.10.0'), 1, - '... should return true if host is outside netmask' ); + '... should return true if host is outside netmask' ); # now try more netmasks $NetConfig{'local_netmask'} = [ '127.0.0.1/24', '10.0.0.0/8' ]; is( Net::Config->requires_firewall('10.10.255.254'), 0, - '... should find success with mutiple local netmasks' ); + '... should find success with mutiple local netmasks' ); is( Net::Config->requires_firewall('192.168.10.0'), 1, - '... should handle failure with multiple local netmasks' ); + '... should handle failure with multiple local netmasks' ); is( \&Net::Config::is_external, \&Net::Config::requires_firewall, - 'is_external() should be an alias for requires_firewall()' ); + 'is_external() should be an alias for requires_firewall()' ); diff --git a/t/datasend.t b/t/datasend.t index 96b5b7c..f642340 100644 --- a/t/datasend.t +++ b/t/datasend.t @@ -2,11 +2,11 @@ BEGIN { if ($ENV{PERL_CORE}) { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; diff --git a/t/external/pop3-ssl.t b/t/external/pop3-ssl.t new file mode 100644 index 0000000..6eec93c --- /dev/null +++ b/t/external/pop3-ssl.t @@ -0,0 +1,54 @@ + +use strict; +use warnings; +use Net::POP3; +use Test::More; + +my $host = 'pop.gmx.net'; +my $debug = 0; + +plan skip_all => "no SSL support" if ! Net::POP3->can_ssl; +{ +no warnings 'once'; +plan skip_all => "no verified SSL connection to $host:995 - $@" if ! eval { + IO::Socket::SSL->new(PeerAddr => "$host:995", Timeout => 10) + || die($IO::Socket::SSL::SSL_ERROR||$!); +}; +} + +plan tests => 2; + +SKIP: { + diag( "connect inet to $host:110" ); + skip "no inet connect to $host:110",1 + if ! IO::Socket::INET->new(PeerAddr => "$host:110", Timeout => 10); + my $pop3 = Net::POP3->new($host, Debug => $debug, Timeout => 10) + or skip "normal POP3 failed: $@",1; + skip "no STARTTLS support",1 if $pop3->message !~/STARTTLS/; + + if (!$pop3->starttls) { + fail("starttls failed: ".$pop3->code." $@") + } else { + # we now should have access to SSL stuff + my $cipher = eval { $pop3->get_cipher }; + if (!$cipher) { + fail("after starttls: not an SSL object"); + } elsif ( $pop3->quit ) { + pass("starttls + quit ok, cipher=$cipher"); + } else { + fail("quit after starttls failed: ".$pop3->code); + } + } +} + + +my $pop3 = Net::POP3->new($host, SSL => 1, Timeout => 10, Debug => $debug); +# we now should have access to SSL stuff +my $cipher = eval { $pop3->get_cipher }; +if (!$cipher) { + fail("after ssl connect: not an SSL object"); +} elsif ( $pop3->quit ) { + pass("ssl connect ok, cipher=$cipher"); +} else { + fail("quit after direct ssl failed: ".$pop3->code); +} diff --git a/t/external/smtp-ssl.t b/t/external/smtp-ssl.t new file mode 100644 index 0000000..1802976 --- /dev/null +++ b/t/external/smtp-ssl.t @@ -0,0 +1,53 @@ + +use strict; +use warnings; +use Net::SMTP; +use Test::More; + +my $host = 'mail.gmx.net'; +my $debug = 0; + +plan skip_all => "no SSL support" if ! Net::SMTP->can_ssl; +{ +no warnings 'once'; +plan skip_all => "no verified SSL connection to $host:465 - $@" if ! eval { + IO::Socket::SSL->new("$host:465") + || die($IO::Socket::SSL::SSL_ERROR||$!); +}; +} + +plan tests => 2; + +SKIP: { + diag( "connect inet to $host:25" ); + skip "no inet connect to $host:25",1 if ! IO::Socket::INET->new("$host:25"); + my $smtp = Net::SMTP->new($host, Debug => $debug) + or skip "normal SMTP failed: $@",1; + skip "no STARTTLS support",1 if $smtp->message !~/STARTTLS/; + + if (!$smtp->starttls) { + fail("starttls failed: ".$smtp->code." $@") + } else { + # we now should have access to SSL stuff + my $cipher = eval { $smtp->get_cipher }; + if (!$cipher) { + fail("after starttls: not an SSL object"); + } elsif ( $smtp->quit ) { + pass("starttls + quit ok, cipher=$cipher"); + } else { + fail("quit after starttls failed: ".$smtp->code); + } + } +} + + +my $smtp = Net::SMTP->new($host, SSL => 1, Debug => $debug); +# we now should have access to SSL stuff +my $cipher = eval { $smtp->get_cipher }; +if (!$cipher) { + fail("after ssl connect: not an SSL object"); +} elsif ( $smtp->quit ) { + pass("ssl connect ok, cipher=$cipher"); +} else { + fail("quit after direct ssl failed: ".$smtp->code); +} @@ -2,11 +2,11 @@ BEGIN { unless (-d 'blib') { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # Skip: no Socket module\n"; exit 0; + print "1..0 # Skip: no Socket module\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # Skip: EBCDIC but no Convert::EBCDIC\n"; exit 0; @@ -30,7 +30,7 @@ my $t = 1; print "1..7\n"; $ftp = Net::FTP->new($NetConfig{ftp_testhost}) - or (print("not ok 1\n"), exit); + or (print("not ok 1\n"), exit); printf "ok %d\n",$t++; diff --git a/t/hostname.t b/t/hostname.t index 4013d74..f486bb4 100644 --- a/t/hostname.t +++ b/t/hostname.t @@ -2,11 +2,11 @@ BEGIN { unless (-d 'blib') { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; diff --git a/t/libnet_t.pl b/t/libnet_t.pl index ed245e6..9337dd1 100644 --- a/t/libnet_t.pl +++ b/t/libnet_t.pl @@ -1,36 +1,36 @@ my $number = 0; sub ok { - my ($condition, $name) = @_; + my ($condition, $name) = @_; - my $message = $condition ? "ok " : "not ok "; - $message .= ++$number; - $message .= " # $name" if defined $name; - print $message, "\n"; - return $condition; + my $message = $condition ? "ok " : "not ok "; + $message .= ++$number; + $message .= " # $name" if defined $name; + print $message, "\n"; + return $condition; } sub is { - my ($got, $expected, $name) = @_; + my ($got, $expected, $name) = @_; - for ($got, $expected) { - $_ = 'undef' unless defined $_; - } + for ($got, $expected) { + $_ = 'undef' unless defined $_; + } - unless (ok($got eq $expected, $name)) { - warn "Got: '$got'\nExpected: '$expected'\n" . join(' ', caller) . "\n"; - } + unless (ok($got eq $expected, $name)) { + warn "Got: '$got'\nExpected: '$expected'\n" . join(' ', caller) . "\n"; + } } sub skip { - my ($reason, $num) = @_; - $reason ||= ''; - $number ||= 1; - - for (1 .. $num) { - $number++; - print "ok $number # skip $reason\n"; - } + my ($reason, $num) = @_; + $reason ||= ''; + $number ||= 1; + + for (1 .. $num) { + $number++; + print "ok $number # skip $reason\n"; + } } 1; @@ -2,11 +2,11 @@ BEGIN { if ($ENV{PERL_CORE}) { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; @@ -25,13 +25,13 @@ $ENV{HOME} = Cwd::cwd(); local (*CORE::GLOBAL::getpwuid, *CORE::GLOBAL::stat); *CORE::GLOBAL::getpwuid = sub ($) { - ((undef) x 7, Cwd::cwd()); + ((undef) x 7, Cwd::cwd()); }; # for testing _readrc my @stat; *CORE::GLOBAL::stat = sub (*) { - return @stat; + return @stat; }; # for testing _readrc @@ -47,29 +47,29 @@ ok( exists $INC{'Net/Netrc.pm'}, 'should be able to use Net::Netrc' ); $Net::Netrc::TESTING=$Net::Netrc::TESTING=1; SKIP: { - skip('incompatible stat() handling for OS', 4), next SKIP - if ($^O =~ /os2|win32|macos|cygwin/i or $] < 5.005); - - my $warn; - local $SIG{__WARN__} = sub { - $warn = shift; - }; - - # add write access for group/other - $stat[2] = 077; - ok( !defined(Net::Netrc::_readrc()), - '_readrc() should not read world-writable file' ); - ok( scalar($warn =~ /^Bad permissions:/), - '... and should warn about it' ); - - # the owner field should still not match - $stat[2] = 0; + skip('incompatible stat() handling for OS', 4), next SKIP + if ($^O =~ /os2|win32|macos|cygwin/i or $] < 5.005); + + my $warn; + local $SIG{__WARN__} = sub { + $warn = shift; + }; + + # add write access for group/other + $stat[2] = 077; + ok( !defined(Net::Netrc::_readrc()), + '_readrc() should not read world-writable file' ); + ok( scalar($warn =~ /^Bad permissions:/), + '... and should warn about it' ); + + # the owner field should still not match + $stat[2] = 0; if ($<) { ok( !defined(Net::Netrc::_readrc()), '_readrc() should not read file owned by someone else' ); ok( scalar($warn =~ /^Not owner:/), - '... and should warn about it' ); + '... and should warn about it' ); } else { skip("testing as root",2); } @@ -80,15 +80,15 @@ $stat[4] = $<; # this curious mix of spaces and quotes tests a regex at line 79 (version 2.11) FileHandle::set_lines(split(/\n/, <<LINES)); -macdef bar -login baz - machine "foo" -login nigol "password" drowssap -machine foo "login" l2 - password p2 -account tnuocca -default login "baz" password p2 -default "login" baz password p3 +macdef bar +login baz +machine "foo" +login nigol "password" drowssap +machine foo "login" l2 +password p2 +account tnuocca +default login "baz" password p2 +default "login" baz password p3 macdef LINES @@ -97,59 +97,59 @@ is( Net::Netrc::_readrc(), 1, '_readrc() should succeed now' ); # on 'foo', the login is 'nigol' is( Net::Netrc->lookup('foo')->{login}, 'nigol', - 'lookup() should find value by host name' ); + 'lookup() should find value by host name' ); # on 'foo' with login 'l2', the password is 'p2' is( Net::Netrc->lookup('foo', 'l2')->{password}, 'p2', - 'lookup() should find value by hostname and login name' ); + 'lookup() should find value by hostname and login name' ); # the default password is 'p3', as later declarations have priority is( Net::Netrc->lookup()->{password}, 'p3', - 'lookup() should find default value' ); + 'lookup() should find default value' ); # lookup() ignores the login parameter when using default data is( Net::Netrc->lookup('default', 'baz')->{password}, 'p3', - 'lookup() should ignore passed login when searching default' ); + 'lookup() should ignore passed login when searching default' ); # lookup() goes to default data if hostname cannot be found in config data is( Net::Netrc->lookup('abadname')->{login}, 'baz', - 'lookup() should use default for unknown machine name' ); + 'lookup() should use default for unknown machine name' ); # now test these accessors my $instance = bless({}, 'Net::Netrc'); for my $accessor (qw( login account password )) { - is( $instance->$accessor(), undef, - "$accessor() should return undef if $accessor is not set" ); - $instance->{$accessor} = $accessor; - is( $instance->$accessor(), $accessor, - "$accessor() should return value when $accessor is set" ); + is( $instance->$accessor(), undef, + "$accessor() should return undef if $accessor is not set" ); + $instance->{$accessor} = $accessor; + is( $instance->$accessor(), $accessor, + "$accessor() should return value when $accessor is set" ); } # and the three-for-one accessor is( scalar( () = $instance->lpa()), 3, - 'lpa() should return login, password, account'); + 'lpa() should return login, password, account'); is( join(' ', $instance->lpa), 'login password account', - 'lpa() should return appropriate values for l, p, and a' ); + 'lpa() should return appropriate values for l, p, and a' ); package FileHandle; sub new { - tie *FH, 'FileHandle', @_; - bless \*FH, $_[0]; + tie *FH, 'FileHandle', @_; + bless \*FH, $_[0]; } sub TIEHANDLE { - my ($class, $file, $mode) = @_[0,2,3]; - bless({ file => $file, mode => $mode }, $class); + my ($class, $file, $mode) = @_[0,2,3]; + bless({ file => $file, mode => $mode }, $class); } my @lines; sub set_lines { - @lines = @_; + @lines = @_; } sub READLINE { - shift @lines; + shift @lines; } sub close { 1 } @@ -2,11 +2,11 @@ BEGIN { unless (-d 'blib') { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; @@ -27,7 +27,7 @@ print "1..4\n"; my $i = 1; $nntp = Net::NNTP->new(Debug => 0) - or (print("not ok 1\n"), exit); + or (print("not ok 1\n"), exit); print "ok 1\n"; diff --git a/t/pop3_ipv6.t b/t/pop3_ipv6.t new file mode 100644 index 0000000..2f073ab --- /dev/null +++ b/t/pop3_ipv6.t @@ -0,0 +1,57 @@ +use strict; +use warnings; +use Test::More; +use File::Temp 'tempfile'; +use Net::POP3; + +my $debug = 0; # Net::POP3->new( Debug => .. ) + +my $inet6class = Net::POP3->can_inet6; +plan skip_all => "no IPv6 support found in Net::POP3" if ! $inet6class; + +plan skip_all => "fork not supported on this platform" + if grep { $^O =~m{$_} } qw(MacOS VOS vmesa riscos amigaos); + +my $srv = $inet6class->new( + LocalAddr => '::1', + Listen => 10 +); +plan skip_all => "cannot create listener on ::1: $!" if ! $srv; +my $saddr = "[".$srv->sockhost."]".':'.$srv->sockport; +diag("server on $saddr"); + +plan tests => 1; + +defined( my $pid = fork()) or die "fork failed: $!"; +exit(pop3_server()) if ! $pid; + +my $cl = Net::POP3->new($saddr, Debug => $debug); +diag("created Net::POP3 object"); +if (!$cl) { + fail("IPv6 POP3 connect failed"); +} else { + $cl->quit; + pass("IPv6 success"); +} +wait; + +sub pop3_server { + my $cl = $srv->accept or die "accept failed: $!"; + print $cl "+OK localhost ready\r\n"; + while (<$cl>) { + my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_; + $cmd = uc($cmd); + if ($cmd eq 'QUIT' ) { + print $cl "+OK bye\r\n"; + last; + } elsif ( $cmd eq 'CAPA' ) { + print $cl "+OK\r\n". + ".\r\n"; + } else { + diag("received unknown command: $cmd"); + print "-ERR unknown cmd\r\n"; + } + } + + diag("POP3 dialog done"); +} diff --git a/t/pop3_ssl.t b/t/pop3_ssl.t new file mode 100644 index 0000000..08ef266 --- /dev/null +++ b/t/pop3_ssl.t @@ -0,0 +1,122 @@ +use strict; +use warnings; +use Test::More; +use File::Temp 'tempfile'; +use Net::POP3; + +my $debug = 0; # Net::POP3 Debug => .. + +my $parent = 0; + +plan skip_all => "no SSL support found in Net::POP3" if ! Net::POP3->can_ssl; + +plan skip_all => "fork not supported on this platform" + if grep { $^O =~m{$_} } qw(MacOS VOS vmesa riscos amigaos); + +plan skip_all => "incomplete or to old version of IO::Socket::SSL" if ! eval { + require IO::Socket::SSL + && IO::Socket::SSL->VERSION(1.968) + && require IO::Socket::SSL::Utils + && defined &IO::Socket::SSL::Utils::CERT_create; +}; + +my $srv = IO::Socket::INET->new( + LocalAddr => '127.0.0.1', + Listen => 10 +); +plan skip_all => "cannot create listener on localhost: $!" if ! $srv; +my $saddr = $srv->sockhost.':'.$srv->sockport; + +plan tests => 2; + +my ($ca,$key) = IO::Socket::SSL::Utils::CERT_create( CA => 1 ); +my ($fh,$cafile) = tempfile(); +print $fh IO::Socket::SSL::Utils::PEM_cert2string($ca); +close($fh); + +$parent = $$; +END { unlink($cafile) if $$ == $parent } + +my ($cert) = IO::Socket::SSL::Utils::CERT_create( + subject => { CN => 'pop3.example.com' }, + issuer_cert => $ca, issuer_key => $key, + key => $key +); + +test(1); # direct ssl +test(0); # starttls + + +sub test { + my $ssl = shift; + defined( my $pid = fork()) or die "fork failed: $!"; + exit(pop3_server($ssl)) if ! $pid; + pop3_client($ssl); + wait; +} + + +sub pop3_client { + my $ssl = shift; + my %sslopt = ( + SSL_verifycn_name => 'pop3.example.com', + SSL_ca_file => $cafile + ); + $sslopt{SSL} = 1 if $ssl; + my $cl = Net::POP3->new($saddr, %sslopt, Debug => $debug); + diag("created Net::POP3 object"); + if (!$cl) { + fail( ($ssl ? "SSL ":"" )."POP3 connect failed"); + } elsif ($ssl) { + $cl->quit; + pass("SSL POP3 connect success"); + } elsif ( ! $cl->starttls ) { + no warnings 'once'; + fail("starttls failed: $IO::Socket::SSL::SSL_ERROR"); + } else { + $cl->quit; + pass("starttls success"); + } +} + +sub pop3_server { + my $ssl = shift; + my $cl = $srv->accept or die "accept failed: $!"; + my %sslargs = ( + SSL_server => 1, + SSL_cert => $cert, + SSL_key => $key, + ); + if ( $ssl ) { + if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) { + diag("initial ssl handshake with client failed"); + return; + } + } + + print $cl "+OK localhost ready\r\n"; + while (<$cl>) { + my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_; + $cmd = uc($cmd); + if ($cmd eq 'QUIT' ) { + print $cl "+OK bye\r\n"; + last; + } elsif ( $cmd eq 'CAPA' ) { + print $cl "+OK\r\n". + ( $ssl ? "" : "STLS\r\n" ). + ".\r\n"; + } elsif ( ! $ssl and $cmd eq 'STLS' ) { + print $cl "+OK starting ssl\r\n"; + if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) { + diag("initial ssl handshake with client failed"); + return; + } + $ssl = 1; + } else { + diag("received unknown command: $cmd"); + print "-ERR unknown cmd\r\n"; + } + } + + diag("POP3 dialog done"); +} diff --git a/t/require.t b/t/require.t index 163c8bd..973ed41 100644 --- a/t/require.t +++ b/t/require.t @@ -2,11 +2,11 @@ BEGIN { unless (-d 'blib') { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; @@ -2,11 +2,11 @@ BEGIN { unless (-d 'blib') { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; @@ -26,7 +26,7 @@ print "1..3\n"; my $i = 1; $smtp = Net::SMTP->new(Debug => 0) - or (print("not ok 1\n"), exit); + or (print("not ok 1\n"), exit); print "ok 1\n"; diff --git a/t/smtp_ipv6.t b/t/smtp_ipv6.t new file mode 100644 index 0000000..6a01520 --- /dev/null +++ b/t/smtp_ipv6.t @@ -0,0 +1,59 @@ +use strict; +use warnings; +use Test::More; +use File::Temp 'tempfile'; +use Net::SMTP; + +my $debug = 0; # Net::SMTP->new( Debug => .. ) + +my $inet6class = Net::SMTP->can_inet6; +plan skip_all => "no IPv6 support found in Net::SMTP" if ! $inet6class; + +plan skip_all => "fork not supported on this platform" + if grep { $^O =~m{$_} } qw(MacOS VOS vmesa riscos amigaos); + +my $srv = $inet6class->new( + LocalAddr => '::1', + Listen => 10 +); +plan skip_all => "cannot create listener on ::1: $!" if ! $srv; +my $saddr = "[".$srv->sockhost."]".':'.$srv->sockport; +diag("server on $saddr"); + +plan tests => 1; + +defined( my $pid = fork()) or die "fork failed: $!"; +exit(smtp_server()) if ! $pid; + +my $cl = Net::SMTP->new($saddr, Debug => $debug); +diag("created Net::SMTP object"); +if (!$cl) { + fail("IPv6 SMTP connect failed"); +} else { + $cl->quit; + pass("IPv6 success"); +} +wait; + +sub smtp_server { + my $cl = $srv->accept or die "accept failed: $!"; + print $cl "220 welcome\r\n"; + while (<$cl>) { + my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_; + $cmd = uc($cmd); + if ($cmd eq 'QUIT' ) { + print $cl "250 bye\r\n"; + last; + } elsif ( $cmd eq 'HELO' ) { + print $cl "250 localhost\r\n"; + } elsif ( $cmd eq 'EHLO' ) { + print $cl "250-localhost\r\n". + "250 HELP\r\n"; + } else { + diag("received unknown command: $cmd"); + print "500 unknown cmd\r\n"; + } + } + + diag("SMTP dialog done"); +} diff --git a/t/smtp_ssl.t b/t/smtp_ssl.t new file mode 100644 index 0000000..e7391f3 --- /dev/null +++ b/t/smtp_ssl.t @@ -0,0 +1,124 @@ +use strict; +use warnings; +use Test::More; +use File::Temp 'tempfile'; +use Net::SMTP; + +my $debug = 0; # Net::SMTP Debug => .. + +my $parent = 0; + +plan skip_all => "no SSL support found in Net::SMTP" if ! Net::SMTP->can_ssl; + +plan skip_all => "fork not supported on this platform" + if grep { $^O =~m{$_} } qw(MacOS VOS vmesa riscos amigaos); + +plan skip_all => "incomplete or to old version of IO::Socket::SSL" if ! eval { + require IO::Socket::SSL + && IO::Socket::SSL->VERSION(1.968) + && require IO::Socket::SSL::Utils + && defined &IO::Socket::SSL::Utils::CERT_create; +}; + +my $srv = IO::Socket::INET->new( + LocalAddr => '127.0.0.1', + Listen => 10 +); +plan skip_all => "cannot create listener on localhost: $!" if ! $srv; +my $saddr = $srv->sockhost.':'.$srv->sockport; + +plan tests => 2; + +my ($ca,$key) = IO::Socket::SSL::Utils::CERT_create( CA => 1 ); +my ($fh,$cafile) = tempfile(); +print $fh IO::Socket::SSL::Utils::PEM_cert2string($ca); +close($fh); + +$parent = $$; +END { unlink($cafile) if $$ == $parent } + +my ($cert) = IO::Socket::SSL::Utils::CERT_create( + subject => { CN => 'smtp.example.com' }, + issuer_cert => $ca, issuer_key => $key, + key => $key +); + +test(1); # direct ssl +test(0); # starttls + + +sub test { + my $ssl = shift; + defined( my $pid = fork()) or die "fork failed: $!"; + exit(smtp_server($ssl)) if ! $pid; + smtp_client($ssl); + wait; +} + + +sub smtp_client { + my $ssl = shift; + my %sslopt = ( + SSL_verifycn_name => 'smtp.example.com', + SSL_ca_file => $cafile + ); + $sslopt{SSL} = 1 if $ssl; + my $cl = Net::SMTP->new($saddr, %sslopt, Debug => $debug); + diag("created Net::SMTP object"); + if (!$cl) { + fail( ($ssl ? "SSL ":"" )."SMTP connect failed"); + } elsif ($ssl) { + $cl->quit; + pass("SSL SMTP connect success"); + } elsif ( ! $cl->starttls ) { + no warnings 'once'; + fail("starttls failed: $IO::Socket::SSL::SSL_ERROR"); + } else { + $cl->quit; + pass("starttls success"); + } +} + +sub smtp_server { + my $ssl = shift; + my $cl = $srv->accept or die "accept failed: $!"; + my %sslargs = ( + SSL_server => 1, + SSL_cert => $cert, + SSL_key => $key, + ); + if ( $ssl ) { + if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) { + diag("initial ssl handshake with client failed"); + return; + } + } + + print $cl "220 welcome\r\n"; + while (<$cl>) { + my ($cmd,$arg) = m{^(\S+)(?: +(.*))?\r\n} or die $_; + $cmd = uc($cmd); + if ($cmd eq 'QUIT' ) { + print $cl "250 bye\r\n"; + last; + } elsif ( $cmd eq 'HELO' ) { + print $cl "250 localhost\r\n"; + } elsif ( $cmd eq 'EHLO' ) { + print $cl "250-localhost\r\n". + ( $ssl ? "" : "250-STARTTLS\r\n" ). + "250 HELP\r\n"; + } elsif ( ! $ssl and $cmd eq 'STARTTLS' ) { + print $cl "250 starting ssl\r\n"; + if ( ! IO::Socket::SSL->start_SSL($cl, %sslargs)) { + diag("initial ssl handshake with client failed"); + return; + } + $ssl = 1; + } else { + diag("received unknown command: $cmd"); + print "500 unknown cmd\r\n"; + } + } + + diag("SMTP dialog done"); +} @@ -2,14 +2,14 @@ BEGIN { if ($ENV{PERL_CORE}) { - chdir 't' if -d 't'; - @INC = '../lib'; + chdir 't' if -d 't'; + @INC = '../lib'; } if (!eval "require Socket") { - print "1..0 # no Socket\n"; exit 0; + print "1..0 # no Socket\n"; exit 0; } if (ord('A') == 193 && !eval "require Convert::EBCDIC") { - print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; + print "1..0 # EBCDIC but no Convert::EBCDIC\n"; exit 0; } $INC{'IO/Socket.pm'} = 1; $INC{'IO/Select.pm'} = 1; @@ -50,84 +50,84 @@ is( Net::Time::inet_daytime('bob'), 'z', 'inet_daytime() should receive data' ); # magic numbers defined in Net::Time my $offset = $^O eq 'MacOS' ? - (4 * 31536000) : (70 * 31536000 + 17 * 86400); + (4 * 31536000) : (70 * 31536000 + 17 * 86400); # check for correct args (time, 13) # pretend it is only six seconds since the offset, create a fake message # inet_time IO::Socket::INET::set_message(pack("N", $offset + 6)); is( Net::Time::inet_time('foo'), 6, - 'inet_time() should calculate time since offset for time()' ); + 'inet_time() should calculate time since offset for time()' ); my %fail; sub make_fail { - my ($pack, $func, $num) = @_; - $num = 1 unless defined $num; + my ($pack, $func, $num) = @_; + $num = 1 unless defined $num; - $fail{$pack}{$func} = $num; + $fail{$pack}{$func} = $num; } package IO::Socket::INET; $fail{'IO::Socket::INET'} = { - new => 0, - 'send' => 0, + new => 0, + 'send' => 0, }; sub new { - my $class = shift; - return if $fail{$class}{new} and $fail{$class}{new}--; - bless( { @_ }, $class ); + my $class = shift; + return if $fail{$class}{new} and $fail{$class}{new}--; + bless( { @_ }, $class ); } sub send { - my $self = shift; - my $class = ref($self); - return if $fail{$class}{'send'} and $fail{$class}{'send'}--; - $self->{sent} .= shift; + my $self = shift; + my $class = ref($self); + return if $fail{$class}{'send'} and $fail{$class}{'send'}--; + $self->{sent} .= shift; } my $msg; sub set_message { - if (ref($_[0])) { - $_[0]->{msg} = $_[1]; - } else { - $msg = shift; - } + if (ref($_[0])) { + $_[0]->{msg} = $_[1]; + } else { + $msg = shift; + } } sub do_recv { - my ($len, $msg) = @_[1,2]; - $_[0] .= substr($msg, 0, $len); + my ($len, $msg) = @_[1,2]; + $_[0] .= substr($msg, 0, $len); } sub recv { - my ($self, $buf, $length, $flags) = @_; - my $message = exists $self->{msg} ? - $self->{msg} : $msg; - - if (defined($message)) { - do_recv($_[1], $length, $message); - } - 1; + my ($self, $buf, $length, $flags) = @_; + my $message = exists $self->{msg} ? + $self->{msg} : $msg; + + if (defined($message)) { + do_recv($_[1], $length, $message); + } + 1; } package IO::Select; sub new { - my $class = shift; - return if defined $fail{$class}{new} and $fail{$class}{new}--; - bless({sock => shift}, $class); + my $class = shift; + return if defined $fail{$class}{new} and $fail{$class}{new}--; + bless({sock => shift}, $class); } sub can_read { - my ($self, $timeout) = @_; - my $class = ref($self); - return if defined $fail{$class}{can_read} and $fail{class}{can_read}--; - $self->{sock}{timeout} = $timeout; - 1; + my ($self, $timeout) = @_; + my $class = ref($self); + return if defined $fail{$class}{can_read} and $fail{class}{can_read}--; + $self->{sock}{timeout} = $timeout; + 1; } 1; |