package handlers import ( "bufio" "byom-infra-api/config" "encoding/json" "fmt" "log" "math/rand" "net/http" "os" "regexp" "strings" "github.com/gorilla/mux" ) type DNSRecord struct { FieldType string `json:"fieldType"` SubDomain string `json:"subDomain"` Target string `json:"target"` TTL int `json:"ttl"` } type DNSHandler struct { Zone string } func NewDNSHandler() *DNSHandler { zone, exists := config.GetConfig("dns_zone") log.Printf("DNS Handler initialized - zone = %s (exists: %v)\n", zone, exists) return &DNSHandler{ Zone: zone, } } // generateRandomWord generates a random 6-letter word from /usr/share/dict/words func (h *DNSHandler) generateRandomWord() (string, error) { file, err := os.Open("/usr/share/dict/words") if err != nil { return "", err } defer file.Close() var words []string scanner := bufio.NewScanner(file) regex := regexp.MustCompile(`^[a-zA-Z]{6}$`) for scanner.Scan() { word := strings.ToLower(scanner.Text()) if regex.MatchString(word) { words = append(words, word) } } if len(words) == 0 { return "", fmt.Errorf("no suitable words found") } return words[rand.Intn(len(words))], nil } // CreateDNSRecord handles the creation of a new DNS record func (h *DNSHandler) CreateDNSRecord(w http.ResponseWriter, r *http.Request) { log.Printf("Handling POST request for new DNS record from %s", r.RemoteAddr) // Parse request body for target IP var request struct { Target string `json:"target"` } if err := json.NewDecoder(r.Body).Decode(&request); err != nil { log.Printf("Error decoding request body: %v", err) http.Error(w, "Invalid request body", http.StatusBadRequest) return } // Generate random subdomain word1, err := h.generateRandomWord() if err != nil { log.Printf("Error generating first word: %v", err) http.Error(w, "Failed to generate subdomain", http.StatusInternalServerError) return } word2, err := h.generateRandomWord() if err != nil { log.Printf("Error generating second word: %v", err) http.Error(w, "Failed to generate subdomain", http.StatusInternalServerError) return } // Create DNS record record := DNSRecord{ FieldType: "A", SubDomain: word1 + "-" + word2, Target: request.Target, TTL: 0, } var response DNSRecord err = config.OVHClient.Post("/domain/zone/"+h.Zone+"/record", record, &response) if err != nil { log.Printf("Error creating DNS record: %v", err) http.Error(w, "Failed to create DNS record: "+err.Error(), http.StatusInternalServerError) return } // Refresh the zone err = h.refreshZone() if err != nil { log.Printf("Warning: zone refresh failed: %v", err) } log.Printf("Successfully created DNS record: %s.%s", response.SubDomain, h.Zone) w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) if err := json.NewEncoder(w).Encode(response); err != nil { log.Printf("Error encoding response: %v", err) http.Error(w, "Failed to encode response", http.StatusInternalServerError) return } } // DeleteDNSRecord handles the deletion of a DNS record func (h *DNSHandler) DeleteDNSRecord(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) recordID := vars["id"] log.Printf("Handling DELETE request for DNS record: %s from %s", recordID, r.RemoteAddr) err := config.OVHClient.Delete("/domain/zone/"+h.Zone+"/record/"+recordID, nil) if err != nil { log.Printf("Error deleting DNS record %s: %v", recordID, err) http.Error(w, "Failed to delete DNS record: "+err.Error(), http.StatusInternalServerError) return } // Refresh the zone err = h.refreshZone() if err != nil { log.Printf("Warning: zone refresh failed: %v", err) } log.Printf("Successfully deleted DNS record: %s", recordID) w.WriteHeader(http.StatusNoContent) } // refreshZone refreshes the DNS zone func (h *DNSHandler) refreshZone() error { return config.OVHClient.Post("/domain/zone/"+h.Zone+"/refresh", nil, nil) } // ListDNSRecords handles listing all A records in the DNS zone func (h *DNSHandler) ListDNSRecords(w http.ResponseWriter, r *http.Request) { log.Printf("Handling GET request for DNS records from %s", r.RemoteAddr) //log.Printf("Loading DNS Zone : %s", h.Zone) var recordIDs []int err := config.OVHClient.Get("/domain/zone/"+h.Zone+"/record", &recordIDs) if err != nil { log.Printf("Error getting DNS record IDs: %v", err) http.Error(w, "Failed to get DNS records: "+err.Error(), http.StatusInternalServerError) return } var records []DNSRecord for _, id := range recordIDs { var record DNSRecord err := config.OVHClient.Get(fmt.Sprintf("/domain/zone/%s/record/%d", h.Zone, id), &record) if err != nil { log.Printf("Error getting DNS record %d: %v", id, err) continue } // Only include A records if record.FieldType == "A" { records = append(records, record) } } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(records); err != nil { log.Printf("Error encoding response: %v", err) http.Error(w, "Failed to encode response", http.StatusInternalServerError) return } } // GetDNSRecordID retrieves the record ID for a specific subdomain func (h *DNSHandler) GetDNSRecordID(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r) subdomain := vars["subdomain"] log.Printf("Handling GET request for DNS record ID with subdomain: %s from %s", subdomain, r.RemoteAddr) // Get all record IDs var recordIDs []int err := config.OVHClient.Get("/domain/zone/"+h.Zone+"/record", &recordIDs) if err != nil { log.Printf("Error getting DNS record IDs: %v", err) http.Error(w, "Failed to get DNS records: "+err.Error(), http.StatusInternalServerError) return } // Search through records to find matching subdomain for _, id := range recordIDs { var record DNSRecord err := config.OVHClient.Get(fmt.Sprintf("/domain/zone/%s/record/%d", h.Zone, id), &record) if err != nil { log.Printf("Error getting DNS record %d: %v", id, err) continue } if record.FieldType == "A" && record.SubDomain == subdomain { response := struct { ID int `json:"id"` }{ ID: id, } w.Header().Set("Content-Type", "application/json") if err := json.NewEncoder(w).Encode(response); err != nil { log.Printf("Error encoding response: %v", err) http.Error(w, "Failed to encode response", http.StatusInternalServerError) return } return } } // If we get here, no matching record was found http.Error(w, "Record not found", http.StatusNotFound) }