Skip to content

Instantly share code, notes, and snippets.

@menduz
Created July 25, 2019 20:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save menduz/7f89b54240d3569b64366f3919cdddb1 to your computer and use it in GitHub Desktop.
Save menduz/7f89b54240d3569b64366f3919cdddb1 to your computer and use it in GitHub Desktop.
DetectNAT.ts
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