dns_handler.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. package handlers
  2. import (
  3. "bufio"
  4. "byom-infra-api/config"
  5. "encoding/json"
  6. "fmt"
  7. "log"
  8. "math/rand"
  9. "net/http"
  10. "os"
  11. "regexp"
  12. "strings"
  13. "github.com/gorilla/mux"
  14. )
  15. type DNSRecord struct {
  16. FieldType string `json:"fieldType"`
  17. SubDomain string `json:"subDomain"`
  18. Target string `json:"target"`
  19. TTL int `json:"ttl"`
  20. }
  21. type DNSHandler struct {
  22. Zone string
  23. }
  24. func NewDNSHandler() *DNSHandler {
  25. zone, exists := config.GetConfig("dns_zone")
  26. log.Printf("DNS Handler initialized - zone = %s (exists: %v)\n", zone, exists)
  27. return &DNSHandler{
  28. Zone: zone,
  29. }
  30. }
  31. // generateRandomWord generates a random 6-letter word from /usr/share/dict/words
  32. func (h *DNSHandler) generateRandomWord() (string, error) {
  33. file, err := os.Open("/usr/share/dict/words")
  34. if err != nil {
  35. return "", err
  36. }
  37. defer file.Close()
  38. var words []string
  39. scanner := bufio.NewScanner(file)
  40. regex := regexp.MustCompile(`^[a-zA-Z]{6}$`)
  41. for scanner.Scan() {
  42. word := strings.ToLower(scanner.Text())
  43. if regex.MatchString(word) {
  44. words = append(words, word)
  45. }
  46. }
  47. if len(words) == 0 {
  48. return "", fmt.Errorf("no suitable words found")
  49. }
  50. return words[rand.Intn(len(words))], nil
  51. }
  52. // CreateDNSRecord handles the creation of a new DNS record
  53. func (h *DNSHandler) CreateDNSRecord(w http.ResponseWriter, r *http.Request) {
  54. log.Printf("Handling POST request for new DNS record from %s", r.RemoteAddr)
  55. // Parse request body for target IP
  56. var request struct {
  57. Target string `json:"target"`
  58. }
  59. if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
  60. log.Printf("Error decoding request body: %v", err)
  61. http.Error(w, "Invalid request body", http.StatusBadRequest)
  62. return
  63. }
  64. // Generate random subdomain
  65. word1, err := h.generateRandomWord()
  66. if err != nil {
  67. log.Printf("Error generating first word: %v", err)
  68. http.Error(w, "Failed to generate subdomain", http.StatusInternalServerError)
  69. return
  70. }
  71. word2, err := h.generateRandomWord()
  72. if err != nil {
  73. log.Printf("Error generating second word: %v", err)
  74. http.Error(w, "Failed to generate subdomain", http.StatusInternalServerError)
  75. return
  76. }
  77. // Create DNS record
  78. record := DNSRecord{
  79. FieldType: "A",
  80. SubDomain: word1 + "-" + word2,
  81. Target: request.Target,
  82. TTL: 0,
  83. }
  84. var response DNSRecord
  85. err = config.OVHClient.Post("/domain/zone/"+h.Zone+"/record", record, &response)
  86. if err != nil {
  87. log.Printf("Error creating DNS record: %v", err)
  88. http.Error(w, "Failed to create DNS record: "+err.Error(), http.StatusInternalServerError)
  89. return
  90. }
  91. // Refresh the zone
  92. err = h.refreshZone()
  93. if err != nil {
  94. log.Printf("Warning: zone refresh failed: %v", err)
  95. }
  96. log.Printf("Successfully created DNS record: %s.%s", response.SubDomain, h.Zone)
  97. w.Header().Set("Content-Type", "application/json")
  98. w.WriteHeader(http.StatusCreated)
  99. if err := json.NewEncoder(w).Encode(response); err != nil {
  100. log.Printf("Error encoding response: %v", err)
  101. http.Error(w, "Failed to encode response", http.StatusInternalServerError)
  102. return
  103. }
  104. }
  105. // DeleteDNSRecord handles the deletion of a DNS record
  106. func (h *DNSHandler) DeleteDNSRecord(w http.ResponseWriter, r *http.Request) {
  107. vars := mux.Vars(r)
  108. recordID := vars["id"]
  109. log.Printf("Handling DELETE request for DNS record: %s from %s", recordID, r.RemoteAddr)
  110. err := config.OVHClient.Delete("/domain/zone/"+h.Zone+"/record/"+recordID, nil)
  111. if err != nil {
  112. log.Printf("Error deleting DNS record %s: %v", recordID, err)
  113. http.Error(w, "Failed to delete DNS record: "+err.Error(), http.StatusInternalServerError)
  114. return
  115. }
  116. // Refresh the zone
  117. err = h.refreshZone()
  118. if err != nil {
  119. log.Printf("Warning: zone refresh failed: %v", err)
  120. }
  121. log.Printf("Successfully deleted DNS record: %s", recordID)
  122. w.WriteHeader(http.StatusNoContent)
  123. }
  124. // refreshZone refreshes the DNS zone
  125. func (h *DNSHandler) refreshZone() error {
  126. return config.OVHClient.Post("/domain/zone/"+h.Zone+"/refresh", nil, nil)
  127. }
  128. // ListDNSRecords handles listing all A records in the DNS zone
  129. func (h *DNSHandler) ListDNSRecords(w http.ResponseWriter, r *http.Request) {
  130. log.Printf("Handling GET request for DNS records from %s", r.RemoteAddr)
  131. //log.Printf("Loading DNS Zone : %s", h.Zone)
  132. var recordIDs []int
  133. err := config.OVHClient.Get("/domain/zone/"+h.Zone+"/record", &recordIDs)
  134. if err != nil {
  135. log.Printf("Error getting DNS record IDs: %v", err)
  136. http.Error(w, "Failed to get DNS records: "+err.Error(), http.StatusInternalServerError)
  137. return
  138. }
  139. var records []DNSRecord
  140. for _, id := range recordIDs {
  141. var record DNSRecord
  142. err := config.OVHClient.Get(fmt.Sprintf("/domain/zone/%s/record/%d", h.Zone, id), &record)
  143. if err != nil {
  144. log.Printf("Error getting DNS record %d: %v", id, err)
  145. continue
  146. }
  147. // Only include A records
  148. if record.FieldType == "A" {
  149. records = append(records, record)
  150. }
  151. }
  152. w.Header().Set("Content-Type", "application/json")
  153. if err := json.NewEncoder(w).Encode(records); err != nil {
  154. log.Printf("Error encoding response: %v", err)
  155. http.Error(w, "Failed to encode response", http.StatusInternalServerError)
  156. return
  157. }
  158. }
  159. // GetDNSRecordID retrieves the record ID for a specific subdomain
  160. func (h *DNSHandler) GetDNSRecordID(w http.ResponseWriter, r *http.Request) {
  161. vars := mux.Vars(r)
  162. subdomain := vars["subdomain"]
  163. log.Printf("Handling GET request for DNS record ID with subdomain: %s from %s", subdomain, r.RemoteAddr)
  164. // Get all record IDs
  165. var recordIDs []int
  166. err := config.OVHClient.Get("/domain/zone/"+h.Zone+"/record", &recordIDs)
  167. if err != nil {
  168. log.Printf("Error getting DNS record IDs: %v", err)
  169. http.Error(w, "Failed to get DNS records: "+err.Error(), http.StatusInternalServerError)
  170. return
  171. }
  172. // Search through records to find matching subdomain
  173. for _, id := range recordIDs {
  174. var record DNSRecord
  175. err := config.OVHClient.Get(fmt.Sprintf("/domain/zone/%s/record/%d", h.Zone, id), &record)
  176. if err != nil {
  177. log.Printf("Error getting DNS record %d: %v", id, err)
  178. continue
  179. }
  180. if record.FieldType == "A" && record.SubDomain == subdomain {
  181. response := struct {
  182. ID int `json:"id"`
  183. }{
  184. ID: id,
  185. }
  186. w.Header().Set("Content-Type", "application/json")
  187. if err := json.NewEncoder(w).Encode(response); err != nil {
  188. log.Printf("Error encoding response: %v", err)
  189. http.Error(w, "Failed to encode response", http.StatusInternalServerError)
  190. return
  191. }
  192. return
  193. }
  194. }
  195. // If we get here, no matching record was found
  196. http.Error(w, "Record not found", http.StatusNotFound)
  197. }