<commit_before>package main

import (
	"crypto/rand"
	"crypto/tls"
	"encoding/base32"
	"encoding/json"
	"errors"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/devel/dnsmapper/storeapi"
	"github.com/gorilla/handlers"
)

type ipResponse struct {
	DNS  string
	EDNS string
	HTTP string
}

var (
	uuidCh    chan string
	localNets []*net.IPNet
)

func init() {
	go uuidFactory()

	pn := []string{
		"127.0.0.0/8",
		"10.0.0.0/8",
		"172.16.0.0/12",
		"192.168.0.0/16",
		"fc00::/7",
	}
	for _, p := range pn {
		_, ipnet, err := net.ParseCIDR(p)
		if err != nil {
			panic(err)
		}
		localNets = append(localNets, ipnet)
	}
}

func uuidFactory() {
	uuidCh = make(chan string, 10)

	enc := base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")

	length := 20

	buf := make([]byte, length)
	uuid := make([]byte, enc.EncodedLen(length))

	for {
		rand.Read(buf)
		enc.Encode(uuid, buf)
		uuidCh <- string(uuid)
	}
}

func uuid() string {
	return <-uuidCh
}

func remoteIP(xff string) string {
	if len(xff) > 0 {
		ips := strings.Split(xff, ",")
		for i := len(ips) - 1; i >= 0; i-- {
			ip := strings.TrimSpace(ips[i])
			nip := net.ParseIP(ip)
			if nip != nil {
				if localNet(nip) {
					continue
				}
				return nip.String()
			}
		}
	}

	return ""
}

func jsonData(req *http.Request) (string, error) {

	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
	nip := net.ParseIP(ip)

	if xff := req.Header.Get("X-Forwarded-For"); len(xff) > 0 && localNet(nip) {
		ip = remoteIP(xff)
	}

	resp := &ipResponse{HTTP: ip, DNS: ""}

	uuid := getUUIDFromDomain(req.Host)

	dns, edns, ok := getCache(uuid)

	if !ok {
		return "", errors.New("UUID not found")
	}

	resp.DNS = dns
	resp.EDNS = edns

	js, err := json.Marshal(resp)
	if err != nil {
		log.Print("JSON ERROR:", err)
		return "", err
	}

	data := storeapi.RequestData{
		TestIP:   *flagip,
		ServerIP: resp.DNS,
		ClientIP: resp.HTTP,
		EdnsNet:  resp.EDNS,
	}
	select {
	case ch <- &data:
	default:
		log.Println("dropped log data, queue full")
	}

	return string(js), nil
}

func redirectUUID(w http.ResponseWriter, req *http.Request) {
	uuid := uuid()
	host := uuid + "." + *flagdomain

	proto := "http"

	if req.TLS != nil {
		proto = "https"
	}

	http.Redirect(w, req, proto+"://"+host+req.RequestURI, 302)
	return
}

func mainServer(w http.ResponseWriter, req *http.Request) {

	if req.URL.Path == "/jsonp" || req.URL.Path == "/json" || req.URL.Path == "/none" {

		w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET")

		uuid := getUUIDFromDomain(req.Host)
		if uuid == "www" {
			redirectUUID(w, req)
			return
		}

		js, err := jsonData(req)
		if err != nil {
			log.Printf("redirecting to new uuid, err: %s", err)
			redirectUUID(w, req)
			return
		}

		if req.URL.Path == "/none" {
			w.WriteHeader(204)
			return
		}

		if jsonp := req.FormValue("jsonp"); len(jsonp) > 0 {
			w.Header().Set("Content-Type", "text/javascript")
			io.WriteString(w, jsonp+"("+js+");\n")
			return
		}

		// not jsonp
		w.Header().Set("Content-Type", "application/json")
		io.WriteString(w, js+"\n")
		return

	}

	if req.URL.Path == "/mapper.js" {
		w.Header().Set("Cache-Control", "public, max-age=86400")
		w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
		w.WriteHeader(200)
		io.WriteString(w, `
(function(global){"use strict";var id=function(){var chars="0123456789abcdefghijklmnopqrstuvxyz".split("");
var uuid=[],rnd=Math.random,r;for(var i=0;i<17;i++){if(!uuid[i]){r=0|rnd()*16;uuid[i]=chars[i==19?r&3|8:r&15]}}
return uuid.join("")};
setTimeout(function(){(new Image).src="http://"+id()+".`+
			*flagdomain+
			`/none"},3200)})(this);
`)
		return
	}

	if req.URL.Path == "/" {
		w.Header().Set("Cache-Control", "public, max-age=900")
		w.WriteHeader(200)
		io.WriteString(w, HOMEPAGE)
		return
	}

	if req.URL.Path == "/robots.txt" {
		w.Header().Set("Cache-Control", "public, max-age=604800")
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(200)
		io.WriteString(w, "# Hi Robot!\n")
		return
	}

	if req.URL.Path == "/version" {
		io.WriteString(w, `<html><head><title>DNS Mapper `+
			VERSION+`</title><body>`+
			`Hello`+
			`</body></html>`)
		return
	}

	http.NotFound(w, req)
	return<commit_msg>Add v6test system compatibility<commit_after>package main

import (
	"crypto/rand"
	"crypto/tls"
	"encoding/base32"
	"encoding/json"
	"errors"
	"io"
	"log"
	"net"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/devel/dnsmapper/storeapi"
	"github.com/gorilla/handlers"
)

type ipResponse struct {
	DNS  string
	EDNS string
	HTTP string
}

var (
	uuidCh    chan string
	localNets []*net.IPNet
)

func init() {
	go uuidFactory()

	pn := []string{
		"127.0.0.0/8",
		"10.0.0.0/8",
		"172.16.0.0/12",
		"192.168.0.0/16",
		"fc00::/7",
	}
	for _, p := range pn {
		_, ipnet, err := net.ParseCIDR(p)
		if err != nil {
			panic(err)
		}
		localNets = append(localNets, ipnet)
	}
}

func uuidFactory() {
	uuidCh = make(chan string, 10)

	enc := base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567")

	length := 20

	buf := make([]byte, length)
	uuid := make([]byte, enc.EncodedLen(length))

	for {
		rand.Read(buf)
		enc.Encode(uuid, buf)
		uuidCh <- string(uuid)
	}
}

func uuid() string {
	return <-uuidCh
}

func remoteIP(xff string) string {
	if len(xff) > 0 {
		ips := strings.Split(xff, ",")
		for i := len(ips) - 1; i >= 0; i-- {
			ip := strings.TrimSpace(ips[i])
			nip := net.ParseIP(ip)
			if nip != nil {
				if localNet(nip) {
					continue
				}
				return nip.String()
			}
		}
	}

	return ""
}

func jsonData(req *http.Request) (string, error) {

	ip, _, _ := net.SplitHostPort(req.RemoteAddr)
	nip := net.ParseIP(ip)

	if xff := req.Header.Get("X-Forwarded-For"); len(xff) > 0 && localNet(nip) {
		ip = remoteIP(xff)
	}

	resp := &ipResponse{HTTP: ip, DNS: ""}

	uuid := getUUIDFromDomain(req.Host)

	dns, edns, ok := getCache(uuid)

	if !ok {
		return "", errors.New("UUID not found")
	}

	resp.DNS = dns
	resp.EDNS = edns

	js, err := json.Marshal(resp)
	if err != nil {
		log.Print("JSON ERROR:", err)
		return "", err
	}

	data := storeapi.RequestData{
		TestIP:   *flagip,
		ServerIP: resp.DNS,
		ClientIP: resp.HTTP,
		EdnsNet:  resp.EDNS,
	}
	select {
	case ch <- &data:
	default:
		log.Println("dropped log data, queue full")
	}

	return string(js), nil
}

func redirectUUID(w http.ResponseWriter, req *http.Request) {
	uuid := uuid()
	host := uuid + "." + *flagdomain

	proto := "http"

	if req.TLS != nil {
		proto = "https"
	}

	http.Redirect(w, req, proto+"://"+host+req.RequestURI, 302)
	return
}

func mainServer(w http.ResponseWriter, req *http.Request) {

	if req.URL.Path == "/jsonp" || req.URL.Path == "/json" || req.URL.Path == "/none" {

		w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
		w.Header().Set("Access-Control-Allow-Origin", "*")
		w.Header().Set("Access-Control-Allow-Methods", "GET")

		uuid := getUUIDFromDomain(req.Host)
		if uuid == "www" {
			redirectUUID(w, req)
			return
		}

		js, err := jsonData(req)
		if err != nil {
			log.Printf("redirecting to new uuid, err: %s", err)
			redirectUUID(w, req)
			return
		}

		if req.URL.Path == "/none" {
			w.WriteHeader(204)
			return
		}

		jsonp := req.FormValue("jsonp")
		if len(jsonp) == 0 {
			jsonp = req.FormValue("callback")
		}

		if len(jsonp) > 0 {
			w.Header().Set("Content-Type", "text/javascript")
			io.WriteString(w, jsonp+"("+js+");\n")
			return
		}

		// not jsonp
		w.Header().Set("Content-Type", "application/json")
		io.WriteString(w, js+"\n")
		return

	}

	mapperScript := `
	 (function(global){"use strict";var id=function(){var chars="0123456789abcdefghijklmnopqrstuvxyz".split("");
	 var uuid=[],rnd=Math.random,r;for(var i=0;i<17;i++){if(!uuid[i]){r=0|rnd()*16;uuid[i]=chars[i==19?r&3|8:r&15]}}
	 return uuid.join("")};
	 setTimeout(function(){(new Image).src="http://"+id()+".` +
		*flagdomain +
		`/none"},3200)})(this);
	 `

	if req.URL.Path == "/mapper.js" {
		w.Header().Set("Cache-Control", "public, max-age=86400")
		w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
		w.WriteHeader(200)
		io.WriteString(w, mapperScript)
		return
	}

	if req.URL.Path == "/mapper-v6compat.js" {
		w.Header().Set("Cache-Control", "public, max-age=60")
		w.Header().Set("Content-Type", "text/javascript; charset=utf-8")
		w.WriteHeader(200)
		io.WriteString(w, mapperScript)
		io.WriteString(w, `v6 = { "version": "2", test: function(){} };`)
		return
	}

	if req.URL.Path == "/" {
		w.Header().Set("Cache-Control", "public, max-age=900")
		w.WriteHeader(200)
		io.WriteString(w, HOMEPAGE)
		return
	}

	if req.URL.Path == "/robots.txt" {
		w.Header().Set("Cache-Control", "public, max-age=604800")
		w.Header().Set("Content-Type", "text/plain; charset=utf-8")
		w.WriteHeader(200)
		io.WriteString(w, "# Hi Robot!\n")
		return
	}

	if req.URL.Path == "/version" {
		io.WriteString(w, `<html><head><title>DNS Mapper `+
			VERSION+`</title><body>`+
			`Hello`+
			`</body></html>`)
		return
	}

	http.NotFound(w, req)
	return
{'fraction_non_alphanumeric': 0.2031284182892146, 'fraction_numerical': 0.025486764384161014, 'mean_word_length': 3.5217606330365974, 'pattern_counts': {'":': 3, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 8, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5018966', 'n_tokens_mistral': 4180, 'n_tokens_neox': 3744, 'n_words': 694}