// Where is this server on the internet? STUN answers it: the public address and port a UDP socket
// has, which is what WebRTC candidates need. A server with credentials is answered, one that
// never replies is told apart, two servers show what kind of NAT this is, and RFC 5769's test
// vectors check the integrity code. The last routes run a small STUN server on udp.listen.
//
//   STUN_SERVER=stun.example.net quillo run stun.qwe      then open http://localhost:8080/me

use "stun.qwer"

let SERVER = env("STUN_SERVER", "127.0.0.1")
let OTHER = env("STUN_OTHER", SERVER)
let LOCKED = env("STUN_LOCKED", SERVER)
let SILENT = env("STUN_SILENT", "127.0.0.1:9")
let MINE = int(env("STUN_LISTEN_PORT", "3479"))
let FAST = { timeout_ms: 2000, rto_ms: 100 }

route GET /me {
    let me = stun_binding(SERVER, { ...FAST, software: "Quillo", fingerprint: true })?
    { address: me.address, family: me.family, port_ok: me.port > 1023, software: me.software, sent: me.sent, retransmitted: stun_binding(env("STUN_DROPPY", SERVER), FAST)?.sent }
}

route GET /fallback {
    let r = stun_public_address([SILENT, SERVER], { timeout_ms: 300, rto_ms: 100 })?
    { address: r.address, tried_first: len(r.tried) }
}

route GET /nat {
    let r = stun_nat_check(SERVER, OTHER, FAST)?
    { mapping: r.mapping, behind_nat: r.behind_nat, sym: stun_nat_check(SERVER, env("STUN_SHIFTY", OTHER), FAST)?.mapping }
}

route GET /locked {
    let ok = stun_binding(LOCKED, { ...FAST, username: "ada", password: "lovelace" })?
    {
        address: ok.address,
        no_password: stun_binding(LOCKED, FAST).error,
        wrong: stun_binding(LOCKED, { ...FAST, username: "ada", password: "nope" }).error,
    }
}

route GET /silent = { error: stun_binding(SILENT, { timeout_ms: 400, rto_ms: 100 }).error }

// RFC 5769: a request signed with a short-term password, and an IPv4 response.
let SAMPLE_REQUEST = "000100582112a442b7e7a701bc34d686fa87dfae" + "802200105354554e207465737420636c69656e74" + "002400046e0001ff" + "80290008932ff9b151263b36" + "000600096576746a3a68367659202020"
    + "000800149aeaa70cbfd8cb56781ef2b5b2d3f249c1b571a2" + "80280004e57a3bcf"
let SAMPLE_RESPONSE = "0101003c2112a442b7e7a701bc34d686fa87dfae" + "8022000b7465737420766563746f7220" + "002000080001a147e112a643"
    + "000800142b91f599fd9e90c38c7489f92af9ba53f06be7d7" + "80280004c07d4c96"
let RFC_PASSWORD = "VOkJxbRl1RmTxUk/WvJxBt"

route GET /vectors {
    let req = stun_parse(SAMPLE_REQUEST)?
    let res = stun_parse(SAMPLE_RESPONSE)?
    {
        request: [req.class, req.username, req.software, req.priority, req.ice_role],
        request_signed: stun_check_integrity(SAMPLE_REQUEST, { password: RFC_PASSWORD })?,
        request_crc: stun_check_fingerprint(SAMPLE_REQUEST)?,
        response: [res.class, res.address, res.port, res.software],
        response_signed: stun_check_integrity(SAMPLE_RESPONSE, { password: RFC_PASSWORD })?,
        wrong_password: stun_check_integrity(SAMPLE_REQUEST, { password: "guess" })?,
    }
}

route GET /damaged = {
    short: stun_parse("0001").error,
    no_cookie: stun_parse("0001000000000000000000000000000000000000").error,
    length: stun_parse("000100042112a442b7e7a701bc34d686fa87dfae").error,
    overrun: stun_parse("000100042112a442b7e7a701bc34d686fa87dfae00060010").error,
    no_integrity: stun_check_integrity("000100002112a442b7e7a701bc34d686fa87dfae", { password: "x" }).error,
    bad_crc: stun_check_fingerprint(slice(SAMPLE_RESPONSE, 0, len(SAMPLE_RESPONSE) - 2) + "00")?,
}

route GET /ice = {
    host: stun_ice_priority("host", 65535, 1)?,
    relay: stun_ice_priority("relay", 0, 2)?,
    bad: stun_ice_priority("mdns", 1, 1).error,
    check: stun_parse(stun_request({ username: "abc:def", password: "secret", priority: 1853817087, use_candidate: true, ice_controlling: "0102030405060708", fingerprint: true })?)?,
}

// Our own server, asked by the plugin.
udp.listen(MINE, (p) => {
    let answer = stun_reply(p.bytes, p.from, { software: "Quillo STUN", fingerprint: true })?
    if answer { udp.reply(p, answer) }
})

route GET /own {
    let me = stun_binding(`127.0.0.1:${MINE}`, { ...FAST, software: "client" })?
    { address: me.address, software: me.software, sent: me.sent }
}
