package main import ( "bufio" "fmt" "io/ioutil" "log" "net" "os" "strings" "time" ) const ( port = "43" whoisversion = "v0.01" dataDir = "./data" // Directory where domain files are stored crlf = "\r\n" ) // Function to get metadata for a domain func getDomainMetadata(domain string) (string, string, string, string, error) { metaFilePath := fmt.Sprintf("%s/%s.meta", dataDir, domain) file, err := os.Open(metaFilePath) if err != nil { return "", "", "", "", err } defer file.Close() scanner := bufio.NewScanner(file) var registrar, registrationDate, expirationDate, status string if scanner.Scan() { registrar = scanner.Text() } if scanner.Scan() { registrationDate = scanner.Text() } if scanner.Scan() { expirationDate = scanner.Text() } if scanner.Scan() { status = scanner.Text() } if err := scanner.Err(); err != nil { return "", "", "", "", err } return registrar, registrationDate, expirationDate, status, nil } func handleConnection(conn net.Conn) { defer conn.Close() // Get the client's IP address clientIP := conn.RemoteAddr().String() // Read the domain name from the client reader := bufio.NewReader(conn) request, err := reader.ReadString('\n') if err != nil { log.Println("Error reading from connection:", err) return } // Clean up the request domain := strings.TrimSpace(request) // Get the current timestamp timestamp := time.Now().Format(time.RFC3339) // Check if the domain file exists filePath := fmt.Sprintf("%s/%s", dataDir, domain) domainInfo, err := ioutil.ReadFile(filePath) if err != nil { // Domain file not found, so check if metadata file exists metaFilePath := fmt.Sprintf("%s/%s.meta", dataDir, domain) if _, err := os.Stat(metaFilePath); err == nil { // Metadata file exists but domain file doesn't registrar, registrationDate, expirationDate, status, err := getDomainMetadata(domain) if err != nil { log.Println("Error reading metadata file:", err) conn.Write([]byte("Error reading domain metadata" + crlf)) return } response := fmt.Sprintf(" __ __ ____ \n ____/ /____ / /_ / __/__ __ _____\n / __ // __ \\ / __/ / /_ / / / // ___/\n/ /_/ // /_/ // /_ _ / __// /_/ // / | \n\\__,_/ \\____/ \\__/(_)/_/ \\__,_//_/ | dot.fur WHOIS Server\n _ __ __ __ ____ ____ _____ | whois.fur | whois.dotfur.net\n| | / // / / // __ \\ / _// ___/ | Version: %s\n| | /| / // /_/ // / / / / / \\__ \\ |\n| |/ |/ // __ // /_/ /_/ / ___/ / \n|__/|__//_/ /_/ \\____//___/ /____/ \n \n", whoisversion) response += fmt.Sprintf( "Domain: %s%sOrigin IP: %s%sTimestamp: %s%sRegistrar: %s%sRegistration Date: %s%sExpiration Date: %s%sStatus: %s%sNo WHOIS Info to show%s", domain, crlf, clientIP, crlf, timestamp, crlf, registrar, crlf, registrationDate, crlf, expirationDate, crlf, status, crlf, crlf) response += fmt.Sprintf("\n.fur WHOIS Server | https://git.fur/caramel/whois | Version: %s", whoisversion) response += crlf conn.Write([]byte(response)) } else { // Both domain file and metadata file not found log.Println("Domain and metadata file not found") conn.Write([]byte("Domain not found" + crlf)) } return } // Domain file found, get metadata for the domain registrar, registrationDate, expirationDate, status, err := getDomainMetadata(domain) if err != nil { log.Println("Error reading metadata file:", err) conn.Write([]byte("Error reading domain metadata" + crlf)) return } // Format the response with additional fields, origin IP, and timestamp response := fmt.Sprintf(" __ __ ____ \n ____/ /____ / /_ / __/__ __ _____\n / __ // __ \\ / __/ / /_ / / / // ___/\n/ /_/ // /_/ // /_ _ / __// /_/ // / | \n\\__,_/ \\____/ \\__/(_)/_/ \\__,_//_/ | dot.fur WHOIS Server\n _ __ __ __ ____ ____ _____ | whois.fur | whois.dotfur.net\n| | / // / / // __ \\ / _// ___/ | Version: %s\n| | /| / // /_/ // / / / / / \\__ \\ |\n| |/ |/ // __ // /_/ /_/ / ___/ / \n|__/|__//_/ /_/ \\____//___/ /____/ \n \n", whoisversion) response += fmt.Sprintf( "Domain: %s%sOrigin IP: %s%sTimestamp: %s%sRegistrar: %s%sRegistration Date: %s%sExpiration Date: %s%sStatus: %s%s%s", domain, crlf, clientIP, crlf, timestamp, crlf, registrar, crlf, registrationDate, crlf, expirationDate, crlf, status, crlf, string(domainInfo)) // Add a final CRLF to signify the end of the response response += fmt.Sprintf("\n.fur WHOIS Server | https://git.fur/caramel/whois | Version: %s", whoisversion) response += crlf // Send the response back to the client conn.Write([]byte(response)) log.Printf("Served domain: %s to %s at %s\n", domain, clientIP, timestamp) } func main() { // Start the TCP server listener, err := net.Listen("tcp", ":"+port) if err != nil { log.Fatalf("Error starting server: %s", err) } defer listener.Close() log.Printf("Whois server running on port %s\n", port) for { // Accept incoming connections conn, err := listener.Accept() if err != nil { log.Println("Error accepting connection:", err) continue } // Handle the connection in a new goroutine go handleConnection(conn) } }