main.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. package main
  2. import (
  3. "errors"
  4. "fmt"
  5. "io/ioutil"
  6. "net"
  7. "net/http"
  8. "strings"
  9. )
  10. var (
  11. lists = []string{
  12. "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts",
  13. "https://mirror1.malwaredomains.com/files/justdomains",
  14. "http://sysctl.org/cameleon/hosts",
  15. "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt",
  16. "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt",
  17. "https://hosts-file.net/ad_servers.txt",
  18. }
  19. invalid = []string{
  20. "localhost", "localhost.localdomain", "local", "broadcasthost",
  21. "ip6-localhost", "ip6-loopback", "ip6-localnet",
  22. "ip6-mcastprefix", "ip6-allnodes", "ip6-allrouters",
  23. "ip6-allhosts",
  24. }
  25. soa = `$TTL 1
  26. @ IN SOA localhost. root.localhost. (
  27. 0 ; serial
  28. 2w ; refresh
  29. 2w ; retry
  30. 2w ; expiry
  31. 2w ) ; negative cache ttl
  32. @ IN NS localhost.
  33. @ IN A 0.0.0.0
  34. `
  35. )
  36. func main() {
  37. http.HandleFunc("/", generateList)
  38. http.ListenAndServe(":8000", nil)
  39. }
  40. func generateList(w http.ResponseWriter, r *http.Request) {
  41. c := make(chan string)
  42. for _, list := range lists {
  43. go handle(list, c)
  44. }
  45. var zones []string
  46. for range lists {
  47. zones = append(zones, <-c)
  48. }
  49. fmt.Fprintf(w, soa)
  50. fmt.Fprintf(w, strings.Join(zones, "\n"))
  51. }
  52. func handle(url string, zone chan string) {
  53. body, err := fetch(url)
  54. if err != nil {
  55. return
  56. }
  57. zone <- sanitize(body)
  58. }
  59. func fetch(url string) (string, error) {
  60. req, err := http.Get(url)
  61. if err != nil {
  62. return "", err
  63. }
  64. defer req.Body.Close()
  65. body, err := ioutil.ReadAll(req.Body)
  66. if err != nil {
  67. return "", err
  68. }
  69. return string(body), nil
  70. }
  71. func sanitize(records string) string {
  72. var sanitized []string
  73. for _, line := range strings.Split(records, "\n") {
  74. _, domain, err := parse_record(line)
  75. if err != nil {
  76. continue
  77. }
  78. // Domain is invalid (e.g., "localhost")
  79. if is_invalid(domain) {
  80. continue
  81. }
  82. // Domain is an IP address
  83. if net.ParseIP(domain) != nil {
  84. continue
  85. }
  86. sanitized = append(sanitized, domain + ".\tCNAME\t.\n")
  87. }
  88. return strings.Join(sanitized, "")
  89. }
  90. func parse_record(record string) (string, string, error) {
  91. if len(record) == 0 {
  92. return "", "", errors.New("Empty line")
  93. }
  94. if strings.HasPrefix(record, "#") {
  95. return "", "", errors.New("Comment")
  96. }
  97. words := strings.Fields(record)
  98. // Domain-only list
  99. if len(words) == 1 {
  100. return "", words[0], nil
  101. }
  102. return words[0], words[1], nil
  103. }
  104. func is_invalid(domain string) bool {
  105. for _, i := range invalid {
  106. if domain == i {
  107. return true
  108. }
  109. }
  110. return false
  111. }