Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NAT checker in client.js #366

Open
zackees opened this issue Jul 14, 2022 · 2 comments
Open

Add NAT checker in client.js #366

zackees opened this issue Jul 14, 2022 · 2 comments

Comments

@zackees
Copy link
Contributor

zackees commented Jul 14, 2022

Users behind a "Symetric NAT" will fail to use instant.io to seed content (and possibly download).

Here is an implementation on how to check the getNATtype(cb) will invoke the callback with either "Permissive NAT" or "Symmetric NAT"

NATtype.js

/* global RTCPeerConnection */

const ICE_SERVERS = [
  { urls: 'stun:stun1.l.google.com:19302' },
  { urls: 'stun:stun2.l.google.com:19302' }
]

function parseCandidate (line) {
  let parts
  // Parse both variants.
  if (line.indexOf('a=candidate:') === 0) {
    parts = line.substring(12).split(' ')
  } else {
    parts = line.substring(10).split(' ')
  }

  const candidate = {
    foundation: parts[0],
    component: parts[1],
    protocol: parts[2].toLowerCase(),
    priority: parseInt(parts[3], 10),
    ip: parts[4],
    port: parseInt(parts[5], 10),
    // skip parts[6] == 'typ'
    type: parts[7]
  }

  for (let i = 8; i < parts.length; i += 2) {
    switch (parts[i]) {
      case 'raddr':
        candidate.relatedAddress = parts[i + 1]
        break
      case 'rport':
        candidate.relatedPort = parseInt(parts[i + 1], 10)
        break
      case 'tcptype':
        candidate.tcpType = parts[i + 1]
        break
      default: // Unknown extensions are silently ignored.
        break
    }
  }
  return candidate
};

exports.getNATtype = function getNATtype (cb) {
  cb = cb || console.log
  const candidates = {}
  const rtcOptions = { iceServers: ICE_SERVERS }
  const pc = new RTCPeerConnection(rtcOptions)
  pc.createDataChannel('foo')
  pc.onicecandidate = function (e) {
    if (e.candidate && e.candidate.candidate.indexOf('srflx') !== -1) {
      const cand = parseCandidate(e.candidate.candidate)
      if (!candidates[cand.relatedPort]) candidates[cand.relatedPort] = []
      candidates[cand.relatedPort].push(cand.port)
    } else if (!e.candidate) {
      if (Object.keys(candidates).length === 1) {
        const ports = candidates[Object.keys(candidates)[0]]
        if (ports.length === 1) {
          cb('Permissive NAT')  // eslint-disable-line
        } else {
          cb('Symmetric NAT')  // eslint-disable-line
        }
      }
    }
  }
  pc.createOffer()
    .then(offer => pc.setLocalDescription(offer))
}
@drobin04
Copy link

I think I've just run into this issue; I live in Mexico and most of us are on Carrier Grade NAT (CGNAT), which is also becoming more common with newer ISP's in the US (Starlink, T-Mobile's home internet, etc).
I'm able to use the site to move items between devices in my own network, but not crossing outside of the network.
Ran into this issue after setting up a webpage with similar code, testing it, and realizing it didn't work outside the network... Then realizing I get the same error(s) on instant.IO... Haha.
Still fascinating code though. Would be interesting to see if there's a way around this.

@zackees
Copy link
Contributor Author

zackees commented Nov 12, 2022

I've had an idea where certain users with permissive nats become peer relay points.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants