]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blobdiff - server/controllers/tracker.ts
Use different p2p policy for embeds and webapp
[github/Chocobozzz/PeerTube.git] / server / controllers / tracker.ts
index 1deb8c40292f6ae997eb77004b858c1165481e56..6d60639b880e66da7a213abe58b0273881d46736 100644 (file)
@@ -1,14 +1,14 @@
+import { Server as TrackerServer } from 'bittorrent-tracker'
+import express from 'express'
+import { createServer } from 'http'
+import proxyAddr from 'proxy-addr'
+import { WebSocketServer } from 'ws'
+import { Redis } from '@server/lib/redis'
 import { logger } from '../helpers/logger'
-import * as express from 'express'
-import * as http from 'http'
-import * as bitTorrentTracker from 'bittorrent-tracker'
-import * as proxyAddr from 'proxy-addr'
-import { Server as WebSocketServer } from 'ws'
-import { CONFIG, TRACKER_RATE_LIMITS } from '../initializers/constants'
+import { CONFIG } from '../initializers/config'
+import { TRACKER_RATE_LIMITS } from '../initializers/constants'
 import { VideoFileModel } from '../models/video/video-file'
-import { parse } from 'url'
-
-const TrackerServer = bitTorrentTracker.Server
+import { VideoStreamingPlaylistModel } from '../models/video/video-streaming-playlist'
 
 const trackerRouter = express.Router()
 
@@ -20,12 +20,15 @@ const trackerServer = new TrackerServer({
   http: false,
   udp: false,
   ws: false,
-  dht: false,
-  filter: function (infoHash, params, cb) {
+  filter: async function (infoHash, params, cb) {
+    if (CONFIG.TRACKER.ENABLED === false) {
+      return cb(new Error('Tracker is disabled on this instance.'))
+    }
+
     let ip: string
 
     if (params.type === 'ws') {
-      ip = params.socket.ip
+      ip = params.ip
     } else {
       ip = params.httpReq.ip
     }
@@ -35,33 +38,53 @@ const trackerServer = new TrackerServer({
     peersIps[ip] = peersIps[ip] ? peersIps[ip] + 1 : 1
     peersIpInfoHash[key] = peersIpInfoHash[key] ? peersIpInfoHash[key] + 1 : 1
 
-    if (peersIpInfoHash[key] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) {
-      return cb(new Error(`Too many requests (${peersIpInfoHash[ key ]} of ip ${ip} for torrent ${infoHash}`))
+    if (CONFIG.TRACKER.REJECT_TOO_MANY_ANNOUNCES && peersIpInfoHash[key] > TRACKER_RATE_LIMITS.ANNOUNCES_PER_IP_PER_INFOHASH) {
+      return cb(new Error(`Too many requests (${peersIpInfoHash[key]} of ip ${ip} for torrent ${infoHash}`))
     }
 
-    VideoFileModel.isInfohashExists(infoHash)
-      .then(exists => {
-        if (exists === false) return cb(new Error(`Unknown infoHash ${infoHash}`))
+    try {
+      if (CONFIG.TRACKER.PRIVATE === false) return cb()
+
+      const videoFileExists = await VideoFileModel.doesInfohashExistCached(infoHash)
+      if (videoFileExists === true) return cb()
+
+      const playlistExists = await VideoStreamingPlaylistModel.doesInfohashExistCached(infoHash)
+      if (playlistExists === true) return cb()
 
-        return cb()
-      })
+      cb(new Error(`Unknown infoHash ${infoHash} requested by ip ${ip}`))
+
+      // Close socket connection and block IP for a few time
+      if (params.type === 'ws') {
+        Redis.Instance.setTrackerBlockIP(ip)
+          .catch(err => logger.error('Cannot set tracker block ip.', { err }))
+
+        // setTimeout to wait filter response
+        setTimeout(() => params.socket.close(), 0)
+      }
+    } catch (err) {
+      logger.error('Error in tracker filter.', { err })
+      return cb(err)
+    }
   }
 })
 
-trackerServer.on('error', function (err) {
-  logger.error('Error in tracker.', { err })
-})
+if (CONFIG.TRACKER.ENABLED !== false) {
 
-trackerServer.on('warning', function (err) {
-  logger.warn('Warning in tracker.', { err })
-})
+  trackerServer.on('error', function (err) {
+    logger.error('Error in tracker.', { err })
+  })
+
+  trackerServer.on('warning', function (err) {
+    logger.warn('Warning in tracker.', { err })
+  })
+}
 
 const onHttpRequest = trackerServer.onHttpRequest.bind(trackerServer)
 trackerRouter.get('/tracker/announce', (req, res) => onHttpRequest(req, res, { action: 'announce' }))
 trackerRouter.get('/tracker/scrape', (req, res) => onHttpRequest(req, res, { action: 'scrape' }))
 
 function createWebsocketTrackerServer (app: express.Application) {
-  const server = http.createServer(app)
+  const server = createServer(app)
   const wss = new WebSocketServer({ noServer: true })
 
   wss.on('connection', function (ws, req) {
@@ -70,11 +93,24 @@ function createWebsocketTrackerServer (app: express.Application) {
     trackerServer.onWebSocketConnection(ws)
   })
 
-  server.on('upgrade', (request, socket, head) => {
-    const pathname = parse(request.url).pathname
+  server.on('upgrade', (request: express.Request, socket, head) => {
+    if (request.url === '/tracker/socket') {
+      const ip = proxyAddr(request, CONFIG.TRUST_PROXY)
+
+      Redis.Instance.doesTrackerBlockIPExist(ip)
+        .then(result => {
+          if (result === true) {
+            logger.debug('Blocking IP %s from tracker.', ip)
+
+            socket.write('HTTP/1.1 403 Forbidden\r\n\r\n')
+            socket.destroy()
+            return
+          }
 
-    if (pathname === '/tracker/socket') {
-      wss.handleUpgrade(request, socket, head, ws => wss.emit('connection', ws, request))
+          // FIXME: typings
+          return wss.handleUpgrade(request, socket as any, head, ws => wss.emit('connection', ws, request))
+        })
+        .catch(err => logger.error('Cannot check if tracker block ip exists.', { err }))
     }
 
     // Don't destroy socket, we have Socket.IO too