加入 GeoIP 的函数;

This commit is contained in:
Jason Lee
2016-11-15 19:25:25 +08:00
parent c4c3c3fa90
commit bb54a0e14d
8 changed files with 204 additions and 112 deletions

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ _testmain.go
*.test
*.prof
flora-kit
.DS_Store

8
Makefile Normal file
View File

@@ -0,0 +1,8 @@
install:
@go get
build:
@go build main.go
run:
@go run main.go
test:
@go test ./flora

View File

@@ -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] [...]
```

View File

@@ -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
View 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

Binary file not shown.

20
geoip_test.go Normal file
View 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
View File

@@ -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))
}