variable iface socks5 server
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

235 lines
5.3 KiB

3 years ago
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)
}
}