Closed kyr0 closed 1 year ago
Hey, yep we can try to add a dedicated readme with small code snippets for different languages.
Idk about doing a fetch from the browser, wouldn't CORS be an issue?
If not then maybe we can even start thinking about shipping small code libs that would make fetching the latest hosts/ips list a one-liner.
Can we play code golf? I used to do this:
const all = fetch(
"https://raw.githubusercontent.com/pradt2/always-online-stun/master/valid_hosts.txt"
)
.then((res) => res.text())
.then((text) => text.trim().split("\n"));
const mapped = all.then((ipv4s) => ipv4s.map((ip) => `stun:${ip}`));
However, I found a couple problems:
Now I have this, but I feel it's way overboard, for an example:
/**
* All these urls are known to have CORS header Access-Control-Allow-Origin: *
*/
const KnownUrls = {
geoIpCache:
"https://raw.githubusercontent.com/pradt2/always-online-stun/master/geoip_cache.txt",
geolocation: "https://geolocation-db.com/json/",
};
/**
* Get the latitude/longitude for the browser
*
* @return {Promise<{latitude: number, longitude: number}>}
*/
function getLocation() {
let result;
result = result || fetch(KnownUrls.geolocation).then((res) => res.json());
return result;
}
/**
* @return {Promise<Record<string, [latitude: number, longitude: number]>>}
*/
function getGeoIpCache() {
let result;
result = result || fetch(KnownUrls.geoIpCache).then((res) => res.json());
return result;
}
/**
* @param {[number, number]} pointA
* @param {[number, number]} pointB
*/
function pointDistance(pointA, pointB) {
return Math.sqrt(
Math.pow(pointA[0] - pointB[0], 2) + Math.pow(pointA[1] - pointB[1], 2)
);
}
/**
* Return a list of STUN servers sorted by distance to <latitude, longitude>
*
* @param {number} latitude
* @param {number} longitude
*/
async function getStunServersByDistance(latitude, longitude) {
const geoIpCache = await getGeoIpCache();
const geoIpCacheArray = Object.entries(geoIpCache).map(
([ip, [latitude, longitude]]) => ({
ip,
latitude,
longitude,
})
);
// Sort the geoIpCacheArray by distance to myLocation
const geoIpCacheArraySorted = geoIpCacheArray.sort((a, b) => {
const distanceA = pointDistance(
[latitude, longitude],
[a.latitude, a.longitude]
);
const distanceB = pointDistance(
[latitude, longitude],
[b.latitude, b.longitude]
);
return distanceA - distanceB;
});
return geoIpCacheArraySorted;
}
/**
* @param {string} ip
*/
function isIpv4(ip) {
return ip.split(".").length === 4;
}
async function getIpv4sByDistance(latitude, longitude) {
return getStunServersByDistance(latitude, longitude).then((stunServers) =>
stunServers.filter((stunServer) => isIpv4(stunServer.ip))
);
}
async function getIpv6sByDistance(latitude, longitude) {
return getStunServersByDistance(latitude, longitude).then((stunServers) =>
stunServers.filter((stunServer) => !isIpv4(stunServer.ip))
);
}
async function getTopClosestIpv4s(n = 5) {
const location = await getLocation();
return getIpv4sByDistance(location.latitude, location.longitude).then(
(ipv4s) => ipv4s.slice(0, n)
);
}
I think we could get this optimized, cached, sorted, and still under 20 LOC with input from others. Let me know what y'all think.
Hi @tarwich, seems like you're trying to find the stun server closest to the caller. We already have a discussion about this in #4. I'll post my code there
Hey :)
as up-to-date STUN servers lists are often needed to be known in-browser, and you might not want to hard-code them, you can actually use this code in-browser to fetch them fresh from GitHub, and Microsoft will pay for the traffic + takes care for DDoS protection: