mirror of
https://github.com/zhigang1992/flora-kit.git
synced 2026-01-12 09:04:04 +08:00
加入 GeoIP 的函数;
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,3 +23,4 @@ _testmain.go
|
||||
*.test
|
||||
*.prof
|
||||
flora-kit
|
||||
.DS_Store
|
||||
|
||||
8
Makefile
Normal file
8
Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
install:
|
||||
@go get
|
||||
build:
|
||||
@go build main.go
|
||||
run:
|
||||
@go run main.go
|
||||
test:
|
||||
@go test ./flora
|
||||
34
README.md
34
README.md
@@ -1,2 +1,36 @@
|
||||
# flora-kit
|
||||
Flora Proxy server
|
||||
|
||||
## 网络调整命令
|
||||
|
||||
列出所有网络链接方式
|
||||
|
||||
```
|
||||
$ networksetup -listallnetworkservices
|
||||
```
|
||||
|
||||
并循环设置
|
||||
|
||||
获取某个连接的 Socks Proxy 配置
|
||||
|
||||
```
|
||||
$ networksetup -setsocksfirewallproxy <networkservice> <domain> <port number> <authenticated> <username> <password>
|
||||
```
|
||||
|
||||
获取某个连接的 HTTP Procx 配置
|
||||
|
||||
```
|
||||
$ networksetup -setwebproxy <networkservice> <domain> <port number> <authenticated> <username> <password>
|
||||
```
|
||||
|
||||
获取某个连接的 HTTPS Proxy 配置
|
||||
|
||||
```
|
||||
$ networksetup -setsecurewebproxy <networkservice> <domain> <port number> <authenticated> <username> <password>
|
||||
```
|
||||
|
||||
设置 Proxy ByPass
|
||||
|
||||
```
|
||||
$ networksetup -setproxybypassdomains <networkservice> <domain1> [domain2] [...]
|
||||
```
|
||||
112
flora/config.go
112
flora/config.go
@@ -5,11 +5,14 @@ import (
|
||||
"github.com/go-ini/ini"
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
SOCKS_PORT = 7657
|
||||
RULE_REJECT = 0
|
||||
RULE_DIRECT = 1
|
||||
RULE_PROXY = 2
|
||||
@@ -25,12 +28,20 @@ var rulePrefixDomains = []*DomainRule{}
|
||||
var ruleKeywordDomains = []*DomainRule{}
|
||||
var ruleGeoIP = &DomainRule{}
|
||||
|
||||
var Config ss.Config
|
||||
var ssConfig ss.Config
|
||||
var iniConfig *ini.File
|
||||
|
||||
func init() {
|
||||
Config.LocalPort = 7657
|
||||
type ProxyServerCipher struct {
|
||||
Server string
|
||||
Cipher *ss.Cipher
|
||||
}
|
||||
|
||||
var ProxyServers struct {
|
||||
SrvCipher []*ProxyServerCipher
|
||||
FailCnt []int // failed connection count
|
||||
}
|
||||
|
||||
func init() {
|
||||
var configFilename = "/Users/jason/Dropbox/Surge/Default.conf"
|
||||
var userConfigFilename = "/Users/jason/Dropbox/Surge/User.conf"
|
||||
var iniOpts = ini.LoadOptions{
|
||||
@@ -54,30 +65,33 @@ func init() {
|
||||
|
||||
// [Proxy] Section
|
||||
func loadProxy() {
|
||||
ssConfig.LocalPort = SOCKS_PORT
|
||||
for _, key := range iniConfig.Section("Proxy").Keys() {
|
||||
var proxys = readArrayLine(key.String())
|
||||
// ShadowSocks Proxys
|
||||
if proxys[0] == "custom" || proxys[0] == "shadowsocks" {
|
||||
var server = strings.Join(proxys[1:3], ":")
|
||||
var serverInfo = []string{server, proxys[4], proxys[3]}
|
||||
Config.ServerPassword = append(Config.ServerPassword, serverInfo)
|
||||
ssConfig.ServerPassword = append(ssConfig.ServerPassword, serverInfo)
|
||||
}
|
||||
}
|
||||
|
||||
if Config.Method == "" {
|
||||
Config.Method = "aes-256-cfb"
|
||||
if ssConfig.Method == "" {
|
||||
ssConfig.Method = "aes-256-cfb"
|
||||
}
|
||||
if len(Config.ServerPassword) == 0 {
|
||||
if !enoughSSOptions(&Config) {
|
||||
if len(ssConfig.ServerPassword) == 0 {
|
||||
if !enoughSSOptions(&ssConfig) {
|
||||
fmt.Fprintln(os.Stderr, "must specify server address, password and both server/local port")
|
||||
os.Exit(1)
|
||||
}
|
||||
} else {
|
||||
if Config.LocalPort == 0 {
|
||||
if ssConfig.LocalPort == 0 {
|
||||
fmt.Fprintln(os.Stderr, "must specify local port")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
parseServerConfig()
|
||||
}
|
||||
|
||||
// 载入 [Rule]
|
||||
@@ -119,9 +133,7 @@ func enoughSSOptions(config *ss.Config) bool {
|
||||
func readArrayLine(source string) []string {
|
||||
out := strings.Split(source, ",")
|
||||
for i, str := range out {
|
||||
out[i] = strings.Trim(str, " ")
|
||||
out[i] = strings.Trim(str, "\t")
|
||||
out[i] = strings.Trim(str, "\n")
|
||||
out[i] = strings.TrimSpace(str)
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -146,3 +158,79 @@ func RuleOfHost(host string) *DomainRule {
|
||||
}
|
||||
return &DomainRule{S: "", T: RULE_DIRECT}
|
||||
}
|
||||
|
||||
func parseServerConfig() {
|
||||
config := ssConfig
|
||||
hasPort := func(s string) bool {
|
||||
_, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return port != ""
|
||||
}
|
||||
|
||||
if len(config.ServerPassword) == 0 {
|
||||
method := config.Method
|
||||
if config.Auth {
|
||||
method += "-auth"
|
||||
}
|
||||
// only one encryption table
|
||||
cipher, err := ss.NewCipher(method, config.Password)
|
||||
if err != nil {
|
||||
log.Fatal("Failed generating ciphers:", err)
|
||||
}
|
||||
srvPort := strconv.Itoa(config.ServerPort)
|
||||
srvArr := config.GetServerArray()
|
||||
n := len(srvArr)
|
||||
ProxyServers.SrvCipher = make([]*ProxyServerCipher, n)
|
||||
|
||||
for i, s := range srvArr {
|
||||
if hasPort(s) {
|
||||
log.Println("ignore server_port option for server", s)
|
||||
ProxyServers.SrvCipher[i] = &ProxyServerCipher{s, cipher}
|
||||
} else {
|
||||
ProxyServers.SrvCipher[i] = &ProxyServerCipher{net.JoinHostPort(s, srvPort), cipher}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// multiple servers
|
||||
n := len(config.ServerPassword)
|
||||
ProxyServers.SrvCipher = make([]*ProxyServerCipher, n)
|
||||
|
||||
cipherCache := make(map[string]*ss.Cipher)
|
||||
i := 0
|
||||
for _, serverInfo := range config.ServerPassword {
|
||||
if len(serverInfo) < 2 || len(serverInfo) > 3 {
|
||||
log.Fatalf("server %v syntax error\n", serverInfo)
|
||||
}
|
||||
server := serverInfo[0]
|
||||
passwd := serverInfo[1]
|
||||
encmethod := ""
|
||||
if len(serverInfo) == 3 {
|
||||
encmethod = serverInfo[2]
|
||||
}
|
||||
if !hasPort(server) {
|
||||
log.Fatalf("no port for server %s\n", server)
|
||||
}
|
||||
// Using "|" as delimiter is safe here, since no encryption
|
||||
// method contains it in the name.
|
||||
cacheKey := encmethod + "|" + passwd
|
||||
cipher, ok := cipherCache[cacheKey]
|
||||
if !ok {
|
||||
var err error
|
||||
cipher, err = ss.NewCipher(encmethod, passwd)
|
||||
if err != nil {
|
||||
log.Fatal("Failed generating ciphers:", err)
|
||||
}
|
||||
cipherCache[cacheKey] = cipher
|
||||
}
|
||||
ProxyServers.SrvCipher[i] = &ProxyServerCipher{server, cipher}
|
||||
i++
|
||||
}
|
||||
}
|
||||
ProxyServers.FailCnt = make([]int, len(ProxyServers.SrvCipher))
|
||||
for _, se := range ProxyServers.SrvCipher {
|
||||
log.Println("available remote server", se.Server)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
29
flora/geoip.go
Normal file
29
flora/geoip.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package flora
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/oschwald/geoip2-golang"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var geoDB *geoip2.Reader
|
||||
|
||||
func init() {
|
||||
file := "./geoip.mmdb"
|
||||
db, err := geoip2.Open(file)
|
||||
defer db.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Could not open GeoIP database\n")
|
||||
}
|
||||
geoDB = db
|
||||
}
|
||||
|
||||
func GeoIP(ipaddr string) string {
|
||||
ip := net.ParseIP(ipaddr)
|
||||
country, err := geoDB.Country(ip)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.ToLower(country.Country.IsoCode)
|
||||
}
|
||||
BIN
geoip.mmdb
Normal file
BIN
geoip.mmdb
Normal file
Binary file not shown.
20
geoip_test.go
Normal file
20
geoip_test.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"github.com/huacnlee/flora-kit/flora"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestGeoIP(t *testing.T) {
|
||||
if flora.GeoIP("121.0.29.91") != "cn" {
|
||||
t.Errorf("121.0.29.91 should be cn")
|
||||
}
|
||||
|
||||
if flora.GeoIP("218.253.0.89") != "hk" {
|
||||
t.Errorf("218.253.0.89 should be hk")
|
||||
}
|
||||
|
||||
if flora.GeoIP("218.176.242.11") != "jp" {
|
||||
t.Errorf("218.176.242.11 should be jp")
|
||||
}
|
||||
}
|
||||
112
main.go
112
main.go
@@ -3,6 +3,8 @@ package main
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/huacnlee/flora-kit/flora"
|
||||
"io"
|
||||
"log"
|
||||
"math/rand"
|
||||
@@ -10,8 +12,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/huacnlee/flora-kit/flora"
|
||||
|
||||
ss "github.com/shadowsocks/shadowsocks-go/shadowsocks"
|
||||
)
|
||||
|
||||
@@ -149,104 +149,19 @@ func getRequest(conn net.Conn) (rawaddr []byte, host string, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
type ServerCipher struct {
|
||||
server string
|
||||
cipher *ss.Cipher
|
||||
}
|
||||
|
||||
var servers struct {
|
||||
srvCipher []*ServerCipher
|
||||
failCnt []int // failed connection count
|
||||
}
|
||||
|
||||
func parseServerConfig(config *ss.Config) {
|
||||
hasPort := func(s string) bool {
|
||||
_, port, err := net.SplitHostPort(s)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return port != ""
|
||||
}
|
||||
|
||||
if len(config.ServerPassword) == 0 {
|
||||
method := config.Method
|
||||
if config.Auth {
|
||||
method += "-auth"
|
||||
}
|
||||
// only one encryption table
|
||||
cipher, err := ss.NewCipher(method, config.Password)
|
||||
if err != nil {
|
||||
log.Fatal("Failed generating ciphers:", err)
|
||||
}
|
||||
srvPort := strconv.Itoa(config.ServerPort)
|
||||
srvArr := config.GetServerArray()
|
||||
n := len(srvArr)
|
||||
servers.srvCipher = make([]*ServerCipher, n)
|
||||
|
||||
for i, s := range srvArr {
|
||||
if hasPort(s) {
|
||||
log.Println("ignore server_port option for server", s)
|
||||
servers.srvCipher[i] = &ServerCipher{s, cipher}
|
||||
} else {
|
||||
servers.srvCipher[i] = &ServerCipher{net.JoinHostPort(s, srvPort), cipher}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// multiple servers
|
||||
n := len(config.ServerPassword)
|
||||
servers.srvCipher = make([]*ServerCipher, n)
|
||||
|
||||
cipherCache := make(map[string]*ss.Cipher)
|
||||
i := 0
|
||||
for _, serverInfo := range config.ServerPassword {
|
||||
if len(serverInfo) < 2 || len(serverInfo) > 3 {
|
||||
log.Fatalf("server %v syntax error\n", serverInfo)
|
||||
}
|
||||
server := serverInfo[0]
|
||||
passwd := serverInfo[1]
|
||||
encmethod := ""
|
||||
if len(serverInfo) == 3 {
|
||||
encmethod = serverInfo[2]
|
||||
}
|
||||
if !hasPort(server) {
|
||||
log.Fatalf("no port for server %s\n", server)
|
||||
}
|
||||
// Using "|" as delimiter is safe here, since no encryption
|
||||
// method contains it in the name.
|
||||
cacheKey := encmethod + "|" + passwd
|
||||
cipher, ok := cipherCache[cacheKey]
|
||||
if !ok {
|
||||
var err error
|
||||
cipher, err = ss.NewCipher(encmethod, passwd)
|
||||
if err != nil {
|
||||
log.Fatal("Failed generating ciphers:", err)
|
||||
}
|
||||
cipherCache[cacheKey] = cipher
|
||||
}
|
||||
servers.srvCipher[i] = &ServerCipher{server, cipher}
|
||||
i++
|
||||
}
|
||||
}
|
||||
servers.failCnt = make([]int, len(servers.srvCipher))
|
||||
for _, se := range servers.srvCipher {
|
||||
log.Println("available remote server", se.server)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func connectToServer(serverId int, rawaddr []byte, addr string) (remote *ss.Conn, err error) {
|
||||
se := servers.srvCipher[serverId]
|
||||
remote, err = ss.DialWithRawAddr(rawaddr, se.server, se.cipher.Copy())
|
||||
se := flora.ProxyServers.SrvCipher[serverId]
|
||||
remote, err = ss.DialWithRawAddr(rawaddr, se.Server, se.Cipher.Copy())
|
||||
if err != nil {
|
||||
log.Println("error connecting to shadowsocks server:", err)
|
||||
const maxFailCnt = 30
|
||||
if servers.failCnt[serverId] < maxFailCnt {
|
||||
servers.failCnt[serverId]++
|
||||
if flora.ProxyServers.FailCnt[serverId] < maxFailCnt {
|
||||
flora.ProxyServers.FailCnt[serverId]++
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
debug.Printf("connected to %s via %s\n", addr, se.server)
|
||||
servers.failCnt[serverId] = 0
|
||||
debug.Printf("connected to %s via %s\n", addr, se.Server)
|
||||
flora.ProxyServers.FailCnt[serverId] = 0
|
||||
return
|
||||
}
|
||||
|
||||
@@ -256,11 +171,11 @@ func connectToServer(serverId int, rawaddr []byte, addr string) (remote *ss.Conn
|
||||
// servers.
|
||||
func createServerConn(rawaddr []byte, addr string) (remote *ss.Conn, err error) {
|
||||
const baseFailCnt = 20
|
||||
n := len(servers.srvCipher)
|
||||
n := len(flora.ProxyServers.SrvCipher)
|
||||
skipped := make([]int, 0)
|
||||
for i := 0; i < n; i++ {
|
||||
// skip failed server, but try it with some probability
|
||||
if servers.failCnt[i] > 0 && rand.Intn(servers.failCnt[i]+baseFailCnt) != 0 {
|
||||
if flora.ProxyServers.FailCnt[i] > 0 && rand.Intn(flora.ProxyServers.FailCnt[i]+baseFailCnt) != 0 {
|
||||
skipped = append(skipped, i)
|
||||
continue
|
||||
}
|
||||
@@ -311,7 +226,7 @@ func handleConnection(conn net.Conn) {
|
||||
|
||||
remote, err := createServerConn(rawaddr, addr)
|
||||
if err != nil {
|
||||
if len(servers.srvCipher) > 1 {
|
||||
if len(flora.ProxyServers.SrvCipher) > 1 {
|
||||
log.Println("Failed connect to all avaiable shadowsocks server")
|
||||
}
|
||||
return
|
||||
@@ -346,8 +261,5 @@ func run(listenAddr string) {
|
||||
|
||||
func main() {
|
||||
ss.SetDebug(true)
|
||||
|
||||
parseServerConfig(&flora.Config)
|
||||
|
||||
run("0.0.0.0:" + strconv.Itoa(flora.Config.LocalPort))
|
||||
run("0.0.0.0:" + fmt.Sprintf("%d", flora.SOCKS_PORT))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user