go 实现的socks5服务器端

benZ · · 162 次点击 · 开始浏览    置顶
#####用go写的socks5服务器端代码: 1.默认只支持用户名密码的认证方式(username:abc , password:123),监听 tcp 8083 端口; 2.CMD支持TCP、UDP,不支持BIND,需要的可自行再实现; 3.支持ipv4、domain,目前不支持 ipv6; 4.建议客户端使用 proxycap, proxycap、SocksCap64 测试过能很好支持。(玩LOL,正常游戏)建议客户端不设置服务器端做DNS解析,效率相对会慢些; 5.注意服务端 防火墙 tcp 8083 端口开放及UDP协议全开放; 6.使用时有bug或问题欢迎留言; 7.想尝试用 go 写个简单的客户端,支持 lsp 劫持,配合服务器端做代理,不清楚有什么方法或 包 实现,请路过的大牛懂的请赐教! ##### 代码部分 //socks5协议文档: http://www.ietf.org/rfc/rfc1928.txt.pdf main.go: ``` package main import ( "bytes" "encoding/binary" "errors" "fmt" "log" "net" "sync" "time" ) var ( tcpConnId uint64 udpConnId uint64 ) type proxy struct { conn *net.TCPConn } type uServ struct { prefix string sigQuit chan bool clientUdpAddr *net.UDPAddr dstMap map[string]string sync.RWMutex } func main() { // Listen Tcp, support ipv4 only. @20171204 service := "0.0.0.0:8083" tcpAddr, err := net.ResolveTCPAddr("tcp", service) errCheckWithFatal("ResolveTcpAddr err:%v\n", err) tcpListener, err := net.ListenTCP("tcp", tcpAddr) errCheckWithFatal("ListenTCP err:%v\n", err) for { tcpConn, err := tcpListener.AcceptTCP() errCheckWithPrint("Warn >>> AcceptTCP err:%v\n", err) if err != nil { continue } proxy := NewProxy(tcpConn) go proxy.run() } } func NewProxy(conn *net.TCPConn) *proxy { return &proxy{ conn: conn, } } func (p *proxy) run() { defer p.conn.Close() remoteAddrStr := p.conn.RemoteAddr().String() // read buf := make([]byte, 1024) n, err := p.conn.Read(buf[0:]) errCheckWithPrint("Error >>> p.conn.Read err:%v\n", err) if err != nil { return } // get data bytes b := buf[0:n] //check Methods if !p.isMethodsOk(b) { log.Printf("WARN >>> from %s request methods invalid.\n", remoteAddrStr) return } n, err = p.conn.Read(buf[0:]) errCheckWithPrint("Error >>> p.conn.Read err:%v\n", err) if err != nil { return } b = buf[0:n] // check username/password if !p.isAuthOk(b) { log.Printf("WARN >>> from %s auth fail.\n", remoteAddrStr) return } // resolve proxy CMD(tcp or udp) /* +----+-----+-------+------+----------+--------+ |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | +----+-----+-------+------+----------+--------+ | 1 | 1 |X’00’| 1 | Variable | 2 | +----+-----+-------+------+----------+--------+ */ n, err = p.conn.Read(buf[0:]) errCheckWithPrint("Error >>> p.conn.Read err:%v\n", err) if err != nil { return } b = buf[0:n] if !p.isCmdSupport(b[0:2]) { return } if !p.isAtypSupport(b[3:4]) { return } dstAddr, dstPort := p.getAddrPort(b[3:]) if dstAddr == "" { log.Printf("WARN >>> get NULL dstAddr,return.\n") return } switch b[1] { case 0x01: //CONNECT -> TCP p.tcpProxy(dstAddr, dstPort) return case 0x02: log.Println("WARN >>> get BIND command, not support.") case 0x03: //UDP p.udpProxy(dstAddr, dstPort) return default: return } } func (p *proxy) isMethodsOk(b []byte) bool { // username/password only. /* +----+----------+----------+ |VER | NMETHODS | METHODS | +----+----------+----------+ | 1 | 1 | 1 to 255 | +----+----------+----------+ */ if b[0] != 0x05 || (b[1] == 0x01 && b[2] != 0x02) { p.conn.Write([]byte{0x05, 0xff}) return false } p.conn.Write([]byte{0x05, 0x02}) return true } func (p *proxy) isAuthOk(b []byte) bool { //0x01 | 用户名长度(1字节)| 用户名(长度根据用户名长度域指定) | 口令长度(1字节) | 口令(长度由口令长度域指定) // b[0] some software use 0x01,but base protocal must use 0x05,so keeps it here. b0 := b[0] nameLens := int(b[1]) name := string(b[2 : 2+nameLens]) passLens := int(b[2+nameLens]) pass := string(b[2+nameLens+1 : 2+nameLens+1+passLens]) //auth lens /* if nameLens != 32 || passLens != 48 { p.conn.Write([]byte{b0, 0xff}) return false } */ // auth count // test auth here. if name != "abc" || pass != "123" { p.conn.Write([]byte{b0, 0xff}) return false } p.conn.Write([]byte{b0, 0x00}) return true } func (p *proxy) isCmdSupport(b []byte) bool { if b[0] != 0x05 { p.conn.Write([]byte{0x05, 0x01}) return false } switch b[1] { case 0x01: //CONNECT return true case 0x02: //BIND //not support here. p.conn.Write([]byte{0x05, 0x07}) return false case 0x03: //UDP return true default: p.conn.Write([]byte{0x05, 0x07}) return false } } func (p *proxy) isAtypSupport(b []byte) bool { switch b[0] { case 0x01: //ipv4 return true case 0x03: //domain return true case 0x04: //ipv6 p.conn.Write([]byte{0x05, 0x08}) return false default: p.conn.Write([]byte{0x05, 0x08}) return false } } func (p *proxy) getAddrPort(b []byte) (string, int) { switch b[0] { case 0x01: //ipv4 addr := net.IPv4(b[1], b[2], b[3], b[4]).String() port := int(b[5])*256 + int(b[6]) return addr, port case 0x03: //domain domainLens := int(b[1]) domain := string(b[2 : 2+domainLens]) port := int(b[2+domainLens])*256 + int(b[2+domainLens+1]) return domain, port default: return "", 0 } } func (p *proxy) tcpProxy(dstAddr string, dstPort int) { addr := fmt.Sprintf("%s:%d", dstAddr, dstPort) tcpAddr, err := net.ResolveTCPAddr("tcp", addr) errCheckWithPrint("Error >>> net.ResolveTcpAddr err:%v\n", err) if err != nil { p.conn.Write([]byte{0x05, 0x03}) return } tcpConn, err := TcpDial(nil, tcpAddr, 60*time.Second) format := fmt.Sprintf("Error >>> net.DialTCP %s err", addr) errCheckWithPrint(format+":%v\n", err) if err != nil { p.conn.Write([]byte{0x05, 0x03}) return } defer tcpConn.Close() time.Sleep(10 * 1e6) p.conn.SetNoDelay(true) tcpConn.SetNoDelay(true) p.conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) tcpConnId++ prefix := fmt.Sprintf("> tcp: %08x", tcpConnId) closeSig := make(chan bool, 0) go pipe("%s >>> %d bytes send\n", prefix, p.conn, tcpConn, closeSig) go pipe("%s <<< %d bytes recieve\n", prefix, tcpConn, p.conn, closeSig) <-closeSig return } func (p *proxy) udpProxy(clientAddr string, clientPort int) { //log.Printf("get client bind -> %s:%d\n", clientAddr, clientPort) // get localIp from p.conn localExitAddr, err := net.ResolveTCPAddr("tcp", p.conn.LocalAddr().String()) errCheckWithFatal("ResolveTCPAddr err:%v\n", err) localExitIP := localExitAddr.IP.String() service := fmt.Sprintf("%s:%d", localExitIP, 0) udpAddr, err := net.ResolveUDPAddr("udp", service) errCheckWithFatal("ResolveUDPAddr err:%v\n", err) udpListener, err := net.ListenUDP("udp", udpAddr) errCheckWithFatal("ListenUDP err:%v\n", err) defer udpListener.Close() //udpListener.SetPKTINFO() bindUdpAddr, err := net.ResolveUDPAddr("udp", udpListener.LocalAddr().String()) errCheckWithFatal("ResolveUDPAddr err:%v\n", err) bindPort := bindUdpAddr.Port //版本 | 代理的应答 | 保留1字节 | 地址类型 | 代理服务器地址 | 绑定的代理端口 bindMsg := []byte{0x05, 0x00, 0x00, 0x01} buffer := bytes.NewBuffer(bindMsg) binary.Write(buffer, binary.BigEndian, localExitAddr.IP.To4()) binary.Write(buffer, binary.BigEndian, uint16(bindPort)) //log.Printf("Local udp BIND >>> %s:%d\n", localExitIP, bindPort) //log.Printf("Send udp BIND Msg >>> %v\n", buffer.Bytes()) p.conn.Write(buffer.Bytes()) finish := make(chan bool, 0) go udpKeepAlive(p.conn, finish) go udpServ(udpListener, finish) <-finish return } func errCheckWithFatal(format string, err error) { if err != nil { log.Fatalf("Fatal >>>"+format, err) } return } func errCheckWithPrint(format string, err error) { if err != nil { log.Printf(format, err) } return } func TcpDial(localAddr, remoteAddr *net.TCPAddr, timeout time.Duration) (*net.TCPConn, error) { returned := false ticker := time.NewTicker(timeout) defer ticker.Stop() type rst struct { tcn *net.TCPConn error } rst_chan := make(chan *rst, 0) go func() { tcpConn, err := net.DialTCP("tcp", localAddr, remoteAddr) if err != nil { goto Finish } else if returned { tcpConn.Close() return } Finish: rst_chan <- &rst{tcn: tcpConn, error: err} }() select { case <-ticker.C: returned = true return nil, errors.New("connect timeout") case result := <-rst_chan: if result.error != nil { return nil, result.error } return result.tcn, nil } } func pipe(format, prefix string, src, dst *net.TCPConn, closeSig chan bool) { buf := make([]byte, 0xff) for { n, err := src.Read(buf[0:]) if err != nil { closeSig <- true return } b := buf[0:n] _, err = dst.Write(b) if err != nil { closeSig <- true return } //log.Printf(format, prefix, n) } } func udpKeepAlive(tcpConn *net.TCPConn, finish chan<- bool) { tcpConn.SetKeepAlive(true) buf := make([]byte, 1024) for { _, err := tcpConn.Read(buf[0:]) if err != nil { finish <- true return } } } func udpServ(udpConn *net.UDPConn, finish chan<- bool) { udpConnId++ udpServ := &uServ{ prefix: fmt.Sprintf("udp >>> #%08x", udpConnId), sigQuit: make(chan bool, 0), dstMap: make(map[string]string), } go udpServ.read(udpConn) udpServ.monitor() finish <- true return } func (uS *uServ) monitor() { for { select { case <-uS.sigQuit: return } } } func (uS *uServ) read(udpConn *net.UDPConn) { buf := make([]byte, 2048) buf2 := make([]byte, 2048) for { n, udpAddr, err := udpConn.ReadFromUDP(buf[0:]) if err != nil { //log.Printf("%s ReadFromUDP err:%v\n", uS.prefix, err) uS.sigQuit <- true return } if uS.clientUdpAddr == nil { uS.clientUdpAddr = udpAddr } b := buf[0:n] if udpAddr.IP.String() == uS.clientUdpAddr.IP.String() { // from client /* +----+------+------+----------+----------+----------+ |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | +----+------+------+----------+----------+----------+ | 2 | 1 | 1 | Variable | 2 | Variable | +----+------+------+----------+----------+----------+ */ if b[2] != 0x00 { log.Printf("%s WARN: FRAG do not support.\n", uS.prefix) continue } switch b[3] { case 0x01: //ipv4 dstAddr := &net.UDPAddr{ IP: net.IPv4(b[4], b[5], b[6], b[7]), Port: int(b[8])*256 + int(b[9]), } uS.Lock() if _, exist := uS.dstMap[dstAddr.String()]; !exist { uS.dstMap[dstAddr.String()] = string(b[0:10]) } uS.Unlock() udpConn.WriteToUDP(b[10:], dstAddr) //log.Printf("%s b-> %v\n", uS.prefix, b) //log.Printf("%s data-> %v\n", uS.prefix, b[10:]) case 0x03: //domain domainLens := int(b[4]) domain := string(b[5 : 5+domainLens]) ipAddr, err := net.ResolveIPAddr("ip", domain) if err != nil { log.Printf("%s Error -> domain %s dns query err:%v\n", uS.prefix, domain, err) continue } dstAddr := &net.UDPAddr{ IP: ipAddr.IP, Port: int(b[5+domainLens])*256 + int(b[6+domainLens]), } uS.Lock() if _, exist := uS.dstMap[dstAddr.String()]; !exist { uS.dstMap[dstAddr.String()] = string(b[0 : 7+domainLens]) } uS.Unlock() udpConn.WriteToUDP(b[7+domainLens:], dstAddr) //log.Printf("%s b-> %v\n", uS.prefix, b) //log.Printf("%s data-> %v\n", uS.prefix, b[7+domainLens:]) default: log.Printf("%s WARN: ATYP %v do not support.\n", uS.prefix, b[3]) continue } } else { // from dst Server uS.RLock() if v, exist := uS.dstMap[udpAddr.String()]; exist { uS.RUnlock() head := []byte(v) headLens := len(head) copy(buf2[0:], head[0:headLens]) copy(buf2[headLens:], b[0:]) sendData := buf2[0 : headLens+n] udpConn.WriteToUDP(sendData, uS.clientUdpAddr) //log.Printf("%s <<< head-> %v\n", uS.prefix, head) //log.Printf("%s <<< b-> %v\n", uS.prefix, b) //log.Printf("%s <<< data-> %v\n", uS.prefix, sendData) } else { fmt.Printf("%s WARN -> %s not in dstMap.\n", uS.prefix, udpAddr.String()) uS.RUnlock() continue } } } uS.sigQuit <- true return } ```
162 次点击  
加入收藏 微博
2 回复  |  直到 2017-12-07 17:58:50
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传