import { createHash } from "crypto" ; import { range } from "lodash" ; import { isIPv4 } from "net" ; export class Candidate { constructor ( public foundation , public component , public transport , public priority , public host , public port , public type , public relatedAddress ? , public relatedPort ? , public tcptype ? , public generation ? ) { } static fromSdp ( sdp ) { const bits = sdp . split ( " " ) ; if ( bits . length < 8 ) throw new Error ( "SDP does not have enough properties" ) ; const kwargs = { foundation : bits [ 0 ] , component : Number ( bits [ 1 ] ) , transport : bits [ 2 ] , priority : Number ( bits [ 3 ] ) , host : bits [ 4 ] , port : Number ( bits [ 5 ] ) , type : bits [ 7 ] , } ; for ( const i of range ( 8 , bits . length - 1 , 2 ) ) { if ( bits [ i ] === "raddr" ) { ( kwargs as any ) [ "related_address" ] = bits [ i + 1 ] ; } else if ( bits [ i ] === "rport" ) { ( kwargs as any ) [ "related_port" ] = Number ( bits [ i + 1 ] ) ; } else if ( bits [ i ] === "tcptype" ) { ( kwargs as any ) [ "tcptype" ] = bits [ i + 1 ] ; } else if ( bits [ i ] === "generation" ) { ( kwargs as any ) [ "generation" ] = Number ( bits [ i + 1 ] ) ; } } const { foundation , component , transport , priority , host , port , type } = kwargs ; return new Candidate ( foundation , component , transport , priority , host , port , type , ( kwargs as any ) [ "related_address" ] , ( kwargs as any ) [ "related_port" ] , ( kwargs as any ) [ "tcptype" ] , ( kwargs as any ) [ "generation" ] ) ; } canPairWith ( other ) { const a = isIPv4 ( this . host ) ; const b = isIPv4 ( other . host ) ; return ( this . component === other . component && this . transport . toLowerCase ( ) === other . transport . toLowerCase ( ) && a === b ) ; } toSdp ( ) { let sdp = `${ this . foundation } ${ this . component } ${ this . transport } ${ this . priority } ${ this . host } ${ this . port } typ ${ this . type }` ; if ( this . relatedAddress ) sdp += ` raddr ${ this . relatedAddress }` ; if ( this . relatedPort != undefined ) sdp += ` rport ${ this . relatedPort }` ; if ( this . tcptype ) sdp += ` tcptype ${ this . tcptype }` ; if ( this . generation != undefined ) sdp += ` generation ${ this . generation }` ; return sdp ; } } export function candidateFoundation ( candidateType , candidateTransport , baseAddress ) { const key = `${ candidateType }|${ candidateTransport }|${ baseAddress }` ; return createHash ( "md5" ) . update ( key , "ascii" ) . digest ( "hex" ) . slice ( 7 ) ; } export function candidatePriority ( candidateComponent , candidateType , localPref = 65535 ) { let typePref = 0 ; if ( candidateType === "host" ) { typePref = 126 ; } else if ( candidateType === "prflx" ) { typePref = 110 ; } else if ( candidateType === "srflx" ) { typePref = 100 ; } else { typePref = 0 ; } return ( ( 1 << 24 ) * typePref + ( 1 << 8 ) * localPref + ( 256 - candidateComponent ) ) ; }