Mitigate Client-Initiated TLS Renegotiation DoS

**Client-Initiated Renegotiation DoS Vulnerability**

The idea of using SSL/TLS renegotiation as a DoS attack vector was first widely publicized in 2011 with the release of the [TLC-SSL-DOS][thc-ssl-dos] tool. The attack vector is plausible because establishing SSL connections requires disproportionately more processing resources for servers than for clients ([Bernat][bernat]). This gap in resource consumption creates an easy target for nefarious clients looking to exhaust server resources through DoS attacks in which the TLS handshake is repeatedly renegotiated.

At this time encrypted stream servers have no defense against malicious client-initiated TLS renegotiation.

**Patch**

This patch uses a standard leaky bucket algorithm to automatically rate-limit client-initiated TLS renegotiations in encrypted stream servers (allowing two renegs every 300 seconds by default). This feature can be customized using the following new SSL context options:

* `"reneg_limit"` *(the number of allowed renegotiations per time window)*
* `"reneg_window"` *(the size of the renegotiation time window in seconds)*

Most servers shouldn't need to customize these values as limiting applies automatically. Additionally, users may specify the following context option to receive notifications when a stream client is rate-limited due to a reneg policy violation:

* `"reneg_limit_callback"`

Servers can prevent automatic closing of a client stream upon reneg policy violation by returning `true` from the callback. In real DoS situations this can be useful to shutdown reads on the client socket without immediately closing it, add the client IP to a blocklist, etc. If no callback is specified or if the callback returns any value other than `true` PHP will shutdown the client connection immediately. Note that if no callback is assigned an `E_WARNING` is generated to inform the script of the violation.

**Example**

```php
<?php
$bindTo = 'tls://127.0.0.1:443';
$flags = STREAM_SERVER_BIND | STREAM_SERVER_LISTEN;
$ctx = stream_context_create(['ssl' => [
    "local_cert" => "/path/to/cert.pem",
    "reneg_limit" => 0, // don't allow any renegotiations
    "reneg_limit_callback" => function($client) {
        printf("Client socket at %s violated the reneg policy!\n", 
            stream_socket_get_name($client, $want_peer = true));
        
        stream_socket_shutdown($client, STREAM_SHUT_RD);

        // Tell PHP not to close the client (we should schedule an
        // event or something to handle this later)
        return true;
    }
]]);
$server = stream_socket_server($bindTo, $errNo, $errStr, $flags, $ctx);
```

 [bernat]: http://vincent.bernat.im/en/blog/2011-ssl-dos-mitigation.html
 [thc-ssl-dos]: https://www.thc.org/thc-ssl-dos/