Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: redirect to proper encoded dnslink subdomain #66

Merged
merged 1 commit into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions src/dns-link-labels.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,35 @@
import { CID } from 'multiformats/cid'
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


/**
* For dnslinks see https://specs.ipfs.tech/http-gateways/subdomain-gateway/#host-request-header
* DNSLink names include . which means they must be inlined into a single DNS label to provide unique origin and work with wildcard TLS certificates.
*/

// DNS label can have up to 63 characters, consisting of alphanumeric
// characters or hyphens -, but it must not start or end with a hyphen.
const dnsLabelRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?$/

/**
* We can receive either IPNS Name string or DNSLink label string here.
* IPNS Names do not have dots or dashes.
*/
export function isValidDnsLabel (label: string): boolean {
// If string is not a valid IPNS Name (CID)
// then we assume it may be a valid DNSLabel.
try {
CID.parse(label)
return false
} catch {
return dnsLabelRegex.test(label)
}
}

/**
* We can receive either a peerId string or dnsLink label string here. PeerId strings do not have dots or dashes.
* Checks if label looks like inlined DNSLink.
* (https://specs.ipfs.tech/http-gateways/subdomain-gateway/#host-request-header)
*/
export function isDnsLabel (label: string): boolean {
return ['-', '.'].some((char) => label.includes(char))
export function isInlinedDnsLink (label: string): boolean {
return isValidDnsLabel(label) && label.includes('-') && !label.includes('.')
}

/**
Expand All @@ -18,7 +40,7 @@ export function isDnsLabel (label: string): boolean {
* @example en-wikipedia--on--ipfs-org.ipns.example.net -> example.net/ipns/en.wikipedia-on-ipfs.org
*/
export function dnsLinkLabelDecoder (linkLabel: string): string {
return linkLabel.replace(/--/g, '-').replace(/-/g, '.')
return linkLabel.replace(/--/g, '%').replace(/-/g, '.').replace(/%/g, '-')
}

/**
Expand All @@ -29,5 +51,5 @@ export function dnsLinkLabelDecoder (linkLabel: string): string {
* @example example.net/ipns/en.wikipedia-on-ipfs.org → Host: en-wikipedia--on--ipfs-org.ipns.example.net
*/
export function dnsLinkLabelEncoder (linkLabel: string): string {
return linkLabel.replace(/\./g, '-').replace(/-/g, '--')
return linkLabel.replace(/-/g, '--').replace(/\./g, '-')
}
7 changes: 6 additions & 1 deletion src/heliaServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createVerifiedFetch, type VerifiedFetch } from '@helia/verified-fetch'
import { type FastifyReply, type FastifyRequest, type RouteGenericInterface } from 'fastify'
import { USE_SUBDOMAINS } from './constants.js'
import { contentTypeParser } from './content-type-parser.js'
import { dnsLinkLabelEncoder, isInlinedDnsLink } from './dns-link-labels.js'
import { getCustomHelia } from './getCustomHelia.js'
import { getIpnsAddressDetails } from './ipns-address-utils.js'
import type { ComponentLogger, Logger } from '@libp2p/interface'
Expand Down Expand Up @@ -152,8 +153,12 @@ export class HeliaServer {
// }
// finalUrl += encodeURIComponent(`?${new URLSearchParams(request.query).toString()}`)
}
let encodedDnsLink = address
if (!isInlinedDnsLink(address)) {
encodedDnsLink = dnsLinkLabelEncoder(address)
}

const finalUrl = `//${cidv1Address ?? address}.${namespace}.${request.hostname}/${relativePath ?? ''}`
const finalUrl = `//${cidv1Address ?? encodedDnsLink}.${namespace}.${request.hostname}/${relativePath ?? ''}`
this.log('redirecting to final URL:', finalUrl)
await reply
.headers({
Expand Down
Loading