Use proxy agents for CLNRest over Tor (#1136)
This commit is contained in:
parent
ed9fc5d3de
commit
6220eb06ee
21
lib/cln.js
21
lib/cln.js
|
@ -1,10 +1,27 @@
|
|||
import fetch from 'cross-fetch'
|
||||
import https from 'https'
|
||||
import crypto from 'crypto'
|
||||
import { HttpProxyAgent, HttpsProxyAgent } from '@/lib/proxy'
|
||||
|
||||
export const createInvoice = async ({ socket, rune, cert, label, description, msats, expiry }) => {
|
||||
const agent = cert ? new https.Agent({ ca: Buffer.from(cert, 'base64') }) : undefined
|
||||
const url = 'https://' + socket + '/v1/invoice'
|
||||
let protocol, agent
|
||||
const httpsAgentOptions = { ca: cert ? Buffer.from(cert, 'base64') : undefined }
|
||||
const isOnion = /\.onion(:[0-9]+)?$/.test(socket)
|
||||
if (isOnion) {
|
||||
// we support HTTP and HTTPS over Tor
|
||||
protocol = cert ? 'https:' : 'http:'
|
||||
// we need to use our Tor proxy to resolve onion addresses
|
||||
const proxyOptions = { proxy: 'http://127.0.0.1:7050/' }
|
||||
agent = protocol === 'https:'
|
||||
? new HttpsProxyAgent({ ...proxyOptions, ...httpsAgentOptions })
|
||||
: new HttpProxyAgent(proxyOptions)
|
||||
} else {
|
||||
// we only support HTTPS over clearnet
|
||||
agent = new https.Agent(httpsAgentOptions)
|
||||
protocol = 'https:'
|
||||
}
|
||||
|
||||
const url = `${protocol}//${socket}/v1/invoice`
|
||||
const res = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
import http from 'http'
|
||||
import https from 'https'
|
||||
|
||||
// from https://github.com/delvedor/hpagent
|
||||
|
||||
export class HttpProxyAgent extends http.Agent {
|
||||
constructor (options) {
|
||||
const { proxy, proxyRequestOptions, ...opts } = options
|
||||
super(opts)
|
||||
this.proxy = typeof proxy === 'string'
|
||||
? new URL(proxy)
|
||||
: proxy
|
||||
this.proxyRequestOptions = proxyRequestOptions || {}
|
||||
}
|
||||
|
||||
createConnection (options, callback) {
|
||||
const requestOptions = {
|
||||
...this.proxyRequestOptions,
|
||||
method: 'CONNECT',
|
||||
host: this.proxy.hostname,
|
||||
port: this.proxy.port,
|
||||
path: `${options.host}:${options.port}`,
|
||||
setHost: false,
|
||||
headers: { ...this.proxyRequestOptions.headers, connection: this.keepAlive ? 'keep-alive' : 'close', host: `${options.host}:${options.port}` },
|
||||
agent: false,
|
||||
timeout: options.timeout || 0
|
||||
}
|
||||
|
||||
if (this.proxy.username || this.proxy.password) {
|
||||
const base64 = Buffer.from(`${decodeURIComponent(this.proxy.username || '')}:${decodeURIComponent(this.proxy.password || '')}`).toString('base64')
|
||||
requestOptions.headers['proxy-authorization'] = `Basic ${base64}`
|
||||
}
|
||||
|
||||
if (this.proxy.protocol === 'https:') {
|
||||
requestOptions.servername = this.proxy.hostname
|
||||
}
|
||||
|
||||
const request = (this.proxy.protocol === 'http:' ? http : https).request(requestOptions)
|
||||
request.once('connect', (response, socket, head) => {
|
||||
request.removeAllListeners()
|
||||
socket.removeAllListeners()
|
||||
if (response.statusCode === 200) {
|
||||
callback(null, socket)
|
||||
} else {
|
||||
socket.destroy()
|
||||
callback(new Error(`Bad response: ${response.statusCode}`), null)
|
||||
}
|
||||
})
|
||||
|
||||
request.once('timeout', () => {
|
||||
request.destroy(new Error('Proxy timeout'))
|
||||
})
|
||||
|
||||
request.once('error', err => {
|
||||
request.removeAllListeners()
|
||||
callback(err, null)
|
||||
})
|
||||
|
||||
request.end()
|
||||
}
|
||||
}
|
||||
|
||||
export class HttpsProxyAgent extends https.Agent {
|
||||
constructor (options) {
|
||||
const { proxy, proxyRequestOptions, ...opts } = options
|
||||
super(opts)
|
||||
this.proxy = typeof proxy === 'string'
|
||||
? new URL(proxy)
|
||||
: proxy
|
||||
this.proxyRequestOptions = proxyRequestOptions || {}
|
||||
}
|
||||
|
||||
createConnection (options, callback) {
|
||||
const requestOptions = {
|
||||
...this.proxyRequestOptions,
|
||||
method: 'CONNECT',
|
||||
host: this.proxy.hostname,
|
||||
port: this.proxy.port,
|
||||
path: `${options.host}:${options.port}`,
|
||||
setHost: false,
|
||||
headers: { ...this.proxyRequestOptions.headers, connection: this.keepAlive ? 'keep-alive' : 'close', host: `${options.host}:${options.port}` },
|
||||
agent: false,
|
||||
timeout: options.timeout || 0
|
||||
}
|
||||
|
||||
if (this.proxy.username || this.proxy.password) {
|
||||
const base64 = Buffer.from(`${decodeURIComponent(this.proxy.username || '')}:${decodeURIComponent(this.proxy.password || '')}`).toString('base64')
|
||||
requestOptions.headers['proxy-authorization'] = `Basic ${base64}`
|
||||
}
|
||||
|
||||
// Necessary for the TLS check with the proxy to succeed.
|
||||
if (this.proxy.protocol === 'https:') {
|
||||
requestOptions.servername = this.proxy.hostname
|
||||
}
|
||||
|
||||
const request = (this.proxy.protocol === 'http:' ? http : https).request(requestOptions)
|
||||
request.once('connect', (response, socket, head) => {
|
||||
request.removeAllListeners()
|
||||
socket.removeAllListeners()
|
||||
if (response.statusCode === 200) {
|
||||
const secureSocket = super.createConnection({ ...options, socket })
|
||||
callback(null, secureSocket)
|
||||
} else {
|
||||
socket.destroy()
|
||||
callback(new Error(`Bad response: ${response.statusCode}`), null)
|
||||
}
|
||||
})
|
||||
|
||||
request.once('timeout', () => {
|
||||
request.destroy(new Error('Proxy timeout'))
|
||||
})
|
||||
|
||||
request.once('error', err => {
|
||||
request.removeAllListeners()
|
||||
callback(err, null)
|
||||
})
|
||||
|
||||
request.end()
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue