import debug from "debug" ; import { Event } from "rx.mini" ; import { Candidate } from "../candidate" ; import { Connection } from "../ice" ; import { UdpTransport } from "../transport" ; import { Address , Protocol } from "../types/model" ; import { classes } from "./const" ; import { Message , parseMessage } from "./message" ; import { Transaction } from "./transaction" ; const log = debug ( "packages/ice/src/stun/protocol.ts" ) ; export class StunProtocol implements Protocol { readonly type = "stun" ; transport ! : UdpTransport ; transactions : { [ key ] : Transaction ; } = { } ; get transactionsKeys ( ) { return Object . keys ( this . transactions ) ; } localCandidate ? : Candidate ; sentMessage ? : Message ; localAddress ? : string ; private readonly closed = new Event ( ) ; constructor ( public receiver ) { } connectionLost ( ) { this . closed . execute ( ) ; this . closed . complete ( ) ; } connectionMade = async ( useIpv4 , portRange ? ) => { if ( useIpv4 ) { this . transport = await UdpTransport . init ( "udp4" , portRange ) ; } else { this . transport = await UdpTransport . init ( "udp6" , portRange ) ; } this . transport . onData = ( data , addr ) => this . datagramReceived ( data , addr ) ; } ; private datagramReceived ( data , addr ) { if ( ! this . localCandidate ) throw new Error ( "not exist" ) ; const message = parseMessage ( data ) ; if ( ! message ) { this . receiver . dataReceived ( data , this . localCandidate . component ) ; return ; } if ( ( message . messageClass === classes . RESPONSE || message . messageClass === classes . ERROR ) && this . transactionsKeys . includes ( message . transactionIdHex ) ) { const transaction = this . transactions [ message . transactionIdHex ] ; transaction . responseReceived ( message , addr ) ; } else if ( message . messageClass === classes . REQUEST ) { this . receiver . requestReceived ( message , addr , this , data ) ; } } getExtraInfo ( ) { const { address : host , port } = this . transport . address ( ) ; return [ host , port ] ; } async sendStun ( message , addr ) { const data = message . bytes ; await this . transport . send ( data , addr ) . catch ( ( ) => { log ( "sendStun failed" , addr , message ) ; } ) ; } async sendData ( data , addr ) { await this . transport . send ( data , addr ) ; } async request ( request , addr , integrityKey ? , retransmissions ? ) { if ( this . transactionsKeys . includes ( request . transactionIdHex ) ) throw new Error ( "already request ed" ) ; if ( integrityKey ) { request . addMessageIntegrity ( integrityKey ) ; request . addFingerprint ( ) ; } const transaction = new Transaction ( request , addr , this , retransmissions ) ; transaction . integrityKey = integrityKey ; this . transactions [ request . transactionIdHex ] = transaction ; try { return await transaction . run ( ) ; } finally { delete this . transactions [ request . transactionIdHex ] ; } } async close ( ) { Object . values ( this . transactions ) . forEach ( ( transaction ) => { transaction . cancel ( ) ; } ) ; await this . transport . close ( ) ; } }