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.
star/httpreverse/reverse.go

223 lines
5.7 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package httpreverse
import (
"b612.me/apps/b612/httpreverse/rp"
"b612.me/starlog"
"b612.me/staros/sysconf"
"bufio"
"errors"
"io"
"io/ioutil"
"net"
"net/http"
"net/url"
"os"
"strings"
"sync"
)
type ReverseConfig struct {
Name string
Addr string
ReverseURL map[string]*url.URL
Port int
UsingSSL bool
Key string
Cert string
Host string
SkipSSLVerify bool
InHeader [][2]string
OutHeader [][2]string
Cookie [][3]string //[3]string should contains path::key::value
ReplaceList [][2]string
ReplaceOnce bool
proxy map[string]*rp.ReverseProxy
IPFilterMode int //0=off 1=useremote 2=add 3=filter
FilterXForward bool
FilterRemoteAddr bool
FilterMustKey string
FilterSetKey string
FilterFile string
httpmux http.ServeMux
httpserver http.Server
CIDR []*net.IPNet
basicAuthUser string
basicAuthPwd string
protectAuthPage []string
blackip map[string]int
whiteip map[string]int
warningpage string
warnpagedata []byte
}
type HttpReverseServer struct {
Config []*ReverseConfig
}
func Parse(path string) (HttpReverseServer, error) {
var res HttpReverseServer
ini := sysconf.NewIni()
err := ini.ParseFromFile(path)
if err != nil {
return res, err
}
for _, v := range ini.Data {
var ins = ReverseConfig{
Name: v.Name,
Host: v.Get("host"),
Addr: v.Get("addr"),
Port: v.Int("port"),
UsingSSL: v.Bool("enablessl"),
Key: v.Get("key"),
Cert: v.Get("cert"),
ReplaceOnce: v.Bool("replaceonce"),
IPFilterMode: v.Int("ipfiltermode"),
FilterXForward: v.Bool("filterxforward"),
FilterRemoteAddr: v.Bool("filterremoteaddr"),
FilterMustKey: v.Get("filtermustkey"),
FilterSetKey: v.Get("filtersetkey"),
FilterFile: v.Get("filterfile"),
basicAuthUser: v.Get("authuser"),
basicAuthPwd: v.Get("authpasswd"),
warningpage: v.Get("warnpage"),
}
if ins.IPFilterMode == 3 && ins.FilterFile != "" {
starlog.Infoln("IP Filter Mode 3, Load IP Filter File", ins.FilterFile)
f, err := os.Open(ins.FilterFile)
if err != nil {
return res, err
}
buf := bufio.NewReader(f)
count := 0
for {
line, err := buf.ReadString('\n')
if err != nil {
if err == io.EOF {
f.Close()
break
}
f.Close()
return res, err
}
line = strings.TrimSpace(line)
if !strings.Contains(line, "/") {
line += "/32" //todo区分IPV6
}
_, cidr, err := net.ParseCIDR(line)
if err != nil {
return res, err
}
ins.CIDR = append(ins.CIDR, cidr)
count++
}
starlog.Infoln("Load", count, "CIDR")
}
if ins.warningpage != "" {
data, err := ioutil.ReadFile(ins.warningpage)
if err != nil {
return res, err
}
ins.warnpagedata = data
}
ins.proxy = make(map[string]*rp.ReverseProxy)
ins.ReverseURL = make(map[string]*url.URL)
for _, reverse := range v.GetAll("reverse") {
kv := strings.SplitN(reverse, "::", 2)
if len(kv) != 2 {
return res, errors.New("reverse settings not correct:" + reverse)
}
ins.ReverseURL[strings.TrimSpace(kv[0])], err = url.Parse(strings.TrimSpace(kv[1]))
if err != nil {
return res, err
}
}
for _, header := range v.GetAll("inheader") {
kv := strings.SplitN(header, "::", 2)
if len(kv) != 2 {
return res, errors.New("header settings not correct:" + header)
}
ins.InHeader = append(ins.InHeader, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
}
for _, authpage := range v.GetAll("authpage") {
ins.protectAuthPage = append(ins.protectAuthPage, authpage)
}
ins.blackip = make(map[string]int)
for _, blackip := range v.GetAll("blackip") {
ip, cidr, err := IPCIDR(blackip)
if err != nil {
return res, err
}
ins.blackip[ip] = cidr
}
ins.whiteip = make(map[string]int)
for _, whiteip := range v.GetAll("whiteip") {
ip, cidr, err := IPCIDR(whiteip)
if err != nil {
return res, err
}
ins.whiteip[ip] = cidr
}
for _, header := range v.GetAll("outheader") {
kv := strings.SplitN(header, "::", 2)
if len(kv) != 2 {
return res, errors.New("header settings not correct:" + header)
}
ins.OutHeader = append(ins.OutHeader, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
}
for _, cookie := range v.GetAll("cookie") {
kv := strings.SplitN(cookie, "::", 3)
if len(kv) != 3 {
return res, errors.New("cookie settings not correct:" + cookie)
}
ins.Cookie = append(ins.Cookie, [3]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]), strings.TrimSpace(kv[2])})
}
for _, replace := range v.GetAll("replace") {
kv := strings.SplitN(replace, "::", 2)
if len(kv) != 2 {
return res, errors.New("replace settings not correct:" + replace)
}
ins.ReplaceList = append(ins.ReplaceList, [2]string{strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1])})
}
res.Config = append(res.Config, &ins)
}
return res, nil
}
func (h *HttpReverseServer) Run() error {
var wg sync.WaitGroup
var mu sync.Mutex
var haveErr string
for _, v := range h.Config {
wg.Add(1)
go func(v *ReverseConfig) {
defer wg.Done()
err := v.Run()
if err != nil {
mu.Lock()
haveErr += err.Error() + "\n"
mu.Unlock()
}
}(v)
}
wg.Wait()
if haveErr != "" {
return errors.New(haveErr)
}
return nil
}
func (h *HttpReverseServer) Close() error {
var haveErr string
for _, v := range h.Config {
err := v.Close()
if err != nil {
haveErr += err.Error() + "\n"
}
}
if haveErr != "" {
return errors.New(haveErr)
}
return nil
}