package main import ( "bufio" "flag" "fmt" "github.com/armon/go-socks5" "golang.org/x/net/context" "log" "net" "os" "regexp" "strconv" "strings" "time" ) // GetFdFromConn get net.Conn's file descriptor. //func GetFdFromConn(l net.Conn) int { // v := reflect.ValueOf(l) // netFD := reflect.Indirect(reflect.Indirect(v).FieldByName("fd")) // fd := int(netFD.FieldByName("sysfd").Int()) // return fd //} // //func GetFdFromConn(l net.Conn) int { // v := reflect.ValueOf(l) // netFD := reflect.Indirect(reflect.Indirect(v).FieldByName("fd")) // pfd := reflect.Indirect(netFD.FieldByName("pfd")) // fd := int(pfd.FieldByName("Sysfd").Int()) // return fd //} // Dialer . type Dialer struct { ifname string laddrIP string err error dialer *net.Dialer } // DialFromInterface . func DialFromInterface(ifaceName string) *Dialer { d := &Dialer{ifname: ifaceName} // Lookup rquested interface. iface, err := net.InterfaceByName(ifaceName) if err != nil { d.err = err return d } // Pull the addresses. addres, err := iface.Addrs() if err != nil { d.err = err return d } // Look for the first usable address. var targetIP string for _, addr := range addres { ip, _, err := net.ParseCIDR(addr.String()) if err != nil { d.err = err return d } if ip.IsUnspecified() { continue } if ip.To4().Equal(ip) { targetIP = ip.String() break } else { targetIP = "[" + ip.String() + "]" } } if targetIP == "" { d.err = fmt.Errorf("no ipv4 found for interface") return d } d.laddrIP = targetIP return d } func (d *Dialer) lookupAddr(network, addr string) (net.Addr, error) { if d.err != nil { return nil, d.err } // If no custom dialer specified, use default one. if d.dialer == nil { d.dialer = &net.Dialer{} } // Resolve the address. switch network { case "tcp", "tcp4", "tcp6": addr, err := net.ResolveTCPAddr(network, d.laddrIP+":0") return addr, err case "udp", "udp4", "udp6": addr, err := net.ResolveUDPAddr(network, d.laddrIP+":0") return addr, err default: return nil, fmt.Errorf("unkown network") } } // Dial . func (d *Dialer) Dial(network, addr string) (net.Conn, error) { laddr, err := d.lookupAddr(network, addr) if err != nil { return nil, err } d.dialer.LocalAddr = laddr conn, err := d.dialer.Dial(network, addr) //syscall.BindToDevice(GetFdFromConn(conn), d.ifname) return conn, err } // DialTimeout . func (d *Dialer) DialTimeout(network, addr string, timeout time.Duration) (net.Conn, error) { laddr, err := d.lookupAddr(network, addr) if err != nil { return nil, err } d.dialer.Timeout = timeout d.dialer.LocalAddr = laddr return d.dialer.Dial(network, addr) } // WithDialer . func (d *Dialer) WithDialer(dialer net.Dialer) *Dialer { d.dialer = &dialer return d } var dialer *Dialer func dial(ctx context.Context, network, addr string) (net.Conn, error) { conn, err := dialer.Dial(network, addr) return conn, err } func main() { var ( proxyPort uint proxyUser string proxyPassword string proxyUsersFile string interfaceName string ) flag.UintVar(&proxyPort, "port", 1080, "custom port") flag.StringVar(&proxyUser, "login", "q", "login name, needs if you are too lazy to creat a file which contains user data") flag.StringVar(&proxyPassword, "password", "q", "password, please read about login name") flag.StringVar(&proxyUsersFile, "users", "", "a file which include a list of users, in which has set one user per line, every line include USERNAME:PASSWORD separated by colons") flag.StringVar(&interfaceName, "iface", "", "run at specific interface") flag.Parse() //Initialize socks5 config socsk5conf := &socks5.Config{ Logger: log.New(os.Stdout, "", log.LstdFlags), } if interfaceName != "" { dialer = DialFromInterface(interfaceName) socsk5conf.Dial = dial } //creds := socks5.StaticCredentials{ // proxyUser: proxyPassword, // "dsa": "dsa", //} //var creds socks5.CredentialStore userData := make(map[string]string) if (proxyUser != "") != (proxyPassword != "") { log.Fatal("Specify password and login name just together a time") } if proxyUser != "" && proxyPassword != "" { userData[proxyUser] = proxyPassword } // reads users from file if proxyUsersFile != "" { file, err := os.Open(proxyUsersFile) if err != nil { log.Print(err) log.Print("I cannot read the file, I ignore it.") } r := bufio.NewReader(file) for { line, _, err := r.ReadLine() if err != nil { break } data := strings.Split(string(line), ":") if len(data) != 2 { log.Print("line \"" + string(line) + "\" is invalid") break } correct, err := regexp.MatchString("[A-z0-9_]+", data[0]) if !correct || err != nil { log.Print("error at parsing line: " + string(line)) break } correct, err = regexp.MatchString("[A-z0-9_]+", data[1]) if !correct || err != nil { log.Print("error at parsing line: " + string(line)) break } // all right userData[data[0]] = data[1] } } cator := socks5.UserPassAuthenticator{Credentials: socks5.StaticCredentials(userData)} socsk5conf.AuthMethods = []socks5.Authenticator{cator} server, err := socks5.New(socsk5conf) if err != nil { log.Fatal(err) } log.Printf("Start listening proxy service on port %d\n", proxyPort) if err := server.ListenAndServe("tcp", ":"+strconv.Itoa(int(proxyPort))); err != nil { log.Fatal(err) } }