RangerMauve / gemini-fetch

Load data from the Gemini protocol the way you would fetch from HTTP in JavaScript
GNU Affero General Public License v3.0
21 stars 4 forks source link

TLS fails with many gemini servers #1

Open bwestergard opened 2 years ago

bwestergard commented 2 years ago

This library seems to fail for a great many gemini severs that work fine with browsers like Lagrange and Amfora.

The gemini://gemini.circumlunar.space capsule is fetched without issue.

bjorn@babylon:~/project/arecibo$ npx gemini-fetch gemini://gemini.circumlunar.space
# Project Gemini

## Overview

Gemini is a new internet protocol which:

But several other sites I've tried consistently fail:

bjorn@babylon:~/project/arecibo$ npx gemini-fetch gemini://flounder.online
Error: Client network socket disconnected before secure TLS connection was established
    at connResetException (internal/errors.js:628:14)
    at TLSSocket.onConnectEnd (_tls_wrap.js:1569:19)
    at TLSSocket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
    at processTicksAndRejections (internal/process/task_queues.js:82:21)

bjorn@babylon:~/project/arecibo$ npx gemini-fetch gemini://bjornwestergard.com
Error: Client network socket disconnected before secure TLS connection was established
    at connResetException (internal/errors.js:628:14)
    at TLSSocket.onConnectEnd (_tls_wrap.js:1569:19)
    at TLSSocket.emit (events.js:412:35)
    at endReadableNT (internal/streams/readable.js:1317:12)
    at processTicksAndRejections (internal/process/task_queues.js:82:21)

Since I control bjornwestergard.com, I can see that the agate process hosting the site logs the following only when receiving requests from this library:

[2022-02-17T23:47:37Z WARN  agate] 172.26.12.95:1965 - "" 00 "TLS error" error:unexpected error: no server certificate chain resolved

This issue in the agate repository suggests to me that this library is perhaps not sending the correct "Server Name Indication".

bwestergard commented 2 years ago

Okay, I believe I have confirmed my hunch. This library does not do the correct Server Name Indication, but the fix is simple: extract the domain and pass it as a tlsOpt. Here is a minimal example you can use to reproduce with any Gemini server that supports SNI.

const request = require('@derhuerst/gemini/client')

request('gemini://bjornwestergard.com',
{
        tlsOpt: {
            rejectUnauthorized: false,
            servername: 'bjornwestergard.com', // Without this line, it fails.
        },
    },
(err, res) => {
    if (err) {
        console.error(err)
        process.exit(1)
    }

    console.log(res.statusCode, res.statusMessage)
    if (res.meta) console.log(res.meta)
    res.pipe(process.stdout)
})