Installing Net::SMTP::SSL for sending patches with Git over secure SMTPEdit

High Sierra notes

Net::SMTP::SSL still doesn’t come with macOS, so you’ll need to install it:

sudo -H /usr/bin/cpan -f Net::SMTP::SSL

Note the full path to /usr/bin/cpan. This is necessary if you have other Perl installs floating around in your path (from Homebrew, for example), to ensure that the module gets installed for the system Perl (the one Git uses).

My first attempt at this failed because the Stack Overflow page I found suggested that I use cpanminus to install the module. So, brew install cpanminus, and then sudo cpanm Net::SMTP::SSL, but that installs the module in the Homebrew-installed Perl directory (/usr/local/Cellar/perl/5.28.0/lib/perl5/site_perl/5.28.0) and Git can’t see it because its shebang lines use a hard-coded /usr/bin/perl (even though this Git is itself installed by Homebrew).

I saw that the error output from git-send-email showed @INC paths related to Perl 5.18 (the system Perl) but perl -V (Homebrew Perl) showed paths relating to 5.28. Forcing git-send-mail to see the installed module by prefixing it with env PERL5LIB=/usr/local/Cellar/perl/5.28.0/lib/perl5/site_perl/5.28.0 naturally doesn’t work, because the version mistmatch will crash the perl process. A bit of a wild goose chase that could have been avoided by following this gist instead.

Older notes

If you want to use Git to submit patches via email over secure SMTP you will need to install the Net::SMTP::SSL module, if you don’t have it installed already on your system.

sudo -H cpan Net::SMTP::SSL

You’ll know that this is required if you see a message like the following on trying to use git-send-email:

Can't locate Net/SMTP/SSL.pm in @INC (@INC contains: ...) at /usr/local/bin/git-send-email line 618.

After installing I got a new failure message:

Unable to initialize SMTP properly.  Is there something wrong with your config? at /usr/local/bin/git-send-email line 629.

A minimal test script showed that the problem wasn’t in Git:

#! /usr/bin/env perl

use warnings;
use strict;
use Net::SMTP::SSL;
$IO::Socket::SSL::DEBUG = 1;
my $smtp = Net::SMTP::SSL->new('smtp.gmail.com', Port => 465, Debug => 1,);

if (!$smtp) {
  die "didn't work";
}

After much Googling I finally found this diamond among the many posts complaining about Net::SMTP::SSL not working and offering advice of differing levels of usefulness:

The meat of the problem is here:

Problem is fixed with IO::Socket::SSL version 1.06 which I just uploaded to CPAN. Problem was, that IO::Socket::SSL "sanitized" the Arguments it gets by setting undef stuff to "". This way it gave a LocalPort of "" to IO::Socket::INET which did not like it.

My problem was that Mac OS X ships with an older version of IO::Socket::SSL (0.999), so I had to upgrade it:

sudo -H cpan IO::Socket::SSL

But some of the tests failed (excerpt):

t/nonblock..............FAILED tests 26-27
	Failed 2/27 tests, 92.59% okay
t/readline..............ok
t/sessions..............ok
t/start-stopssl.........ok
t/startssl..............ok
t/sysread_write.........ok
Failed Test  Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
t/nonblock.t               27    2   7.41%  26-27
Failed 1/14 test scripts, 92.86% okay. 2/220 subtests failed, 99.09% okay.
make: *** [test_dynamic] Error 2
  /usr/bin/make test -- NOT OK
Running make install
  make test had returned bad status, won't install without force

So to gather more information I decided to run the tests manually:

sudo -s -H
cd /var/root/.cpan/sources/authors/id/S/SU/SULLR
tar xzvf IO-Socket-SSL-1.12.tar.gz
cd IO-Socket-SSL-1.12
perl Makefile.PL
make
make test

Curiously, this time even more tests failed:

t/nonblock..............NOK 19sysread() on closed filehandle GEN2 at t/nonblock.t line 322.
Use of uninitialized value in numeric eq (==) at t/nonblock.t line 322.
setsockopt() on closed socket GEN2 at (eval 14) line 2.
t/nonblock..............NOK 21getsockopt() on closed socket GEN2 at (eval 14) line 3.
Use of uninitialized value in unpack at (eval 14) line 3.
Use of uninitialized value in concatenation (.) or string at (eval 14) line 3.
syswrite() on closed filehandle GEN2 at t/nonblock.t line 179.
t/nonblock..............NOK 25sysread() on closed filehandle GEN2 at t/nonblock.t line 332.
t/nonblock..............FAILED tests 16, 19, 21-22, 24-27
	Failed 8/27 tests, 70.37% okay
t/readline..............ok
t/sessions..............ok
t/start-stopssl.........ok
t/startssl..............ok
t/sysread_write.........ok
Failed Test  Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
t/nonblock.t               27    8  29.63%  16 19 21-22 24-27
Failed 1/14 test scripts, 92.86% okay. 8/220 subtests failed, 96.36% okay.
make: *** [test_dynamic] Error 2

On a subsequent run, yet another number of subtests failed:

t/nonblock..............NOK 18sysread() on closed filehandle GEN2 at t/nonblock.t line 322.
Use of uninitialized value in numeric eq (==) at t/nonblock.t line 322.
setsockopt() on closed socket GEN2 at (eval 14) line 2.
getsockopt() on closed socket GEN2 at (eval 14) line 3.
Use of uninitialized value in unpack at (eval 14) line 3.
t/nonblock..............NOK 20Use of uninitialized value in concatenation (.) or string at (eval 14) line 3.
syswrite() on closed filehandle GEN2 at t/nonblock.t line 179.
t/nonblock..............NOK 24sysread() on closed filehandle GEN2 at t/nonblock.t line 332.
t/nonblock..............FAILED tests 13, 15, 18, 20-21, 23-27
	Failed 10/27 tests, 62.96% okay
t/readline..............ok
t/sessions..............ok
t/start-stopssl.........ok
t/startssl..............ok
t/sysread_write.........ok
Failed Test  Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
t/nonblock.t               27   10  37.04%  13 15 18 20-21 23-27
Failed 1/14 test scripts, 92.86% okay. 10/220 subtests failed, 95.45% okay.
make: *** [test_dynamic] Error 2

Out of curiosity I wanted to see if the version that ships with Leopard (0.999) also fails those tests:

wget http://search.cpan.org/CPAN/authors/id/S/SU/SULLR/IO-Socket-SSL-0.999.tar.gz
tar xzvf IO-Socket-SSL-0.999.tar.gz
cd IO-Socket-SSL-0.999
perl Makefile.PL
make
make test

The results:

t/nonblock.........FAILED tests 13, 26
	Failed 2/27 tests, 92.59% okay
t/readline.........ok
t/sessions.........ok
t/startssl.........ok
t/sysread_write....ok
Failed Test  Stat Wstat Total Fail  Failed  List of Failed
-------------------------------------------------------------------------------
t/nonblock.t               27    2   7.41%  13 26
Failed 1/11 test scripts, 90.91% okay. 2/175 subtests failed, 98.86% okay.

Sure enough, it does, and just as inconsistently as the newer version (repeating the test 5 times it failed between 2 and 9 times). Are there any differences between the shipped version and the official 0.999 release?

diff SSL.pm /System/Library/Perl/Extras/5.8.8/IO/Socket/SSL.pm && echo "same"

Output: same

So, if Apple is prepared to ship a broken module with their OS I guess it’s ok to install a newer, broken version of the same module, so I proceeded to force the install:

sudo -H cpan -fi IO::Socket::SSL

This too failed, and my guess was that it was because the installed CPAN module is too old (v1.7602, but v1.9204 is available at the time of writing), so I tried updating it:

# upgrade CPAN first
# (if it's too old it could try upgrading Perl when we try upgrading Bundle::CPAN)
sudo -H perl -MCPAN -e '$ENV{FTP_PASSIVE} = 1; install CPAN'

This also failed:

Checksum mismatch for distribution file. Please investigate.

Distribution id = A/AN/ANDK/CPAN-1.9204.tar.gz
    CPAN_USERID  ANDK (Andreas J. Koenig <andreas.koenig@anima.de>)
    CALLED_FOR   CPAN
    CONTAINSMODS CPAN::Nox CPAN::FirstTime CPAN::Tarzip CPAN::DeferedCode CPAN::Debug CPAN::Kwalify CPAN::Version CPAN::Admin CPAN::Queue CPAN CPAN::HandleConfig
    MD5_STATUS
    incommandcolor 1
    localfile    /var/root/.cpan/sources/authors/id/A/AN/ANDK/CPAN-1.9204.tar.gz

I'd recommend removing
/var/root/.cpan/sources/authors/id/A/AN/ANDK/CPAN-1.9204.tar.gz. Its MD5
checksum is incorrect. Maybe you have configured your 'urllist' with
a bad URL. Please check this array with 'o conf urllist', and retry.

So I tried removing the URL, which happened to be the first in the list:

cpan> o conf urllist
cpan> o conf urllist shift
cpan> o conf urllist
cpan> o conf commit
cpan> exit

And trying again.

sudo rm /var/root/.cpan/sources/authors/id/A/AN/ANDK/CPAN-1.9204.tar.gz
sudo -H perl -MCPAN -e '$ENV{FTP_PASSIVE} = 1; install CPAN'

This time it worked and I could proceed:

sudo -H cpan Bundle::CPAN
sudo -H cpan -fi IO::Socket::SSL

After this my test script did indeed pass, and git-send-email stopped bailing with that error also; but all was not well however, because after connecting to the server the script would hang indefinitely.

Further investigation reveals that it was failing on trying to use Kerberos authentication. So I changed my sendmail confAUTH_MECHANISMS from:

EXTERNAL GSSAPI DIGEST-MD5 CRAM-MD5 LOGIN PLAIN

To:

EXTERNAL DIGEST-MD5 CRAM-MD5 LOGIN PLAIN

And everything works fine.

See also