Commit 9c7d940

Ansari <ping@ansari.wtf>
2026-05-12 11:08:02
Adding running server and ai to it
1 parent 701999f
ai/client.go
@@ -0,0 +1,54 @@
+package ai
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"strings"
+	"time"
+)
+
+var client = &http.Client{
+	Timeout: 10 * time.Second,
+}
+
+func Ask(prompt string) (string, error) {
+	payload := map[string]any{
+		"model":  "llama3-chatqa:8b",
+		"prompt": prompt,
+		"stream": false,
+		"think":  false,
+	}
+
+	body, err := json.Marshal(payload)
+	if err != nil {
+		return "", fmt.Errorf("marshal: %w", err)
+	}
+
+	resp, err := client.Post(
+		"http://localhost:11434/api/generate",
+		"application/json",
+		bytes.NewReader(body),
+	)
+	if err != nil {
+		return "", fmt.Errorf("http post: %w", err)
+	}
+	defer resp.Body.Close()
+
+	respBody, err := io.ReadAll(resp.Body)
+	if err != nil {
+		return "", fmt.Errorf("read body: %w", err)
+	}
+
+	var result struct {
+		Response string `json:"response"`
+	}
+
+	if err := json.Unmarshal(respBody, &result); err != nil {
+		return "", fmt.Errorf("unmarshal: %w", err)
+	}
+
+	return strings.TrimSpace(result.Response), nil
+}
handlers/commands.go
@@ -0,0 +1,47 @@
+package handlers
+
+import (
+	"dnsense/ai"
+	"dnsense/dns"
+	"fmt"
+)
+
+type command struct {
+	prompt string
+}
+
+var commands = map[string]command{
+	"explain": {
+		prompt: "Explain '%s' in 2-3 sentences. Be concise, technical, and clear. No markdown.",
+	},
+	"tldr": {
+		prompt: "Give a TL;DR of '%s' in one sentence, max 200 characters. No markdown.",
+	},
+	"define": {
+		prompt: "Define '%s' in one sentence like a dictionary. Max 200 characters. No markdown.",
+	},
+	"ask": {
+		prompt: "Answer this question factually and concisely in 2-3 sentences. No markdown. No preamble. Question: '%s'",
+	},
+}
+
+func handleCMD(req *dns.Message, cmdName string, input string) *dns.Message {
+	cmd, ok := commands[cmdName]
+	if !ok {
+		return errorResponse(req, "unknown command: "+cmdName)
+	}
+
+	text, err := ai.Ask(fmt.Sprintf(cmd.prompt, input))
+	if err != nil {
+		return errorResponse(req, "ai error: "+err.Error())
+	}
+
+	rr, err := dns.NewTXTRecord(req.Questions[0].Name, 300, text)
+	if err != nil {
+		return errorResponse(req, "encoding error")
+	}
+
+	resp := baseResponse(req)
+	resp.Answers = append(resp.Answers, *rr)
+	return resp
+}
handlers/routers.go
@@ -0,0 +1,53 @@
+package handlers
+
+import (
+	"dnsense/dns"
+	"strings"
+)
+
+func baseResponse(req *dns.Message) *dns.Message {
+	resp := &dns.Message{}
+	resp.Header = req.Header
+	resp.Header.SetResponse()
+	resp.Header.SetAA()
+	resp.Header.Flags &^= (1 << 5) // clear AD bit
+	resp.Header.ARCount = 0
+	resp.Questions = req.Questions
+	return resp
+}
+
+func errorResponse(req *dns.Message, msg string) *dns.Message {
+	resp := baseResponse(req)
+
+	rr, err := dns.NewTXTRecord(req.Questions[0].Name, 30, "error: "+msg)
+	if err != nil {
+		return resp
+	}
+
+	resp.Answers = append(resp.Answers, *rr)
+	return resp
+}
+
+func Route(req *dns.Message) *dns.Message {
+	if len(req.Questions) == 0 {
+		return errorResponse(req, "no question")
+	}
+
+	q := req.Questions[0]
+
+	if q.Type != dns.TypeTXT {
+		return errorResponse(req, "only TXT queries supported")
+	}
+
+	name := strings.TrimSuffix(q.Name, ".")
+	labels := strings.SplitN(name, ".", 3)
+
+	if len(labels) < 2 {
+		return errorResponse(req, "format: <command>.<input>")
+	}
+
+	command := strings.ToLower(labels[0])
+	input := strings.ReplaceAll(labels[1], "-", " ")
+
+	return handleCMD(req, command, input)
+}
go.mod
@@ -0,0 +1,3 @@
+module dnsense
+
+go 1.25.5
main.go
@@ -0,0 +1,16 @@
+package main
+
+import (
+	"dnsense/dns"
+	"dnsense/handlers"
+	"log"
+)
+
+func main() {
+	server := dns.NewServer("0.0.0.0:8053", func(req *dns.Message) *dns.Message {
+		return handlers.Route(req)
+	})
+
+	log.Println("dns running on :8053")
+	log.Fatal(server.Start())
+}