Skip to content

Commit

Permalink
Merge pull request #37 from krystal/rdns
Browse files Browse the repository at this point in the history
rdns tracing support
  • Loading branch information
strideynet authored Mar 14, 2022
2 parents 1b05a56 + 948e2f1 commit 3a624ae
Show file tree
Hide file tree
Showing 5 changed files with 122 additions and 109 deletions.
6 changes: 5 additions & 1 deletion backend/api_v1/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,9 @@ func Init(g *gin.RouterGroup, log *zap.Logger, cachedDnsServer string, pinger *p
traceroute(g.Group("/traceroute", pingingBucket), pinger)
bgp(g.Group("/bgp", ratelimiter.NewBucket(log, 20, time.Hour, time.Minute*10)), makeBirdSocket)
whois(g.Group("/whois", ratelimiter.NewBucket(log, 20, time.Hour, time.Minute*10)), defaultWhoisLookuper{})
rdns(g.Group("/rdns", ratelimiter.NewBucket(log, 40, time.Hour, time.Minute*10)))
rdns(
g.Group("/rdns", ratelimiter.NewBucket(log, 40, time.Hour, time.Minute*10)),
log,
cachedDnsServer,
)
}
62 changes: 53 additions & 9 deletions backend/api_v1/rdns.go
Original file line number Diff line number Diff line change
@@ -1,31 +1,75 @@
package api_v1

import (
"fmt"
"net"

"github.com/gin-gonic/gin"
dnsLib "github.com/krystal/krystal-network-tools/backend/dns"
"go.uber.org/zap"
)

func rdns(g group) {
type rdnsParams struct {
// Trace is used to define if the DNS record should be traced all the way to the nameserver.
Trace bool `form:"trace"`
}

func rdns(g group, log *zap.Logger, dnsServer string) {
g.GET("/:ip", func(ctx *gin.Context) {
ip := ctx.Param("ip")
hosts, err := net.LookupAddr(ip)
if err != nil || len(hosts) == 0 {
if ctx.ContentType() == "application/json" {

isJson := ctx.ContentType() == "application/json"
var params rdnsParams
if err := ctx.BindQuery(&params); err != nil {
if isJson {
ctx.JSON(400, map[string]string{
"message": "Failed to find IP",
"message": err.Error(),
})
} else {
ctx.String(400, "Failed to find IP")
ctx.String(400, "unable to parse query params: %s", err.Error())
}
return
}

if !params.Trace {
hosts, err := net.LookupAddr(ip)
if err != nil || len(hosts) == 0 {
if ctx.ContentType() == "application/json" {
ctx.JSON(400, map[string]string{
"message": "Failed to find IP",
})
} else {
ctx.String(400, "Failed to find IP")
}
return
}
if ctx.ContentType() == "application/json" {
ctx.JSON(200, map[string]string{
"hostname": hosts[0],
})
} else {
ctx.String(200, hosts[0])
}

return
}

result, err := dnsLib.LookupRDNS(
log, ip, dnsServer,
)
if err != nil {
ctx.Error(&gin.Error{
Type: gin.ErrorTypePublic,
Err: fmt.Errorf("failed to perform dns lookup: %v", err),
})
return
}
if ctx.ContentType() == "application/json" {
ctx.JSON(200, map[string]string{
"hostname": hosts[0],
ctx.JSON(200, map[string]interface{}{
"trace": result,
})
} else {
ctx.String(200, hosts[0])
ctx.String(200, result.String())
}
})
}
75 changes: 0 additions & 75 deletions backend/api_v1/rdns_test.go

This file was deleted.

50 changes: 39 additions & 11 deletions backend/dns/dns_resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,23 @@ outerFor:
type Response map[string]RecordType

func (r Response) String() string {
return "TODO"
str := ""
for t, record := range r {
str += "--- " + t + " ---\n" + record.String()
}

return str
}

type RecordType []Server

func (rt RecordType) String() string {
return "TODO"
str := ""
for _, srv := range rt {
str += srv.String() + "\n"
}

return str
}

type Server struct {
Expand All @@ -75,10 +85,10 @@ type Server struct {
}

func (srv Server) String() string {
str := srv.Server + ": \n"
str := "-- " + srv.Server + " --\n"
for _, record := range srv.Records {
if record.stringer != nil {
str += record.stringer()
str += record.stringer() + "\n"
}
}

Expand Down Expand Up @@ -289,14 +299,14 @@ func findAuthoritativeNameserver(log *zap.Logger, hostname string) (string, Reco

// We need to follow the nameservers deeper. Select a random one and
// perform the search on that one now.
nextNameserver := msg.Ns[rand.Intn(len(msg.Ns))]

nameserverRecord, ok := nextNameserver.(*godns.NS)
if !ok {
return "", fmt.Errorf("unexpected record returned: %T", nextNameserver)
switch v := msg.Ns[rand.Intn(len(msg.Ns))].(type) {
case *godns.NS:
return recursiveSearch(iteration, v.Ns)
case *godns.SOA:
return v.Ns, nil
default:
return "", fmt.Errorf("unexpected record returned: %T", v)
}

return recursiveSearch(iteration, nameserverRecord.Ns)
}

authoritativeNameserver, err := recursiveSearch(0, rootNameserver)
Expand Down Expand Up @@ -416,3 +426,21 @@ func Lookup(log *zap.Logger, dnsServer, recordType, hostname string, fullTrace b

return recursiveQuery(log, dnsServer, recordType, hostname)
}

func reverseString(s string) string {
chars := []rune(s)
for i, j := 0, len(chars)-1; i < j; i, j = i+1, j-1 {
chars[i], chars[j] = chars[j], chars[i]
}
return string(chars)
}

func LookupRDNS(log *zap.Logger, ip, dnsServer string) (RecordType, error) {
hostname := reverseString(ip) + ".in-addr.arpa."
resp, err := traceQuery(log, dnsServer, "PTR", hostname)
if err != nil {
return nil, err
}

return resp["TRACE"], nil
}
38 changes: 25 additions & 13 deletions frontend/src/pages/reverse-dns/reverse-dns.view.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import { FC, useState } from "react";
import { FC, Fragment, useState } from "react";

import { Heading, Stack } from "@chakra-ui/react";
import { Box, Heading, Stack, Tag } from "@chakra-ui/react";
import Card from "../../common/card/card";
import ReverseDnsForm from "./reverse-dns-form";
import request from "../../api/request";
import endpoint from "../../api/endpoint";
import Code from "../../common/code/code";
import { DnsType } from "../dns/dns.schema";
import { DnsResponse } from "../dns/dns.view";
import DnsTable from "../dns/dns-table";

type ReverseDnsResponse = {
hostname: string;
};
type ReverseDnsResponse =
| { trace: DnsResponse[DnsType] }
| { hostname: string };

const ReverseDns: FC = () => {
const [ip, setIp] = useState("");
Expand All @@ -33,14 +36,23 @@ const ReverseDns: FC = () => {
</Card>

{result !== null && (
<Card>
<Heading size="sm" mb={4}>
{ip}
</Heading>
<Code fontSize="lg" w="100%">
{result.hostname}
</Code>
</Card>
<Fragment>
<Box>
<Tag colorScheme="brand" size="lg">
{ip}
</Tag>
</Box>

{"trace" in result ? (
<Card overflowX="auto">
<DnsTable record={result.trace} />
</Card>
) : (
<Card>
<Code fontSize="lg">{result.hostname}</Code>
</Card>
)}
</Fragment>
)}
</Stack>
);
Expand Down

0 comments on commit 3a624ae

Please sign in to comment.