dns_handler.go 6.4 KB

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