diff --git a/client.go b/client.go index 85d2c03..f7516d1 100644 --- a/client.go +++ b/client.go @@ -18,7 +18,7 @@ const ( func NewClient(addr string, server string, target string, timeout int, key int, tcpmode int, tcpmode_buffersize int, tcpmode_maxwin int, tcpmode_resend_timems int, tcpmode_compress int, - tcpmode_stat int) (*Client, error) { + tcpmode_stat int, open_sock5 int) (*Client, error) { var ipaddr *net.UDPAddr var tcpaddr *net.TCPAddr @@ -58,6 +58,7 @@ func NewClient(addr string, server string, target string, timeout int, key int, tcpmode_resend_timems: tcpmode_resend_timems, tcpmode_compress: tcpmode_compress, tcpmode_stat: tcpmode_stat, + open_sock5: open_sock5, }, nil } @@ -75,6 +76,7 @@ type Client struct { tcpmode_resend_timems int tcpmode_compress int tcpmode_stat int + open_sock5 int ipaddr *net.UDPAddr tcpaddr *net.TCPAddr @@ -201,13 +203,17 @@ func (p *Client) AcceptTcp() error { } if conn != nil { - go p.AcceptTcpConn(conn) + if p.open_sock5 > 0 { + go p.AcceptSock5Conn(conn) + } else { + go p.AcceptTcpConn(conn, p.targetAddr) + } } } } -func (p *Client) AcceptTcpConn(conn *net.TCPConn) { +func (p *Client) AcceptTcpConn(conn *net.TCPConn, targetAddr string) { uuid := UniqueId() tcpsrcaddr := conn.RemoteAddr().(*net.TCPAddr) @@ -234,7 +240,7 @@ func (p *Client) AcceptTcpConn(conn *net.TCPConn) { f := e.Value.(*Frame) mb, _ := proto.Marshal(f) p.sequence++ - sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, + sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, p.tcpmode_buffersize, p.tcpmode_maxwin, p.tcpmode_resend_timems, p.tcpmode_compress, p.tcpmode_stat, p.timeout) @@ -294,7 +300,7 @@ func (p *Client) AcceptTcpConn(conn *net.TCPConn) { continue } p.sequence++ - sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, + sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, 0, 0, 0, 0, 0, 0) @@ -355,7 +361,7 @@ func (p *Client) AcceptTcpConn(conn *net.TCPConn) { f := e.Value.(*Frame) mb, _ := proto.Marshal(f) p.sequence++ - sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, + sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, targetAddr, clientConn.id, (uint32)(MyMsg_DATA), mb, SEND_PROTO, RECV_PROTO, p.key, p.tcpmode, 0, 0, 0, 0, 0, 0) @@ -530,7 +536,7 @@ func (p *Client) ping() { if p.sendPacket == 0 { now := time.Now() b, _ := now.MarshalBinary() - sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, p.targetAddr, "", (uint32)(MyMsg_PING), b, + sendICMP(p.id, p.sequence, *p.conn, p.ipaddrServer, "", "", (uint32)(MyMsg_PING), b, SEND_PROTO, RECV_PROTO, p.key, 0, 0, 0, 0, 0, 0, 0) @@ -547,3 +553,32 @@ func (p *Client) showNet() { p.sendPacketSize = 0 p.recvPacketSize = 0 } + +func (p *Client) AcceptSock5Conn(conn *net.TCPConn) { + + var err error = nil + if err = sock5Handshake(conn); err != nil { + loggo.Error("socks handshake: %s", err) + conn.Close() + return + } + _, addr, err := sock5GetRequest(conn) + if err != nil { + loggo.Error("error getting request: %s", err) + conn.Close() + return + } + // Sending connection established message immediately to client. + // This some round trip time for creating socks connection with the client. + // But if connection failed, the client will get connection reset error. + _, err = conn.Write([]byte{0x05, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x08, 0x43}) + if err != nil { + loggo.Error("send connection confirmation:", err) + conn.Close() + return + } + + loggo.Info("accept new sock5 conn: %s", addr) + + p.AcceptTcpConn(conn, addr) +} diff --git a/cmd/main.go b/cmd/main.go index fc1f070..5044413 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -14,10 +14,18 @@ var usage = ` Usage: + // server pingtunnel -type server + // client, Forward udp + pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 + + // client, Forward tcp pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -t SERVER_IP:4455 -tcp 1 + // client, Forward sock5, implicitly open tcp, so no target server is needed + pingtunnel -type client -l LOCAL_IP:4455 -s SERVER_IP -sock5 1 + -type 服务器或者客户端 client or server @@ -59,6 +67,9 @@ Usage: -loglevel 日志文件等级,默认info log level, default is info + + -sock5 开启sock5转发,默认0 + Turn on sock5 forwarding, default 0 is off ` func main() { @@ -77,16 +88,30 @@ func main() { nolog := flag.Int("nolog", 0, "write log file") tcpmode_stat := flag.Int("tcp_stat", 0, "print tcp stat") loglevel := flag.String("loglevel", "info", "log level") + open_sock5 := flag.Int("sock5", 0, "sock5 mode") flag.Usage = func() { fmt.Printf(usage) } flag.Parse() - if (*t != "client" && *t != "server") || (*t == "client" && (len(*listen) == 0 || len(*target) == 0 || len(*server) == 0)) { + if *t != "client" && *t != "server" { flag.Usage() return } + if *t == "client" { + if len(*listen) == 0 || len(*server) == 0 { + flag.Usage() + return + } + if *open_sock5 == 0 && len(*target) == 0 { + flag.Usage() + return + } + if *open_sock5 != 0 { + *tcpmode = 1 + } + } if *tcpmode_maxwin*10 > pingtunnel.FRAME_MAX_ID { fmt.Println("set tcp win to big, max = " + strconv.Itoa(pingtunnel.FRAME_MAX_ID/10)) return @@ -129,7 +154,7 @@ func main() { c, err := pingtunnel.NewClient(*listen, *server, *target, *timeout, *key, *tcpmode, *tcpmode_buffersize, *tcpmode_maxwin, *tcpmode_resend_timems, *tcpmode_compress, - *tcpmode_stat) + *tcpmode_stat, *open_sock5) if err != nil { loggo.Error("ERROR: %s", err.Error()) return diff --git a/sock5.go b/sock5.go new file mode 100644 index 0000000..dc474e5 --- /dev/null +++ b/sock5.go @@ -0,0 +1,136 @@ +package pingtunnel + +import ( + "encoding/binary" + "errors" + "io" + "net" + "strconv" + "time" +) + +var ( + errAddrType = errors.New("socks addr type not supported") + errVer = errors.New("socks version not supported") + errMethod = errors.New("socks only support 1 method now") + errAuthExtraData = errors.New("socks authentication get extra data") + errReqExtraData = errors.New("socks request get extra data") + errCmd = errors.New("socks command not supported") +) + +const ( + socksVer5 = 5 + socksCmdConnect = 1 +) + +func sock5Handshake(conn net.Conn) (err error) { + const ( + idVer = 0 + idNmethod = 1 + ) + // version identification and method selection message in theory can have + // at most 256 methods, plus version and nmethod field in total 258 bytes + // the current rfc defines only 3 authentication methods (plus 2 reserved), + // so it won't be such long in practice + + buf := make([]byte, 258) + + var n int + conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) + // make sure we get the nmethod field + if n, err = io.ReadAtLeast(conn, buf, idNmethod+1); err != nil { + return + } + if buf[idVer] != socksVer5 { + return errVer + } + nmethod := int(buf[idNmethod]) + msgLen := nmethod + 2 + if n == msgLen { // handshake done, common case + // do nothing, jump directly to send confirmation + } else if n < msgLen { // has more methods to read, rare case + if _, err = io.ReadFull(conn, buf[n:msgLen]); err != nil { + return + } + } else { // error, should not get extra data + return errAuthExtraData + } + // send confirmation: version 5, no authentication required + _, err = conn.Write([]byte{socksVer5, 0}) + return +} + +func sock5GetRequest(conn net.Conn) (rawaddr []byte, host string, err error) { + const ( + idVer = 0 + idCmd = 1 + idType = 3 // address type index + idIP0 = 4 // ip address start index + idDmLen = 4 // domain address length index + idDm0 = 5 // domain address start index + + typeIPv4 = 1 // type is ipv4 address + typeDm = 3 // type is domain address + typeIPv6 = 4 // type is ipv6 address + + lenIPv4 = 3 + 1 + net.IPv4len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv4 + 2port + lenIPv6 = 3 + 1 + net.IPv6len + 2 // 3(ver+cmd+rsv) + 1addrType + ipv6 + 2port + lenDmBase = 3 + 1 + 1 + 2 // 3 + 1addrType + 1addrLen + 2port, plus addrLen + ) + // refer to getRequest in server.go for why set buffer size to 263 + buf := make([]byte, 263) + var n int + conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)) + // read till we get possible domain length field + if n, err = io.ReadAtLeast(conn, buf, idDmLen+1); err != nil { + return + } + // check version and cmd + if buf[idVer] != socksVer5 { + err = errVer + return + } + if buf[idCmd] != socksCmdConnect { + err = errCmd + return + } + + reqLen := -1 + switch buf[idType] { + case typeIPv4: + reqLen = lenIPv4 + case typeIPv6: + reqLen = lenIPv6 + case typeDm: + reqLen = int(buf[idDmLen]) + lenDmBase + default: + err = errAddrType + return + } + + if n == reqLen { + // common case, do nothing + } else if n < reqLen { // rare case + if _, err = io.ReadFull(conn, buf[n:reqLen]); err != nil { + return + } + } else { + err = errReqExtraData + return + } + + rawaddr = buf[idType:reqLen] + + switch buf[idType] { + case typeIPv4: + host = net.IP(buf[idIP0 : idIP0+net.IPv4len]).String() + case typeIPv6: + host = net.IP(buf[idIP0 : idIP0+net.IPv6len]).String() + case typeDm: + host = string(buf[idDm0 : idDm0+buf[idDmLen]]) + } + port := binary.BigEndian.Uint16(buf[reqLen-2 : reqLen]) + host = net.JoinHostPort(host, strconv.Itoa(int(port))) + + return +}