mirror of
https://github.com/schlagmichdoch/PairDrop.git
synced 2025-04-20 07:05:05 -04:00
Modularize server into multiple files
This commit is contained in:
parent
d9270a5560
commit
cb72edef20
8 changed files with 923 additions and 901 deletions
200
server/peer.js
Normal file
200
server/peer.js
Normal file
|
@ -0,0 +1,200 @@
|
|||
import crypto from "crypto";
|
||||
import parser from "ua-parser-js";
|
||||
import {animals, colors, uniqueNamesGenerator} from "unique-names-generator";
|
||||
import {cyrb53, hasher} from "./helper.js";
|
||||
|
||||
export default class Peer {
|
||||
|
||||
constructor(socket, request, conf) {
|
||||
this.conf = conf
|
||||
|
||||
// set socket
|
||||
this.socket = socket;
|
||||
|
||||
// set remote ip
|
||||
this._setIP(request);
|
||||
|
||||
// set peer id
|
||||
this._setPeerId(request);
|
||||
|
||||
// is WebRTC supported ?
|
||||
this.rtcSupported = request.url.indexOf('webrtc') > -1;
|
||||
|
||||
// set name
|
||||
this._setName(request);
|
||||
|
||||
this.requestRate = 0;
|
||||
|
||||
this.roomSecrets = [];
|
||||
this.pairKey = null;
|
||||
|
||||
this.publicRoomId = null;
|
||||
}
|
||||
|
||||
rateLimitReached() {
|
||||
// rate limit implementation: max 10 attempts every 10s
|
||||
if (this.requestRate >= 10) {
|
||||
return true;
|
||||
}
|
||||
this.requestRate += 1;
|
||||
setTimeout(_ => this.requestRate -= 1, 10000);
|
||||
return false;
|
||||
}
|
||||
|
||||
_setIP(request) {
|
||||
if (request.headers['cf-connecting-ip']) {
|
||||
this.ip = request.headers['cf-connecting-ip'].split(/\s*,\s*/)[0];
|
||||
} else if (request.headers['x-forwarded-for']) {
|
||||
this.ip = request.headers['x-forwarded-for'].split(/\s*,\s*/)[0];
|
||||
} else {
|
||||
this.ip = request.connection.remoteAddress;
|
||||
}
|
||||
|
||||
// remove the prefix used for IPv4-translated addresses
|
||||
if (this.ip.substring(0,7) === "::ffff:")
|
||||
this.ip = this.ip.substring(7);
|
||||
|
||||
let ipv6_was_localized = false;
|
||||
if (this.conf.ipv6Localize && this.ip.includes(':')) {
|
||||
this.ip = this.ip.split(':',this.conf.ipv6Localize).join(':');
|
||||
ipv6_was_localized = true;
|
||||
}
|
||||
|
||||
if (this.conf.debugMode) {
|
||||
console.debug("\n");
|
||||
console.debug("----DEBUGGING-PEER-IP-START----");
|
||||
console.debug("remoteAddress:", request.connection.remoteAddress);
|
||||
console.debug("x-forwarded-for:", request.headers['x-forwarded-for']);
|
||||
console.debug("cf-connecting-ip:", request.headers['cf-connecting-ip']);
|
||||
if (ipv6_was_localized) {
|
||||
console.debug("IPv6 client IP was localized to", this.conf.ipv6Localize, this.conf.ipv6Localize > 1 ? "segments" : "segment");
|
||||
}
|
||||
console.debug("PairDrop uses:", this.ip);
|
||||
console.debug("IP is private:", this.ipIsPrivate(this.ip));
|
||||
console.debug("if IP is private, '127.0.0.1' is used instead");
|
||||
console.debug("----DEBUGGING-PEER-IP-END----");
|
||||
}
|
||||
|
||||
// IPv4 and IPv6 use different values to refer to localhost
|
||||
// put all peers on the same network as the server into the same room as well
|
||||
if (this.ip === '::1' || this.ipIsPrivate(this.ip)) {
|
||||
this.ip = '127.0.0.1';
|
||||
}
|
||||
}
|
||||
|
||||
ipIsPrivate(ip) {
|
||||
// if ip is IPv4
|
||||
if (!ip.includes(":")) {
|
||||
// 10.0.0.0 - 10.255.255.255 || 172.16.0.0 - 172.31.255.255 || 192.168.0.0 - 192.168.255.255
|
||||
return /^(10)\.(.*)\.(.*)\.(.*)$/.test(ip) || /^(172)\.(1[6-9]|2[0-9]|3[0-1])\.(.*)\.(.*)$/.test(ip) || /^(192)\.(168)\.(.*)\.(.*)$/.test(ip)
|
||||
}
|
||||
|
||||
// else: ip is IPv6
|
||||
const firstWord = ip.split(":").find(el => !!el); //get first not empty word
|
||||
|
||||
if (/^fe[c-f][0-f]$/.test(firstWord)) {
|
||||
// The original IPv6 Site Local addresses (fec0::/10) are deprecated. Range: fec0 - feff
|
||||
return true;
|
||||
}
|
||||
|
||||
// These days Unique Local Addresses (ULA) are used in place of Site Local.
|
||||
// Range: fc00 - fcff
|
||||
else if (/^fc[0-f]{2}$/.test(firstWord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Range: fd00 - fcff
|
||||
else if (/^fd[0-f]{2}$/.test(firstWord)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Link local addresses (prefixed with fe80) are not routable
|
||||
else if (firstWord === "fe80") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Discard Prefix
|
||||
else if (firstWord === "100") {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Any other IP address is not Unique Local Address (ULA)
|
||||
return false;
|
||||
}
|
||||
|
||||
_setPeerId(request) {
|
||||
const searchParams = new URL(request.url, "http://server").searchParams;
|
||||
let peerId = searchParams.get("peer_id");
|
||||
let peerIdHash = searchParams.get("peer_id_hash");
|
||||
if (peerId && Peer.isValidUuid(peerId) && this.isPeerIdHashValid(peerId, peerIdHash)) {
|
||||
this.id = peerId;
|
||||
} else {
|
||||
this.id = crypto.randomUUID();
|
||||
}
|
||||
}
|
||||
|
||||
_setName(req) {
|
||||
let ua = parser(req.headers['user-agent']);
|
||||
|
||||
let deviceName = '';
|
||||
|
||||
if (ua.os && ua.os.name) {
|
||||
deviceName = ua.os.name.replace('Mac OS', 'Mac') + ' ';
|
||||
}
|
||||
|
||||
if (ua.device.model) {
|
||||
deviceName += ua.device.model;
|
||||
} else {
|
||||
deviceName += ua.browser.name;
|
||||
}
|
||||
|
||||
if (!deviceName) {
|
||||
deviceName = 'Unknown Device';
|
||||
}
|
||||
|
||||
const displayName = uniqueNamesGenerator({
|
||||
length: 2,
|
||||
separator: ' ',
|
||||
dictionaries: [colors, animals],
|
||||
style: 'capital',
|
||||
seed: cyrb53(this.id)
|
||||
})
|
||||
|
||||
this.name = {
|
||||
model: ua.device.model,
|
||||
os: ua.os.name,
|
||||
browser: ua.browser.name,
|
||||
type: ua.device.type,
|
||||
deviceName,
|
||||
displayName
|
||||
};
|
||||
}
|
||||
|
||||
getInfo() {
|
||||
return {
|
||||
id: this.id,
|
||||
name: this.name,
|
||||
rtcSupported: this.rtcSupported
|
||||
}
|
||||
}
|
||||
|
||||
static isValidUuid(uuid) {
|
||||
return /^([0-9]|[a-f]){8}-(([0-9]|[a-f]){4}-){3}([0-9]|[a-f]){12}$/.test(uuid);
|
||||
}
|
||||
|
||||
isPeerIdHashValid(peerId, peerIdHash) {
|
||||
return peerIdHash === hasher.hashCodeSalted(peerId);
|
||||
}
|
||||
|
||||
addRoomSecret(roomSecret) {
|
||||
if (!(roomSecret in this.roomSecrets)) {
|
||||
this.roomSecrets.push(roomSecret);
|
||||
}
|
||||
}
|
||||
|
||||
removeRoomSecret(roomSecret) {
|
||||
if (roomSecret in this.roomSecrets) {
|
||||
delete this.roomSecrets[roomSecret];
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue