import assert from "assert" ; import net from "net" ; import { ServiceType } from "../CiaoService" ; import { Protocol } from "../index" ; function isProtocol ( part ) { return part === "_" + Protocol . TCP || part === "_" + Protocol . UDP ; } function isSub ( part ) { return part === "_sub" ; } function removePrefixedUnderscore ( part ) { return part . startsWith ( "_" ) ? part . slice ( 1 ) : part ; } export interface PTRQueryDomain { domain : string ; protocol : Protocol ; type : ServiceType | string ; } export interface InstanceNameDomain { domain : string ; protocol : Protocol ; type : ServiceType | string ; name : string ; } export interface SubTypedNameDomain { domain : string ; protocol : Protocol ; type : ServiceType | string ; subtype : ServiceType | string ; } export interface FQDNParts { name ? : string ; type : ServiceType | string ; protocol ? : Protocol ; domain ? : string ; } export interface SubTypePTRParts { subtype : ServiceType | string ; type : ServiceType | string ; protocol ? : Protocol ; domain ? : string ; } function isSubTypePTRParts ( parts ) { return "subtype" in parts ; } export function parseFQDN ( fqdn ) { const parts = fqdn . split ( "." ) ; assert ( parts . length >= 3 , "Received illegal fqdn: " + fqdn ) ; let i = parts . length - 1 ; let domain = "" ; while ( ! isProtocol ( parts [ i ] ) ) { domain = removePrefixedUnderscore ( parts [ i ] ) + ( domain ? "." + domain : "" ) ; i -- ; } assert ( i >= 1 , "Failed to parse illegal fqdn: " + fqdn ) ; const protocol = removePrefixedUnderscore ( parts [ i -- ] ) as Protocol ; const type = removePrefixedUnderscore ( parts [ i -- ] ) ; if ( i < 0 ) { return { domain : domain , protocol : protocol , type : type , } ; } else if ( isSub ( parts [ i ] ) ) { i -- ; assert ( i === 0 , "Received illegal formatted sub type fqdn: " + fqdn ) ; const subtype = removePrefixedUnderscore ( parts [ i ] ) ; return { domain : domain , protocol : protocol , type : type , subtype : subtype , } ; } else { const name = removePrefixedUnderscore ( parts . slice ( 0 , i + 1 ) . join ( "." ) ) ; return { domain : domain , protocol : protocol , type : type , name : name , } ; } } export function stringify ( parts ) { assert ( parts . type , "type cannot be undefined" ) ; assert ( parts . type . length <= 15 , "type must not be longer than 15 characters" ) ; let prefix ; if ( isSubTypePTRParts ( parts ) ) { prefix = `_${ parts . subtype }._sub.` ; } else { prefix = parts . name ? `${ parts . name }.` : "" ; } return `${ prefix }_${ parts . type }._${ parts . protocol || Protocol . TCP }.${ parts . domain || "local" }.` ; } export function formatHostname ( hostname , domain = "local" ) { assert ( ! hostname . endsWith ( "." ) , "hostname must not end with the root label!" ) ; const tld = "." + domain ; return ( ! hostname . endsWith ( tld ) ? hostname + tld : hostname ) + "." ; } export function removeTLD ( hostname ) { if ( hostname . endsWith ( "." ) ) { hostname = hostname . substring ( 0 , hostname . length - 1 ) ; } const lastDot = hostname . lastIndexOf ( "." ) ; return hostname . slice ( 0 , lastDot ) ; } export function enlargeIPv6 ( address ) { assert ( net . isIPv6 ( address ) , "Illegal argument. Must be ipv6 address!" ) ; assert ( ! address . includes ( "." ) , "ipv4-mapped ipv6 addresses are currently unsupported!" ) ; const split = address . split ( ":" ) ; if ( split [ 0 ] === "" ) { split . splice ( 0 , 1 ) ; while ( split . length < 8 ) { split . unshift ( "0000" ) ; } } else if ( split [ split . length - 1 ] === "" ) { split . splice ( split . length - 1 , 1 ) ; while ( split . length < 8 ) { split . push ( "0000" ) ; } } else if ( split . length < 8 ) { let emptySection ; for ( emptySection = 0 ; emptySection < split . length ; emptySection ++ ) { if ( split [ emptySection ] === "" ) { break ; } } const replacements = new Array ( 9 - split . length ) . fill ( "0000" ) ; split . splice ( emptySection , 1 , ... replacements ) ; } for ( let i = 0 ; i < split . length ; i ++ ) { const element = split [ i ] ; if ( element . length < 4 ) { const zeros = new Array ( 4 - element . length ) . fill ( "0" ) . join ( "" ) ; split . splice ( i , 1 , zeros + element ) ; } } const result = split . join ( ":" ) ; assert ( split . length <= 8 , `Resulting ipv6 address has more than 8 sections (${ result })!` ) ; return result ; } export function shortenIPv6 ( address ) { if ( typeof address === "string" ) { address = address . split ( ":" ) ; } for ( let i = 0 ; i < address . length ; i ++ ) { const part = address [ i ] ; let j = 0 ; for ( ; j < Math . min ( 3 , part . length - 1 ) ; j ++ ) { if ( part . charAt ( j ) !== "0" ) { break ; } } address [ i ] = part . substr ( j ) ; } let longestBlockOfZerosIndex = - 1 ; let longestBlockOfZerosLength = 0 ; for ( let i = 0 ; i < address . length ; i ++ ) { if ( address [ i ] !== "0" ) { continue ; } let zerosCount = 1 ; let j = i + 1 ; for ( ; j < address . length ; j ++ ) { if ( address [ j ] === "0" ) { zerosCount ++ ; } else { break ; } } if ( zerosCount > longestBlockOfZerosLength ) { longestBlockOfZerosIndex = i ; longestBlockOfZerosLength = zerosCount ; } i = j ; } if ( longestBlockOfZerosIndex !== - 1 ) { const startOrEnd = longestBlockOfZerosIndex === 0 || ( longestBlockOfZerosIndex + longestBlockOfZerosLength === 8 ) ; address [ longestBlockOfZerosIndex ] = startOrEnd ? ":" : "" ; if ( longestBlockOfZerosLength > 1 ) { address . splice ( longestBlockOfZerosIndex + 1 , longestBlockOfZerosLength - 1 ) ; } } const result = address . join ( ":" ) ; if ( result === ":" ) { return "::" ; } return result ; } export function formatReverseAddressPTRName ( address ) { if ( net . isIPv4 ( address ) ) { const split = address . split ( "." ) . reverse ( ) ; return split . join ( "." ) + ".in-addr.arpa" ; } else if ( net . isIPv6 ( address ) ) { address = enlargeIPv6 ( address ) . toUpperCase ( ) ; const nibbleSplit = address . replace ( /:/g , "" ) . split ( "" ) . reverse ( ) ; assert ( nibbleSplit . length === 32 , "Encountered invalid ipv6 address length! " + nibbleSplit . length ) ; return nibbleSplit . join ( "." ) + ".ip6.arpa" ; } else { throw new Error ( "Supplied illegal ip address format: " + address ) ; } } export function ipAddressFromReversAddressName ( name ) { name = name . toLowerCase ( ) ; if ( name . endsWith ( ".in-addr.arpa" ) ) { const split = name . replace ( ".in-addr.arpa" , "" ) . split ( "." ) . reverse ( ) ; return split . join ( "." ) ; } else if ( name . endsWith ( ".ip6.arpa" ) ) { const split = name . replace ( ".ip6.arpa" , "" ) . split ( "." ) . reverse ( ) ; assert ( split . length === 32 , "Encountered illegal length for .ip6.arpa split!" ) ; const parts = [ ] ; for ( let i = 0 ; i < split . length ; i += 4 ) { parts . push ( split . slice ( i , i + 4 ) . join ( "" ) ) ; } return shortenIPv6 ( parts . join ( ":" ) ) ; } else { throw new Error ( "Supplied unknown reverse address name format: " + name ) ; } } export function getNetAddress ( address , netmask ) { assert ( net . isIP ( address ) === net . isIP ( netmask ) , "IP address version must match. Netmask cannot have a version different from the address!" ) ; if ( net . isIPv4 ( address ) ) { const addressParts = address . split ( "." ) ; const netmaskParts = netmask . split ( "." ) ; const netAddressParts = new Array ( 4 ) ; for ( let i = 0 ; i < addressParts . length ; i ++ ) { const addressNum = parseInt ( addressParts [ i ] ) ; const netmaskNum = parseInt ( netmaskParts [ i ] ) ; netAddressParts [ i ] = ( addressNum & netmaskNum ) . toString ( ) ; } return netAddressParts . join ( "." ) ; } else if ( net . isIPv6 ( address ) ) { const addressParts = enlargeIPv6 ( address ) . split ( ":" ) ; const netmaskParts = enlargeIPv6 ( netmask ) . split ( ":" ) ; const netAddressParts = new Array ( 8 ) ; for ( let i = 0 ; i < addressParts . length ; i ++ ) { const addressNum = parseInt ( addressParts [ i ] , 16 ) ; const netmaskNum = parseInt ( netmaskParts [ i ] , 16 ) ; netAddressParts [ i ] = ( addressNum & netmaskNum ) . toString ( 16 ) ; } return shortenIPv6 ( enlargeIPv6 ( netAddressParts . join ( ":" ) ) ) ; } else { throw new Error ( "Illegal argument. Address is not an ip address!" ) ; } }