UNPKG

@multiformats/multiaddr-matcher

Version:
498 lines (451 loc) 12.4 kB
/** * @packageDocumentation * * This module exports various matchers that can be used to infer the type of a * passed multiaddr. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { DNS } from '@multiformats/multiaddr-matcher' * * const ma = multiaddr('/dnsaddr/example.org') * * DNS.matches(ma) // true - this is a multiaddr with a DNS address at the start * ``` * * @example * * The default matching behaviour ignores any subsequent tuples in the multiaddr. * If you want stricter matching you can use `.exactMatch`: * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { DNS, Circuit } from '@multiformats/multiaddr-matcher' * * const ma = multiaddr('/dnsaddr/example.org/p2p/QmFoo/p2p-circuit/p2p/QmBar') * * DNS.exactMatch(ma) // false - this address has extra tuples after the DNS component * Circuit.matches(ma) // true * Circuit.exactMatch(ma) // true - the extra tuples are circuit relay related * ``` */ import { isIPv4, isIPv6 } from '@chainsafe/is-ip' import { and, or, literal, string, peerId, optional, fmt, func, number, certhash } from './utils.js' import type { Multiaddr } from '@multiformats/multiaddr' /** * A matcher accepts multiaddr components and either fails to match and returns * false or returns a sublist of unmatched components */ export interface Matcher { match(parts: string[]): string[] | false pattern: string } /** * A MultiaddrMatcher allows interpreting a multiaddr as a certain type of * multiaddr */ export interface MultiaddrMatcher { /** * The matchers that make up this MultiaddrMatcher - useful if you want to * make your own custom matchers */ matchers: Matcher[] /** * Returns true if the passed multiaddr can be treated as this type of * multiaddr */ matches(ma: Multiaddr): boolean /** * Returns true if the passed multiaddr terminates as this type of * multiaddr */ exactMatch(ma: Multiaddr): boolean } /** * Matches PeerId addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { PEER_ID } from '@multiformats/multiaddr-matcher' * * PEER_ID.matches(multiaddr('/p2p/Qmfoo')) // true * PEER_ID.matches(multiaddr('/ipfs/Qmfoo')) // true * ``` */ const _PEER_ID = peerId() export const PEER_ID = fmt(_PEER_ID) /** * DNS matchers */ const _DNS4 = and(literal('dns4'), string()) const _DNS6 = and(literal('dns6'), string()) const _DNSADDR = and(literal('dnsaddr'), string()) const _DNS = and(literal('dns'), string()) /** * Matches dns4 addresses. * * Use {@link DNS DNS} instead to match any type of DNS address. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { DNS4 } from '@multiformats/multiaddr-matcher' * * DNS4.matches(multiaddr('/dns4/example.org')) // true * ``` */ export const DNS4 = fmt(_DNS4, optional(peerId())) /** * Matches dns6 addresses. * * Use {@link DNS DNS} instead to match any type of DNS address. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { DNS6 } from '@multiformats/multiaddr-matcher' * * DNS6.matches(multiaddr('/dns6/example.org')) // true * ``` */ export const DNS6 = fmt(_DNS6, optional(peerId())) /** * Matches dnsaddr addresses. * * Use {@link DNS DNS} instead to match any type of DNS address. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { DNSADDR } from '@multiformats/multiaddr-matcher' * * DNSADDR.matches(multiaddr('/dnsaddr/example.org')) // true * DNSADDR.matches(multiaddr('/dnsaddr/example.org/p2p/Qmfoo')) // true * ``` */ export const DNSADDR = fmt(_DNSADDR, optional(peerId())) /** * Matches any dns address. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { DNS } from '@multiformats/multiaddr-matcher' * * DNS.matches(multiaddr('/dnsaddr/example.org')) // true * DNS.matches(multiaddr('/dns4/example.org')) // true * DNS.matches(multiaddr('/dns6/example.org')) // true * DNS.matches(multiaddr('/dns6/example.org/p2p/Qmfoo')) // true * ``` */ export const DNS = fmt(or(_DNS, _DNSADDR, _DNS4, _DNS6), optional(peerId())) const _IP4 = and(literal('ip4'), func(isIPv4)) const _IP6 = and(literal('ip6'), func(isIPv6)) const _IP = or(_IP4, _IP6) const _IP_OR_DOMAIN = or(_IP, _DNS, _DNS4, _DNS6, _DNSADDR) /** * A matcher for addresses that start with IP or DNS tuples. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { IP_OR_DOMAIN } from '@multiformats/multiaddr-matcher' * * IP_OR_DOMAIN.matches(multiaddr('/ip4/123.123.123.123')) // true * IP_OR_DOMAIN.matches(multiaddr('/ip4/123.123.123.123/p2p/QmFoo')) // true * IP_OR_DOMAIN.matches(multiaddr('/dns/example.com/p2p/QmFoo')) // true * IP_OR_DOMAIN.matches(multiaddr('/p2p/QmFoo')) // false * ``` */ export const IP_OR_DOMAIN = fmt(or(_IP, and(or(_DNS, _DNSADDR, _DNS4, _DNS6), optional(peerId())))) /** * Matches ip4 addresses. * * Use {@link IP IP} instead to match any ip4/ip6 address. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { IP4 } from '@multiformats/multiaddr-matcher' * * const ma = multiaddr('/ip4/123.123.123.123') * * IP4.matches(ma) // true * ``` */ export const IP4 = fmt(_IP4) /** * Matches ip6 addresses. * * Use {@link IP IP} instead to match any ip4/ip6 address. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { IP6 } from '@multiformats/multiaddr-matcher' * * const ma = multiaddr('/ip6/fe80::1cc1:a3b8:322f:cf22') * * IP6.matches(ma) // true * ``` */ export const IP6 = fmt(_IP6) /** * Matches ip4 or ip6 addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { IP } from '@multiformats/multiaddr-matcher' * * IP.matches(multiaddr('/ip4/123.123.123.123')) // true * IP.matches(multiaddr('/ip6/fe80::1cc1:a3b8:322f:cf22')) // true * ``` */ export const IP = fmt(_IP) const _TCP = and(_IP_OR_DOMAIN, literal('tcp'), number()) const _UDP = and(_IP_OR_DOMAIN, literal('udp'), number()) /** * Matches TCP addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { TCP } from '@multiformats/multiaddr-matcher' * * TCP.matches(multiaddr('/ip4/123.123.123.123/tcp/1234')) // true * ``` */ export const TCP = fmt(and(_TCP, optional(peerId()))) /** * Matches UDP addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { UDP } from '@multiformats/multiaddr-matcher' * * UDP.matches(multiaddr('/ip4/123.123.123.123/udp/1234')) // true * ``` */ export const UDP = fmt(_UDP) const _QUIC = and(_UDP, literal('quic'), optional(peerId())) const _QUICV1 = and(_UDP, literal('quic-v1'), optional(peerId())) const QUIC_V0_OR_V1 = or(_QUIC, _QUICV1) /** * Matches QUIC addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { QUIC } from '@multiformats/multiaddr-matcher' * * QUIC.matches(multiaddr('/ip4/123.123.123.123/udp/1234/quic')) // true * ``` */ export const QUIC = fmt(_QUIC) /** * Matches QUICv1 addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { QUICV1 } from '@multiformats/multiaddr-matcher' * * QUICV1.matches(multiaddr('/ip4/123.123.123.123/udp/1234/quic-v1')) // true * ``` */ export const QUICV1 = fmt(_QUICV1) const _WEB = or( _IP_OR_DOMAIN, _TCP, _UDP, _QUIC, _QUICV1 ) const _WebSockets = or( and(_WEB, literal('ws'), optional(peerId())) ) /** * Matches WebSocket addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { WebSockets } from '@multiformats/multiaddr-matcher' * * WebSockets.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/ws')) // true * ``` */ export const WebSockets = fmt(_WebSockets) const _WebSocketsSecure = or( and(_WEB, literal('wss'), optional(peerId())), and(_WEB, literal('tls'), optional(and(literal('sni'), string())), literal('ws'), optional(peerId())) ) /** * Matches secure WebSocket addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { WebSocketsSecure } from '@multiformats/multiaddr-matcher' * * WebSocketsSecure.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/wss')) // true * ``` */ export const WebSocketsSecure = fmt(_WebSocketsSecure) const _WebRTCDirect = and(_UDP, literal('webrtc-direct'), optional(certhash()), optional(certhash()), optional(peerId())) /** * Matches WebRTC-direct addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { WebRTCDirect } from '@multiformats/multiaddr-matcher' * * WebRTCDirect.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/p2p/QmFoo/webrtc-direct/certhash/u....')) // true * ``` */ export const WebRTCDirect = fmt(_WebRTCDirect) const _WebTransport = and(_QUICV1, literal('webtransport'), optional(certhash()), optional(certhash()), optional(peerId())) /** * Matches WebTransport addresses. * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { WebRTCDirect } from '@multiformats/multiaddr-matcher' * * WebRTCDirect.matches(multiaddr('/ip4/123.123.123.123/udp/1234/quic-v1/webtransport/certhash/u..../certhash/u..../p2p/QmFoo')) // true * ``` */ export const WebTransport = fmt(_WebTransport) const _P2P = or( _WebSockets, _WebSocketsSecure, and(_TCP, optional(peerId())), and(QUIC_V0_OR_V1, optional(peerId())), and(_IP_OR_DOMAIN, optional(peerId())), _WebRTCDirect, _WebTransport, peerId() ) /** * Matches peer addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { P2P } from '@multiformats/multiaddr-matcher' * * P2P.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/p2p/QmFoo')) // true * ``` */ export const P2P = fmt(_P2P) const _Circuit = and(_P2P, literal('p2p-circuit'), peerId()) /** * Matches circuit relay addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { Circuit } from '@multiformats/multiaddr-matcher' * * Circuit.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/p2p/QmRelay/p2p-circuit/p2p/QmTarget')) // true * ``` */ export const Circuit = fmt(_Circuit) const _WebRTC = or( and(_P2P, literal('p2p-circuit'), literal('webrtc'), optional(peerId())), and(_P2P, literal('webrtc'), optional(peerId())), and(literal('webrtc'), optional(peerId())) ) /** * Matches WebRTC addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { WebRTC } from '@multiformats/multiaddr-matcher' * * WebRTC.matches(multiaddr('/ip4/123.123.123.123/tcp/1234/p2p/QmRelay/p2p-circuit/webrtc/p2p/QmTarget')) // true * ``` */ export const WebRTC = fmt(_WebRTC) const _HTTP = or( and(_IP_OR_DOMAIN, literal('tcp'), number(), literal('http'), optional(peerId())), and(_IP_OR_DOMAIN, literal('http'), optional(peerId())) ) /** * Matches HTTP addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { HTTP } from '@multiformats/multiaddr-matcher' * * HTTP.matches(multiaddr('/dns/example.org/http')) // true * ``` */ export const HTTP = fmt(_HTTP) const _HTTPS = or( and(_IP_OR_DOMAIN, literal('tcp'), or( and(literal('443'), literal('http')), and(number(), literal('https')) ), optional(peerId())), and(_IP_OR_DOMAIN, literal('tls'), literal('http'), optional(peerId())), and(_IP_OR_DOMAIN, literal('https'), optional(peerId())) ) /** * Matches HTTPS addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { HTTP } from '@multiformats/multiaddr-matcher' * * HTTP.matches(multiaddr('/dns/example.org/tls/http')) // true * ``` */ export const HTTPS = fmt(_HTTPS) const _Memory = or( and(literal('memory'), string(), optional(peerId())) ) /** * Matches Memory addresses * * @example * * ```ts * import { multiaddr } from '@multiformats/multiaddr' * import { Memory } from '@multiformats/multiaddr-matcher' * * Memory.matches(multiaddr('/memory/0xDEADBEEF')) // true * ``` */ export const Memory = fmt(_Memory)