If you want to implement SSL in perl, there's what seems like a dizzying maze of available options. Some better and some worse. Some more suited to one thing, and some to another. This article will help find your way through some of that maze (but not all of it). A thorough review would be nice, but this is not that.
I set out to write an IMAP mail client in perl, and with that in mind, I won't have much to say about SSL modules that are only or primarily concerned with the secure web (https) protocol. My goal was to come up with an implementation of a program that would work on the most different platforms, without too much trouble on my part, and without assuming that end-users can install perl modules.
I had to learn an unncessary amount about too many implementations none of which work extremely well. It's all a good argument for having some sort of perl-poobah that would oversee module development and bless certain recommended interfaces.
At any rate, for IMAP, I wanted the ability to establish a secure connection, and I wanted to do non-blocking reads from the socket descriptor from within a polling (select()) loop. I also wanted the ability to take an existing connection, and start up the SSL stuff in the middle, to support the IMAP STARTTLS command on an insecure connection.
I will briefly say that for the web in most cases, just use LWP. It will work as long as you have an appropriate SSL module installed. I can't give a concrete answer on what "appropriate" means, but I'm sure that Net::SSL (which is in turn part of Crypt::SSLeay) works. I've seen it reported that Net::SSLeay (and maybe also IO::Socket::SSL) will work, but that doesn't match with my testing. Then again, my error message wasn't the expected one of needing a module, so maybe it's my mistake, or maybe my LWP isn't up-to-date.
The following modules are the things that I'm aware of that can do what
we need:
Crypt::SSLeay
Net::SSL
Net::SSLeay
Net::SSLeay::Handle
IO::Socket::SSL
Net::Server::Proto::SSL
For my purposes, IO::Socket::SSL provides the most features I wanted, and Net::SSL close behind. They'll reverse positions if I can switch a live connection to SSL with Net::SSL, becauase Net::SSL looks to be better maintained and I think it deals with file handles better.. IO::Socket::SSL lives on top of Net::SSLeay. On the outside Net::SSLeay is primarily oriented towards https, but it supposedly has lots of good stuff hidden inside (and hidden from documentation too). You can use Net::SSLeay::Handle for non web things, but dealing with it's file descriptors is at least a pain, and possibly completely broken. Net::SSLeay itself can supposedly do non-web things, but it isn't well documented. Net::Server::Proto::SSL is so far "experimental". It's somehow related to IO::Socket::SSL.
use Net::SSL; ... $sock = Net::SSL->new(PeerAddr => $host, PeerPort => $port, Proto => 'tcp', Timeout => 200) || die "sslsocket"; ... print $sock->getchunk;
Net::SSL includes a getchunk() function, which just grabs all the data available on the network (up to 32K) and gives it to you. It blocks until something is read, or a timeout expires. This worked great for my purposes.
Since I was using an IMAP server that was only running the secure port, this was also fine for testing. But if there's a way in Net::SSL to convert an existing connection to SSL, it's not obvious from the documentation.
use Net::SSLeay::Handle; ... tie(*S2,"Net::SSLeay::Handle", $host, $port); my $sock = \*S2;So that seemed to work. At least, it made a connection, and reading from <$sock> would do just what you'd hope. But things went downhill from there. I didn't want to use <$sock>, I wanted to use read(), to read a chunk of data from the socket, just the way getchunk() in Net::SSL works. In other words something like "read($sock,$buf,8192);". Now this should be fine - with normal socket reading semantics, a read will block until it gets something, but then it will return even if it doesn't get everything. Perl does get in the way here a bit, but with normal perl sockets, using sysread() skips all that rot, and sysread() from a socket works just like I'd expect.
But this didn't do that. Using either read or sysread, the call blocked until the buffer size I gave was satisfied. Not good. Fine, I thought I can still work with this, I'll just make the socket completely non-blocking with "$sock->blocking(0);" (hint: don't forget to add in "use IO::Handle;").
That sort-of worked. But when I tried to put things inside of a select(), I found that $sock was getting select()ed, but then a subsequent sysread would find nothing there to read. At this point I punted. Granted, the error may have been mine, and I could have been minutes away from solving it, but I was getting annoyed.
use IO::Socket::SSL;
...
$sock = IO::Socket::SSL->new("$host:$port");
...
sysread($sock,$buf,8192);
Happy happy, joy joy. It worked fine. sysread() does exactly what I
expect it to, as does select(). I don't care for the "$host:$port"
syntax (too web-specific looking), but that's a minor quibble.
But that's still not a perfect solution.
Nevertheless, I needed a solution that would let one program run on platforms which had different SSL options. I can at least cover what seem to be the two most popular approaches. So here's what I've got at this point. If there's a better way, I'd be happy to hear it.
eval {
require Net::SSL;
Net::SSL->import();
};
if ($@) {
require IO::Socket::SSL;
IO::Socket::SSL->import();
if ($@) {
print STDERR "Can't find SSL module!\n";
exit(-1);
}
$SSL="Net";
} else {
$SSL="Crypt";
}
. . .
if ($SSL eq "Crypt") {
$sock = Net::SSL->new(PeerAddr => $host, PeerPort => $port,
Proto => 'tcp', Timeout => 200) || die "new Net::SSL";
} else {
$sock = IO::Socket::SSL->new("$host:$port") || die "new IO::Socket::SSL";
}
. . .
sub GetChunk {
my $s=shift(@_);
if ($SSL eq "Crypt") {
return($s->getchunk);
} else {
my $buf="";
sysread($s,$buf,8192);
return($buf);
}
}
And, I'm still working on implementing STARTTLS, which requires
jump-starting SSL on an already in-use socket. I'm pretty sure I can
do it with IO::Socket::SSL. I'll see if I can make Net::SSL do it too.
3 comments:
|
At 2008/12/20 13:23 Scott Gifford wrote:
|
Have you tried making the sockets nonblocking, like this:
$sock->blocking(0);
For me at least, that seems to give the effect you're looking for.
|
At 2008/12/20 13:34 wrote:
|
Yes, I've tried that. It sort of works, but the behaviour is subtly different. Standard socket reading behaviour is to block until it reads something. The bad behavior I was getting was to block until it reads everything that was requested. With your non-blocking suggestion, it's possible to return after reading nothing. A select() would in theory eliminate this issue, but when I tried non-blocking, I wasn't getting correct behavior from the select() -- it would come up in the select() when nothing was there to be read; though admittedly I didn't try to thoroughly debug that issue. tom
|
|
At 2008/12/20 13:59 Scott Gifford wrote:
|
Ah, I see. Select and SSL don't always mix well, but I have had pretty good luck using nonblocking sockets with select() and sysread(), just ignoring errors EAGAIN and EINTR returned by sysread().
|
Fine's Home
|
![]() |
Send Me Email |