Created
July 25, 2019 20:42
-
-
Save menduz/7f89b54240d3569b64366f3919cdddb1 to your computer and use it in GitHub Desktop.
DetectNAT.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export enum NatType { | |
Other = 0, | |
SymmetricNAT = 1, | |
NoNAT = -1 // No NAT (Open Internet, Blocked, Symmetric UDP Firewall) | |
} | |
export async function detectNat(): Promise<NatType> { | |
const candidates = new Map<number, number[]>(); | |
const pc = new RTCPeerConnection(peerConnectionConfig); | |
const result = future<NatType>(); | |
const channel = pc.createDataChannel("foo"); | |
pc.onicecandidate = function(e) { | |
if (e.candidate && e.candidate.candidate.indexOf("srflx") !== -1) { | |
const cand = parseCandidate(e.candidate.candidate); | |
if (cand.relatedPort !== null) { | |
let list = candidates.get(cand.relatedPort); | |
if (!list) { | |
list = []; | |
candidates.set(cand.relatedPort, list); | |
} | |
list.push(cand.port); | |
} | |
} else if (!e.candidate) { | |
if (candidates.size === 1) { | |
const ports = candidates.values().next()!; | |
if (ports.value.length === 1) { | |
result.resolve(NatType.Other); | |
} else { | |
result.resolve(NatType.SymmetricNAT); | |
} | |
} else { | |
console.dir(candidates); | |
result.resolve(NatType.NoNAT); | |
} | |
} | |
}; | |
pc.onicecandidateerror = err => { | |
console.error("ICE Error", err); | |
result.resolve(NatType.NoNAT); | |
}; | |
const offer = await pc.createOffer(); | |
await pc.setLocalDescription(offer); | |
try { | |
await result; | |
} finally { | |
pc.close(); | |
channel.close(); | |
} | |
return result; | |
} | |
// parseCandidate from https://github.com/fippo/sdp | |
export function parseCandidate(line: string) { | |
var parts; | |
// Parse both variants. | |
if (line.indexOf("a=candidate:") === 0) { | |
parts = line.substring(12).split(" "); | |
} else { | |
parts = line.substring(10).split(" "); | |
} | |
var candidate = { | |
foundation: parts[0], | |
component: parseInt(parts[1], 10), | |
protocol: parts[2].toLowerCase(), | |
priority: parseInt(parts[3], 10), | |
ip: parts[4], | |
address: parts[4], // address is an alias for ip. | |
port: parseInt(parts[5], 10), | |
// skip parts[6] == 'typ' | |
type: parts[7], | |
relatedPort: null as null | number | |
}; | |
var candidateAny = candidate as any; | |
for (var i = 8; i < parts.length; i += 2) { | |
switch (parts[i]) { | |
case "raddr": | |
candidateAny.relatedAddress = parts[i + 1]; | |
break; | |
case "rport": | |
candidateAny.relatedPort = parseInt(parts[i + 1], 10); | |
break; | |
case "tcptype": | |
candidateAny.tcpType = parts[i + 1]; | |
break; | |
case "ufrag": | |
candidateAny.ufrag = parts[i + 1]; // for backward compability. | |
candidateAny.usernameFragment = parts[i + 1]; | |
break; | |
default: | |
// extension handling, in particular ufrag | |
candidateAny[parts[i]] = parts[i + 1]; | |
break; | |
} | |
} | |
return candidate as typeof candidate & { | |
ufrag?: string; | |
usernameFragment?: string; | |
relatedAddress?: string; | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment