main.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package main
  2. import (
  3. "bufio"
  4. "errors"
  5. "flag"
  6. "fmt"
  7. "io/ioutil"
  8. "log"
  9. "net"
  10. "net/http"
  11. "os"
  12. "strings"
  13. )
  14. var (
  15. lists = flag.String("lists", "/etc/blacklists", "Blacklist sources")
  16. invalid = []string{
  17. "localhost", "localhost.localdomain", "local", "broadcasthost",
  18. "ip6-localhost", "ip6-loopback", "ip6-localnet",
  19. "ip6-mcastprefix", "ip6-allnodes", "ip6-allrouters",
  20. "ip6-allhosts",
  21. }
  22. soa = `$TTL 1
  23. @ IN SOA localhost. root.localhost. (
  24. 0 ; serial
  25. 2w ; refresh
  26. 2w ; retry
  27. 2w ; expiry
  28. 2w ) ; negative cache ttl
  29. @ IN NS localhost.
  30. @ IN A 0.0.0.0
  31. `
  32. )
  33. type Server struct {
  34. Lists []string
  35. }
  36. func main() {
  37. flag.Parse()
  38. s := &Server{ readLists(), }
  39. s.Serve()
  40. }
  41. func readLists() []string {
  42. file, err := os.Open(*lists)
  43. if err != nil {
  44. return nil
  45. }
  46. defer file.Close()
  47. var sources []string
  48. scanner := bufio.NewScanner(file)
  49. for scanner.Scan() {
  50. sources = append(sources, scanner.Text())
  51. }
  52. return sources
  53. }
  54. func (s *Server) Serve() {
  55. http.HandleFunc("/", s.generateList)
  56. log.Fatal(http.ListenAndServe(":8000", nil))
  57. }
  58. func (s *Server) generateList(w http.ResponseWriter, r *http.Request) {
  59. c := make(chan string)
  60. for _, list := range s.Lists {
  61. go handle(list, c)
  62. }
  63. var zones []string
  64. for range s.Lists {
  65. zones = append(zones, <-c)
  66. }
  67. fmt.Fprintf(w, soa)
  68. fmt.Fprintf(w, strings.Join(zones, "\n"))
  69. }
  70. func handle(url string, zone chan string) {
  71. body, err := fetch(url)
  72. if err != nil {
  73. return
  74. }
  75. zone <- sanitize(body)
  76. }
  77. func fetch(url string) (string, error) {
  78. req, err := http.Get(url)
  79. if err != nil {
  80. return "", err
  81. }
  82. defer req.Body.Close()
  83. body, err := ioutil.ReadAll(req.Body)
  84. if err != nil {
  85. return "", err
  86. }
  87. return string(body), nil
  88. }
  89. func sanitize(records string) string {
  90. var sanitized []string
  91. for _, line := range strings.Split(records, "\n") {
  92. _, domain, err := parse_record(line)
  93. if err != nil {
  94. continue
  95. }
  96. // Domain is invalid (e.g., "localhost")
  97. if is_invalid(domain) {
  98. continue
  99. }
  100. // Domain is an IP address
  101. if net.ParseIP(domain) != nil {
  102. continue
  103. }
  104. sanitized = append(sanitized, domain + ".\tCNAME\t.\n")
  105. }
  106. return strings.Join(sanitized, "")
  107. }
  108. func parse_record(record string) (string, string, error) {
  109. if len(record) == 0 {
  110. return "", "", errors.New("Empty line")
  111. }
  112. if strings.HasPrefix(record, "#") {
  113. return "", "", errors.New("Comment")
  114. }
  115. words := strings.Fields(record)
  116. // Domain-only list
  117. if len(words) == 1 {
  118. return "", words[0], nil
  119. }
  120. return words[0], words[1], nil
  121. }
  122. func is_invalid(domain string) bool {
  123. for _, i := range invalid {
  124. if domain == i {
  125. return true
  126. }
  127. }
  128. return false
  129. }