You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
3.2 KiB
Go
210 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"io/ioutil"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/go-yaml/yaml"
|
|
)
|
|
|
|
type conf struct {
|
|
Listen []addr
|
|
BlacklistFile string
|
|
HostFile string
|
|
ForwardRules []rule
|
|
DefaultUpstream []addr
|
|
Timeout int
|
|
Debug bool
|
|
blacklist item
|
|
hosts hostitem
|
|
}
|
|
|
|
type rule struct {
|
|
Server []addr
|
|
DomainFile string
|
|
domains item
|
|
}
|
|
|
|
type addr struct {
|
|
Host string
|
|
Port int
|
|
Network string
|
|
}
|
|
|
|
func loadConfig(f string) (*conf, error) {
|
|
c := new(conf)
|
|
data, err := ioutil.ReadFile(f)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := yaml.Unmarshal(data, c); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if c.blacklist == nil {
|
|
c.blacklist = item{}
|
|
}
|
|
|
|
if c.Timeout == 0 {
|
|
c.Timeout = 2
|
|
}
|
|
|
|
if err := loadItemFile(c.blacklist, c.BlacklistFile); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
for i := range c.ForwardRules {
|
|
if c.ForwardRules[i].domains == nil {
|
|
c.ForwardRules[i].domains = item{}
|
|
}
|
|
if err := loadItemFile(c.ForwardRules[i].domains,
|
|
c.ForwardRules[i].DomainFile); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if c.hosts == nil {
|
|
c.hosts = hostitem{}
|
|
}
|
|
|
|
if err := loadHostsFile(c.hosts, c.HostFile); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return c, nil
|
|
}
|
|
|
|
func loadHostsFile(h hostitem, f string) error {
|
|
if f == "" {
|
|
return nil
|
|
}
|
|
fd, err := os.Open(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fd.Close()
|
|
|
|
r := bufio.NewReader(fd)
|
|
for {
|
|
s, err := r.ReadString('\n')
|
|
if err != nil {
|
|
break
|
|
}
|
|
s1 := strings.Trim(s, " \t\r\n")
|
|
|
|
// ignore blank line and comment
|
|
if s1 == "" || s1[0] == '#' {
|
|
continue
|
|
}
|
|
s1 = strings.Replace(s1, "\t", " ", -1)
|
|
s1 = strings.Trim(s1, " \t\r\n")
|
|
ss := strings.Split(s1, " ")
|
|
|
|
// ipv4
|
|
t := 1
|
|
if strings.Index(ss[0], ":") != -1 {
|
|
// ipv6
|
|
t = 28
|
|
}
|
|
|
|
for _, s2 := range ss[1:] {
|
|
if s2 == "" {
|
|
continue
|
|
}
|
|
|
|
h.add(s2, ss[0], t)
|
|
}
|
|
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func loadItemFile(it item, f string) error {
|
|
if f == "" {
|
|
return nil
|
|
}
|
|
fd, err := os.Open(f)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer fd.Close()
|
|
|
|
r := bufio.NewReader(fd)
|
|
for {
|
|
s, err := r.ReadString('\n')
|
|
if s != "" {
|
|
s1 := strings.Trim(s, " \r\n")
|
|
if s1 != "" && s1[0] != '#' {
|
|
it.add(s1)
|
|
}
|
|
}
|
|
if err != nil {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
type item map[string]int
|
|
|
|
func (it item) has(s string) bool {
|
|
ss := strings.Split(s, ".")
|
|
|
|
for i := 0; i < len(ss); i++ {
|
|
s1 := strings.Join(ss[i:], ".")
|
|
if _, ok := it[s1]; ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (it item) exists(s string) bool {
|
|
_, ok := it[s]
|
|
return ok
|
|
}
|
|
|
|
func (it item) add(s string) {
|
|
it[s] = 1
|
|
}
|
|
|
|
type hostitem map[string][]hostentry
|
|
|
|
func (ht hostitem) get(domain string, t int) string {
|
|
if v, ok := ht[domain]; ok {
|
|
for _, v1 := range v {
|
|
if v1.domain == domain && v1.t == t {
|
|
return v1.ip
|
|
}
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func (ht hostitem) add(domain, ip string, t int) {
|
|
if v, ok := ht[domain]; ok {
|
|
exists := false
|
|
for _, v1 := range v {
|
|
if v1.domain == domain && v1.ip == ip && v1.t == t {
|
|
exists = true
|
|
break
|
|
}
|
|
}
|
|
if !exists {
|
|
ht[domain] = append(ht[domain], hostentry{domain, ip, t})
|
|
}
|
|
} else {
|
|
v1 := []hostentry{{domain, ip, t}}
|
|
ht[domain] = v1
|
|
}
|
|
}
|
|
|
|
type hostentry struct {
|
|
domain string
|
|
ip string
|
|
t int
|
|
}
|