Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/stale.yml

This file was deleted.

23 changes: 23 additions & 0 deletions .github/workflows/stale.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Mark stale issues and pull requests

on:
schedule:
- cron: '0 12 * * *'

jobs:
stale:
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write

steps:
- uses: actions/stale@v4
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?'
stale-pr-message: 'Is this still relevant? If so, what is blocking it? Is there anything you can do to help move it forward?'
exempt-issue-labels: accepted,blocked,bug,dependency,enhancement,'help wanted',question,security,meta
exempt-pr-labels: accepted,blocked,bug,dependency,enhancement,'help wanted',question,security,meta
stale-issue-label: 'stale'
stale-pr-label: 'stale'
2 changes: 2 additions & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,7 @@
- Diego Rodríguez Baquero ([email protected])
- Diego Rodriguez Baquero ([email protected])
- semantic-release-bot ([email protected])
- renovate[bot] (29139614+renovate[bot]@users.noreply.github.com)
- Jocelyn Liu ([email protected])

#### Generated by tools/update-authors.sh.
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,31 @@
## [9.18.2](https://github.com/webtorrent/bittorrent-tracker/compare/v9.18.1...v9.18.2) (2021-09-02)


### Bug Fixes

* **deps:** update dependency socks to v2 ([#394](https://github.com/webtorrent/bittorrent-tracker/issues/394)) ([353e1f4](https://github.com/webtorrent/bittorrent-tracker/commit/353e1f40093a5e74cb54219abbae8ef0cc3d9e0b))

## [9.18.1](https://github.com/webtorrent/bittorrent-tracker/compare/v9.18.0...v9.18.1) (2021-09-01)


### Bug Fixes

* disable socks in chromeapp ([#398](https://github.com/webtorrent/bittorrent-tracker/issues/398)) ([7fd5877](https://github.com/webtorrent/bittorrent-tracker/commit/7fd587789548453a852ea01e54900a5e9155db67))

# [9.18.0](https://github.com/webtorrent/bittorrent-tracker/compare/v9.17.4...v9.18.0) (2021-08-20)


### Features

* add proxy support for tracker clients ([#356](https://github.com/webtorrent/bittorrent-tracker/issues/356)) ([ad64dc3](https://github.com/webtorrent/bittorrent-tracker/commit/ad64dc3a68cddccc2c1f05d0d8bb833f2c4860b2))

## [9.17.4](https://github.com/webtorrent/bittorrent-tracker/compare/v9.17.3...v9.17.4) (2021-07-22)


### Bug Fixes

* if websocket closed, don't produce a response ([ca88435](https://github.com/webtorrent/bittorrent-tracker/commit/ca88435617e59714a456031c75b3a329897d97bd))

## [9.17.3](https://github.com/webtorrent/bittorrent-tracker/compare/v9.17.2...v9.17.3) (2021-07-02)


Expand Down
50 changes: 44 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,12 @@ var requiredOpts = {
}

var optionalOpts = {
// RTCPeerConnection config object (only used in browser)
rtcConfig: {},
// User-Agent header for http requests
userAgent: '',
// Custom webrtc impl, useful in node to specify [wrtc](https://npmjs.com/package/wrtc)
wrtc: {},
getAnnounceOpts: function () {
// Provide a callback that will be called whenever announce() is called
// internally (on timer), or by the user
Expand All @@ -75,12 +81,44 @@ var optionalOpts = {
customParam: 'blah' // custom parameters supported
}
},
// RTCPeerConnection config object (only used in browser)
rtcConfig: {},
// User-Agent header for http requests
userAgent: '',
// Custom webrtc impl, useful in node to specify [wrtc](https://npmjs.com/package/wrtc)
wrtc: {},
// Proxy config object
proxyOpts: {
// Socks proxy options (used to proxy requests in node)
socksProxy: {
// Configuration from socks module (https://github.com/JoshGlazebrook/socks)
proxy: {
// IP Address of Proxy (Required)
ipaddress: "1.2.3.4",
// TCP Port of Proxy (Required)
port: 1080,
// Proxy Type [4, 5] (Required)
// Note: 4 works for both 4 and 4a.
// Type 4 does not support UDP association relay
type: 5,

// SOCKS 4 Specific:

// UserId used when making a SOCKS 4/4a request. (Optional)
userid: "someuserid",

// SOCKS 5 Specific:

// Authentication used for SOCKS 5 (when it's required) (Optional)
authentication: {
username: "Josh",
password: "somepassword"
}
},

// Amount of time to wait for a connection to be established. (Optional)
// - defaults to 10000ms (10 seconds)
timeout: 10000
},
// NodeJS HTTP agents (used to proxy HTTP and Websocket requests in node)
// Populated with Socks.Agent if socksProxy is provided
httpAgent: {},
httpsAgent: {}
},
}

var client = new Client(requiredOpts)
Expand Down
2 changes: 2 additions & 0 deletions client.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const WebSocketTracker = require('./lib/client/websocket-tracker')
* @param {number} opts.rtcConfig RTCPeerConnection configuration object
* @param {number} opts.userAgent User-Agent header for http requests
* @param {number} opts.wrtc custom webrtc impl (useful in node.js)
* @param {object} opts.proxyOpts proxy options (useful in node.js)
*/
class Client extends EventEmitter {
constructor (opts = {}) {
Expand Down Expand Up @@ -54,6 +55,7 @@ class Client extends EventEmitter {
this._getAnnounceOpts = opts.getAnnounceOpts
this._rtcConfig = opts.rtcConfig
this._userAgent = opts.userAgent
this._proxyOpts = opts.proxyOpts

// Support lazy 'wrtc' module initialization
// See: https://github.com/webtorrent/webtorrent-hybrid/issues/46
Expand Down
15 changes: 12 additions & 3 deletions lib/client/http-tracker.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const arrayRemove = require('unordered-array-remove')
const bencode = require('bencode')
const clone = require('clone')
const compact2string = require('compact2string')
const debug = require('debug')('bittorrent-tracker:http-tracker')
const get = require('simple-get')
const Socks = require('socks')

const common = require('../common')
const Tracker = require('./tracker')
Expand Down Expand Up @@ -110,13 +112,20 @@ class HTTPTracker extends Tracker {

_request (requestUrl, params, cb) {
const self = this
const u = requestUrl + (!requestUrl.includes('?') ? '?' : '&') +
common.querystringStringify(params)
const parsedUrl = new URL(requestUrl + (requestUrl.indexOf('?') === -1 ? '?' : '&') + common.querystringStringify(params))
let agent
if (this.client._proxyOpts) {
agent = parsedUrl.protocol === 'https:' ? this.client._proxyOpts.httpsAgent : this.client._proxyOpts.httpAgent
if (!agent && this.client._proxyOpts.socksProxy) {
agent = new Socks.Agent(clone(this.client._proxyOpts.socksProxy), (parsedUrl.protocol === 'https:'))
}
}

this.cleanupFns.push(cleanup)

let request = get.concat({
url: u,
url: parsedUrl.toString(),
agent: agent,
timeout: common.REQUEST_TIMEOUT,
headers: {
'user-agent': this.client._userAgent || ''
Expand Down
88 changes: 69 additions & 19 deletions lib/client/udp-tracker.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const arrayRemove = require('unordered-array-remove')
const BN = require('bn.js')
const clone = require('clone')
const compact2string = require('compact2string')
const debug = require('debug')('bittorrent-tracker:udp-tracker')
const dgram = require('dgram')
const randombytes = require('randombytes')
const Socks = require('socks')

const common = require('../common')
const Tracker = require('./tracker')
Expand Down Expand Up @@ -77,27 +79,65 @@ class UDPTracker extends Tracker {
let { hostname, port } = common.parseUrl(this.announceUrl)
if (port === '') port = 80

let timeout
// Socket used to connect to the socks server to create a relay, null if socks is disabled
let proxySocket
// Socket used to connect to the tracker or to the socks relay if socks is enabled
let socket
// Contains the host/port of the socks relay
let relay

let transactionId = genTransactionId()
let socket = dgram.createSocket('udp4')

let timeout = setTimeout(() => {
// does not matter if `stopped` event arrives, so supress errors
if (opts.event === 'stopped') cleanup()
else onError(new Error(`tracker request timed out (${opts.event})`))
timeout = null
}, common.REQUEST_TIMEOUT)
if (timeout.unref) timeout.unref()
const proxyOpts = this.client._proxyOpts && clone(this.client._proxyOpts.socksProxy)
if (proxyOpts) {
if (!proxyOpts.proxy) proxyOpts.proxy = {}
// UDP requests uses the associate command
proxyOpts.proxy.command = 'associate'
if (!proxyOpts.target) {
// This should contain client IP and port but can be set to 0 if we don't have this information
proxyOpts.target = {
host: '0.0.0.0',
port: 0
}
}

if (proxyOpts.proxy.type === 5) {
Socks.createConnection(proxyOpts, onGotConnection)
} else {
debug('Ignoring Socks proxy for UDP request because type 5 is required')
onGotConnection(null)
}
} else {
onGotConnection(null)
}

this.cleanupFns.push(cleanup)

send(Buffer.concat([
common.CONNECTION_ID,
common.toUInt32(common.ACTIONS.CONNECT),
transactionId
]))
function onGotConnection (err, s, info) {
if (err) return onError(err)

socket.once('error', onError)
socket.on('message', onSocketMessage)
proxySocket = s
socket = dgram.createSocket('udp4')
relay = info

timeout = setTimeout(() => {
// does not matter if `stopped` event arrives, so supress errors
if (opts.event === 'stopped') cleanup()
else onError(new Error(`tracker request timed out (${opts.event})`))
timeout = null
}, common.REQUEST_TIMEOUT)
if (timeout.unref) timeout.unref()

send(Buffer.concat([
common.CONNECTION_ID,
common.toUInt32(common.ACTIONS.CONNECT),
transactionId
]), relay)

socket.once('error', onError)
socket.on('message', onSocketMessage)
}

function cleanup () {
if (timeout) {
Expand All @@ -111,6 +151,10 @@ class UDPTracker extends Tracker {
socket.on('error', noop) // ignore all future errors
try { socket.close() } catch (err) {}
socket = null
if (proxySocket) {
try { proxySocket.close() } catch (err) {}
proxySocket = null
}
}
if (self.maybeDestroyCleanup) self.maybeDestroyCleanup()
}
Expand All @@ -128,6 +172,7 @@ class UDPTracker extends Tracker {
}

function onSocketMessage (msg) {
if (proxySocket) msg = msg.slice(10)
if (msg.length < 8 || msg.readUInt32BE(4) !== transactionId.readUInt32BE(0)) {
return onError(new Error('tracker sent invalid transaction id'))
}
Expand Down Expand Up @@ -211,8 +256,13 @@ class UDPTracker extends Tracker {
}
}

function send (message) {
socket.send(message, 0, message.length, port, hostname)
function send (message, proxyInfo) {
if (proxyInfo) {
const pack = Socks.createUDPFrame({ host: hostname, port: port }, message)
socket.send(pack, 0, pack.length, proxyInfo.port, proxyInfo.host)
} else {
socket.send(message, 0, message.length, port, hostname)
}
}

function announce (connectionId, opts) {
Expand All @@ -232,7 +282,7 @@ class UDPTracker extends Tracker {
common.toUInt32(0), // key (optional)
common.toUInt32(opts.numwant),
toUInt16(self.client._port)
]))
]), relay)
}

function scrape (connectionId) {
Expand All @@ -247,7 +297,7 @@ class UDPTracker extends Tracker {
common.toUInt32(common.ACTIONS.SCRAPE),
transactionId,
infoHash
]))
]), relay)
}
}
}
Expand Down
12 changes: 11 additions & 1 deletion lib/client/websocket-tracker.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const clone = require('clone')
const debug = require('debug')('bittorrent-tracker:websocket-tracker')
const Peer = require('simple-peer')
const randombytes = require('randombytes')
const Socket = require('simple-websocket')
const Socks = require('socks')

const common = require('../common')
const Tracker = require('./tracker')
Expand Down Expand Up @@ -176,7 +178,15 @@ class WebSocketTracker extends Tracker {
this._onSocketConnectBound()
}
} else {
this.socket = socketPool[this.announceUrl] = new Socket(this.announceUrl)
const parsedUrl = new URL(this.announceUrl)
let agent
if (this.client._proxyOpts) {
agent = parsedUrl.protocol === 'wss:' ? this.client._proxyOpts.httpsAgent : this.client._proxyOpts.httpAgent
if (!agent && this.client._proxyOpts.socksProxy) {
agent = new Socks.Agent(clone(this.client._proxyOpts.socksProxy), (parsedUrl.protocol === 'wss:'))
}
}
this.socket = socketPool[this.announceUrl] = new Socket({ url: this.announceUrl, agent: agent })
this.socket.consumers = 1
this.socket.once('connect', this._onSocketConnectBound)
}
Expand Down
1 change: 1 addition & 0 deletions lib/server/swarm.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ class Swarm {
self._onAnnounceStarted(params, peer, id)
} else if (params.event === 'stopped') {
self._onAnnounceStopped(params, peer, id)
if (!cb) return // when websocket is closed
} else if (params.event === 'completed') {
self._onAnnounceCompleted(params, peer, id)
} else if (params.event === 'update') {
Expand Down
Loading