Git Product home page Git Product logo

p6-io-socket-async-ssl's Introduction

IO::Socket::Async::SSL Build Status

This module provides a secure sockets implementation with an API very much like that of the Perl 6 built-in IO::Socket::Async class. For the client case, provided the standard certificate and host verification are sufficient, it is drop-in replacement. The server case only needs two extra arguments to listen, specifying the server key and certificate.

As with IO::Socket::Async, it is safe to have concurrent connections and to share them across threads.

Synopsis

Client:

use IO::Socket::Async::SSL;

my $conn = await IO::Socket::Async::SSL.connect('www.perl6.org', 443);
$conn.print: "GET / HTTP/1.0\r\nHost: www.perl6.org\r\n\r\n";
react {
    whenever $conn {
        .print
    }
}
$conn.close;

Server (assumes certificate and key files server-crt.pem and server-key.pem):

use IO::Socket::Async::SSL;

react {
    my %ssl-config =
        certificate-file => 'server-crt.pem',
        private-key-file => 'server-key.pem';
    whenever IO::Socket::Async::SSL.listen('localhost', 4433, |%ssl-config) -> $conn {
        my $req = '';
        whenever $conn {
            $req ~= $_;
            if $req.contains("\r\n\r\n") {
                say $req.lines[0];
                await $conn.print(
                    "HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n" ~
                    "<strong>Hello from a Perl 6 HTTP server</strong>\n");
                $conn.close;
            }
        }
    }
}

Client

The connect method on IO::Socket::Async::SSL is used to establish a SSL connection to a server. It requies two positional arguments, which specify the host and port to connect to. It returns a Promise, which will be kept with an IO::Socket::Async::SSL instance when the connection is established and the SSL handshake completed.

my $conn = await IO::Socket::Async::SSL.connect($host, $port);

By default, the SSL certificate will be verified, using the default set of accepted Certificate Authorities. The Promise return by conenct will be broken if verification fails.

Sometimes it is convenient to create a CA and use it to sign certificates for internal use, for example to secure communications between a set of services on an internal network. In this case, the ca-file named argument can be passed to specify the certificate authority certificate file:

my $ca-file = '/config/ca-crt.pem';
my $conn = await IO::Socket::Async::SSL.connect('user-service', 443, :$ca-file);

Alternatively, a ca-path argument can be specified, indicating a directory where one or more certificates may be found.

It is possible to disable certificate verification by passing the insecure named argument a true value. As the name suggests, this is not a secure configuration, since there is no way for the client to be sure that it is communicating with the intended server. Therefore, it is vulnerable to man-in-the-middle attacks.

For control over the ciphers that may be used, pass the ciphers argument to connect. It should be a string in OpenSSL cipher list format.

Server

The listen method returns a Supply that, when tapped, will start an SSL server. The server can be shut down by closing the tap. Whenever a connection is made to the server, the Supply will emit an IO::Socket::Async::SSL instance. The listen method requires two positional arguments, specifying the host and port to listen on. Two named arguments are also required, providing the certificate-file and private-key-file.

my %ssl-config =
    certificate-file => 'server-crt.pem',
    private-key-file => 'server-key.pem';
my $connections = IO::Socket::Async::SSL.listen('localhost', 4433, |%ssl-config);
react {
    my $listener = do whenever $connections -> $conn {
        say "Got a connection!";
        $conn.close;
    }

    whenever signal(SIGINT) {
        say "Shutting down...";
        $listener.close;
        exit;
    }
}

For control over the ciphers that may be used, pass the ciphers argument to connect. It should be a string in OpenSSL cipher list format. The following boolean options are also accepted:

  • prefer-server-ciphers - indicates that the order of the ciphers list as configured on the server should be preferred over that of the one presented by the client
  • no-compression - disables compression
  • no-session-resumption-on-renegotiation

Common client and server functionality

Both the connect and listen methods take the following optional named arguments:

  • enc, which specifies the encoding to use when the socket is used in character mode. Defaults to utf-8.
  • scheduler, which specifies the scheduler to use for processing events from the underlying IO::Socket::Async instance. The default is $*SCHEDULER. There is rarely a need to change this.

The Supply, print, write, and close methods have the same semantics as in IO::Socket::Async.

Upgrading connections

Some protocols use opportunistic TLS, where the decision to use transport layer security is first negotiated using a non-encrypted protocol - provided negotiation is successful - a TLS handshake is then performed. This functionality is provided by the upgrade-server and upgrade-client methods. Note that the socket to upgrade must be an instance of IO::Socket::Async. Further, it is important to stop reading from the socket after initiating the upgrade.

Here is an example of using upgrade-server. Note the use of last in order to terminate the whenever that reads from the plain connection.

react whenever IO::Socket::Async.listen('localhost', TEST_PORT) -> $plain-conn {
    whenever $plain-conn.Supply -> $start {
        if $start eq "Psst, let's talk securely!\n" {
            my $enc-conn-handshake = IO::Socket::Async::SSL.upgrade-server(
                $plain-conn,
                private-key-file => 't/certs-and-keys/server.key',
                certificate-file => 't/certs-and-keys/server-bundle.crt'
            );
            whenever $enc-conn-handshake -> $enc-conn {
                uc-service($enc-conn);
            }
            $plain-conn.print("OK, let's talk securely!\n");
            last;
        }
        else {
            $plain-conn.print("OK, let's talk insecurely\n");
            uc-service($plain-conn);
        }
    }

    sub uc-service($conn) {
        whenever $conn -> $crypt-text {
            whenever $conn.print($crypt-text.uc) {
                $conn.close;
            }
        }
    }
}

Here's an example using upgrade-client; again, take note of the last.

my $plain-conn = await IO::Socket::Async.connect('localhost', TEST_PORT);
await $plain-conn.print("Psst, let's talk securely!\n");
react whenever $plain-conn -> $msg {
    my $enc-conn-handshake = IO::Socket::Async::SSL.upgrade-client(
        $plain-conn,
        host => 'localhost',
        ca-file => 't/certs-and-keys/ca.crt');
    whenever $enc-conn-handshake -> $enc-conn {
        await $enc-conn.print("hello!\n");
        whenever $enc-conn.head -> $got {
            print $got; # HELLO!
            done;
        }
    }

    last;
}

Bugs, feature requests, and contributions

Please use GitHub Issues to file bug reports and feature requests. If you wish to contribute to this module, please file a GitHub Pull Request, or email a Git patch (produced using format-patch) to [email protected]. Please also use this email address to report security vulnerabilities.

p6-io-socket-async-ssl's People

Contributors

altai-man avatar dwarring avatar gfldex avatar japhb avatar jnthn avatar lukasvalle avatar nicqrocks avatar samcv avatar seaker avatar ufobat avatar ugexe avatar winthrowe avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.