diff --git a/netlify.toml b/netlify.toml index 720cea1..4760a7e 100644 --- a/netlify.toml +++ b/netlify.toml @@ -113,6 +113,11 @@ from = "/whois-lookup" to = "/.netlify/functions/whois-lookup" status = 301 + force = true +[[redirects]] + from = "/dns-server" + to = "/.netlify/functions/dns-server" + status = 301 force = true # For router history mode, ensure pages land on index diff --git a/server/lambda/dns-server.js b/server/lambda/dns-server.js new file mode 100644 index 0000000..084f104 --- /dev/null +++ b/server/lambda/dns-server.js @@ -0,0 +1,48 @@ +const dns = require('dns'); +const dnsPromises = dns.promises; +// const https = require('https'); +const axios = require('axios'); + +exports.handler = async (event) => { + const domain = event.queryStringParameters.url.replace(/^(?:https?:\/\/)?/i, ""); + try { + const addresses = await dnsPromises.resolve4(domain); + const results = await Promise.all(addresses.map(async (address) => { + const hostname = await dnsPromises.reverse(address).catch(() => null); + let dohDirectSupports = false; + try { + await axios.get(`https://${address}/dns-query`); + dohDirectSupports = true; + } catch (error) { + dohDirectSupports = false; + } + return { + address, + hostname, + dohDirectSupports, + }; + })); + // let dohMozillaSupport = false; + // try { + // const mozillaList = await axios.get('https://firefox.settings.services.mozilla.com/v1/buckets/security-state/collections/onecrl/records'); + // dohMozillaSupport = results.some(({ hostname }) => mozillaList.data.data.some(({ id }) => id.includes(hostname))); + // } catch (error) { + // console.error(error); + // } + return { + statusCode: 200, + body: JSON.stringify({ + domain, + dns: results, + // dohMozillaSupport, + }), + }; + } catch (error) { + return { + statusCode: 500, + body: JSON.stringify({ + error: `An error occurred while resolving DNS. ${error.message}`, + }), + }; + } +}; diff --git a/src/components/Results/DnsServer.tsx b/src/components/Results/DnsServer.tsx new file mode 100644 index 0000000..f78e2a7 --- /dev/null +++ b/src/components/Results/DnsServer.tsx @@ -0,0 +1,38 @@ + +import { Card } from 'components/Form/Card'; +import Heading from 'components/Form/Heading'; +import Row, { ExpandableRow, RowProps } from 'components/Form/Row'; +import colors from 'styles/colors'; + +const cardStyles = ` + small { + margin-top: 1rem; + opacity: 0.5; + display: block; + a { color: ${colors.primary}; } + } +`; + +const DnsServerCard = (props: {data: any, title: string, actionButtons: any }): JSX.Element => { + const dnsSecurity = props.data; + console.log(dnsSecurity); + return ( + + {dnsSecurity.dns.map((dns: any, index: number) => { + return (<> + DNS Server #{index+1} + + + + ); + })} + {dnsSecurity.dns.length > 0 && ( + * DoH Support is determined by the DNS server's response to a DoH query. + Sometimes this gives false negatives, and it's also possible that the DNS server supports DoH but does not respond to DoH queries. + If the DNS server does not support DoH, it may still be possible to use DoH by using a DoH proxy. + )} + + ); +} + +export default DnsServerCard; diff --git a/src/pages/Results.tsx b/src/pages/Results.tsx index 0745a37..fe717b2 100644 --- a/src/pages/Results.tsx +++ b/src/pages/Results.tsx @@ -35,6 +35,7 @@ import SiteFeaturesCard from 'components/Results/SiteFeatures'; import DnsSecCard from 'components/Results/DnsSec'; import HstsCard from 'components/Results/Hsts'; import DomainLookup from 'components/Results/DomainLookup'; +import DnsServerCard from 'components/Results/DnsServer'; import SelfScanMsg from 'components/misc/SelfScanMsg'; import ProgressBar, { LoadingJob, LoadingState, initialJobs } from 'components/misc/ProgressBar'; @@ -380,6 +381,14 @@ const Results = (): JSX.Element => { fetchRequest: () => fetch(`/whois-lookup?url=${address}`).then(res => parseJson(res)), }); + // Get the DNS server(s) for a domain, and test DoH/DoT support + const [dnsServerResults, updateDnsServerResults] = useMotherHook({ + jobId: 'dns-server', + updateLoadingJobs, + addressInfo: { address, addressType, expectedAddressTypes: urlTypeOnly }, + fetchRequest: () => fetch(`/dns-server?url=${address}`).then(res => parseJson(res)), + }); + /* Cancel remaining jobs after 10 second timeout */ useEffect(() => { const checkJobs = () => { @@ -424,6 +433,7 @@ const Results = (): JSX.Element => { { id: 'txt-records', title: 'TXT Records', result: txtRecordResults, Component: TxtRecordCard, refresh: updateTxtRecordResults }, { id: 'hsts', title: 'HSTS Check', result: hstsResults, Component: HstsCard, refresh: updateHstsResults }, { id: 'whois', title: 'Domain Info', result: whoIsResults, Component: WhoIsCard, refresh: updateWhoIsResults }, + { id: 'dns-server', title: 'DNS Server', result: dnsServerResults, Component: DnsServerCard, refresh: updateDnsServerResults }, { id: 'features', title: 'Site Features', result: siteFeaturesResults, Component: SiteFeaturesCard, refresh: updateSiteFeaturesResults }, { id: 'carbon', title: 'Carbon Footprint', result: carbonResults, Component: CarbonFootprintCard, refresh: updateCarbonResults }, ];