// step3-4 func (s socks5Handler) readRequest() (string, bool) { var ( buf = make([]byte, 262) // 4+(1+255)+2 host string ofs int ver, cmd, atyp byte ) var msg = []byte{5, 0, 0, 1, 0, 0, 0, 0, 0, 0} setRTimeout(s.conn) _, err := s.conn.Read(buf) if err != nil { exception.Spawn(&err, "socks: read request") goto errLogging } ver, cmd, atyp = buf[0], buf[1], buf[3] if ver != S5_VER || cmd != 1 { exception.Spawn(&err, "socks: invalid request") goto errHandler } buf = buf[4:] switch atyp { case IPV4: host = net.IP(buf[:net.IPv4len]).String() ofs = net.IPv4len case IPV6: host = "[" + net.IP(buf[:net.IPv6len]).String() + "]" ofs = net.IPv6len case DOMAIN: dlen := int(buf[0]) ofs = dlen + 1 host = string(buf[1:ofs]) // literal IPv6 if strings.Count(host, ":") >= 2 && !strings.HasPrefix(host, "[") { host = "[" + host + "]" } default: exception.Spawn(&err, "socks: invalid request") goto errHandler } // accept _, err = s.conn.Write(msg) if err != nil { exception.Spawn(&err, "socks: write response") goto errLogging } host += ":" + strconv.Itoa(int(binary.BigEndian.Uint16(buf[ofs:]))) return host, true errHandler: msg[1] = 0x1 // general SOCKS server failure setWTimeout(s.conn) s.conn.Write(msg) errLogging: log.Warningln(err) return NULL, false }
func (n *d5sman) authenticate(conn *Conn, session *Session) error { var err error setRTimeout(conn) hashSRand, err := ReadFullByLen(1, conn) if err != nil { // client aborted if IsClosedError(err) { return ABORTED_ERROR.Apply(err) } else { return exception.Spawn(&err, "srand: read connection") } } myHashSRand := hash256(n.sRand) if !bytes.Equal(hashSRand, myHashSRand) { // MITM ? return INCONSISTENT_HASH } // client identity setRTimeout(conn) idBuf, err := ReadFullByLen(1, conn) if err != nil { return exception.Spawn(&err, "auth: read connection") } user, passwd, err := n.deserializeIdentity(idBuf) if err != nil { return err } if log.V(log.LV_LOGIN) { log.Infoln("Login request:", user) } pass, err := n.AuthSys.Authenticate(user, passwd) if !pass { // authSys denied log.Warningf("Auth %s:%s failed: %v\n", user, passwd, err) // reply failed msg conn.Write([]byte{1, 0}) return VALIDATION_FAILED } session.indentifySession(user, conn) w := newMsgWriter() w.WriteL1Msg([]byte{AUTH_PASS}) w.WriteL2Msg(n.tunParams.serialize()) // send tokens num := maxInt(GENERATE_TOKEN_NUM, n.Parallels+2) tokens := n.sessionMgr.createTokens(session, num) w.WriteL2Msg(tokens[1:]) // skip index=0 setWTimeout(conn) err = w.WriteTo(conn) return exception.Spawn(&err, "setting: write connection") }
// finish DHE // 1, dhPub, dhSign, rand // 2, hashHello, version func (n *d5sman) finishDHExchange(conn *Conn) (cf *CipherFactory, err error) { var dhPub, key []byte dhKey, _ := crypto.NewDHKey(DH_METHOD) setRTimeout(conn) dhPub, err = ReadFullByLen(2, conn) if err != nil { exception.Spawn(&err, "dh: read connection") return } w := newMsgWriter() myDhPub := dhKey.ExportPubKey() w.WriteL1Msg(myDhPub) myDhSign := DSASign(n.privateKey, myDhPub) w.WriteL1Msg(myDhSign) n.sRand = randMinArray() w.WriteL1Msg(n.sRand) setWTimeout(conn) err = w.WriteTo(conn) if err != nil { exception.Spawn(&err, "dh: write connection") return } key, err = dhKey.ComputeKey(dhPub) if err != nil { exception.Spawn(&err, "dh: compute") return } // setup cipher cf = NewCipherFactory(n.Cipher, key, n.dbcHello) conn.SetupCipher(cf, n.sRand) // encrypted w.WriteL1Msg(hash256(n.dbcHello)) w.WriteL1Msg(ito4b(VERSION)) setWTimeout(conn) err = w.WriteTo(conn) if err != nil { exception.Spawn(&err, "em: write connection") return } return }
// step1-2 func (s socks5Handler) handshake() bool { var buf = make([]byte, 2) var n, nmethods int var ver byte setRTimeout(s.conn) _, err := io.ReadFull(s.conn, buf) if err != nil { exception.Spawn(&err, "socks: read header") goto errLogging } ver, nmethods = buf[0], int(buf[1]) if ver != S5_VER || nmethods < 1 { err = INVALID_SOCKS5_HEADER exception.Spawn(&err, "socks: read header [% x]", buf[:2]) goto errHandler } buf = make([]byte, nmethods+1) // consider method non-00 setRTimeout(s.conn) n, err = io.ReadAtLeast(s.conn, buf, nmethods) if err != nil || n != nmethods { err = INVALID_SOCKS5_HEADER exception.Spawn(&err, "socks: read header [% x]", buf) goto errHandler } // accept buf = []byte{5, 0} setWTimeout(s.conn) _, err = s.conn.Write(buf) if err == nil { return true } else { err = exception.Spawn(&err, "socks: write response") goto errLogging } errHandler: // handshake error feedback // NO ACCEPTABLE METHODS buf = []byte{5, 0xff} setWTimeout(s.conn) s.conn.Write(buf) errLogging: log.Warningln(err) return false }
// report hashRand0 then request authentication // get tun params and tokens func (n *d5cman) authThenFinishSetting(conn *Conn, t *tunParams) error { var err error w := newMsgWriter() // hash sRand w.WriteL1Msg(hash256(n.sRand)) // identity w.WriteL1Msg(n.serializeIdentity()) setWTimeout(conn) err = w.WriteTo(conn) if err != nil { return exception.Spawn(&err, "auth: write connection") } setRTimeout(conn) var buf, params []byte buf, err = ReadFullByLen(1, conn) if err != nil { return exception.Spawn(&err, "auth: read connection") } // auth_result switch buf[0] { case AUTH_PASS: default: return auth.AUTH_FAILED } // parse params params, err = ReadFullByLen(2, conn) if err != nil { return exception.Spawn(&err, "param: read connection") } t.deserialize(params) t.token, err = ReadFullByLen(2, conn) if err != nil { return exception.Spawn(&err, "token: read connection") } if len(t.token) < TKSZ || len(t.token)%TKSZ != 0 { return ILLEGAL_STATE.Apply("incorrect token") } if log.V(log.LV_TOKEN) { log.Infof("Received tokens size=%d\n", len(t.token)/TKSZ) } return nil }
// read dhPub from server and verify sign // dhPubLen~1 | dhPub~? | signLen~1 | sign~? | rand func (n *d5cman) finishDHExchange(conn *Conn) (cf *CipherFactory, err error) { var dhk, dhkSign []byte // recv: rhPub~2+256 or ecdhPub~2+32 setRTimeout(conn) dhk, err = ReadFullByLen(1, conn) if err != nil { // maybe: closed conn or reset by peer error caused by dbcHello if IsClosedError(err) { return nil, ERR_TIME_ERROR.Apply(NULL) } else { exception.Spawn(&err, "dh: read response") return } } setRTimeout(conn) dhkSign, err = ReadFullByLen(1, conn) if err != nil { exception.Spawn(&err, "dh: read sign") return } if !DSAVerify(n.sPubKey, dhkSign, dhk) { // MITM ? return nil, VALIDATION_FAILED } key, err := n.dhKey.ComputeKey(dhk) if err != nil { exception.Spawn(&err, "dh: compute") return } n.sRand, err = ReadFullByLen(1, conn) if err != nil { exception.Spawn(&err, "srand: read connection") return } // setup cipher cf = NewCipherFactory(n.cipher, key, n.dbcHello) conn.SetupCipher(cf, n.sRand) return }
// verify encrypted message // hashHello, version func (n *d5cman) validate(conn *Conn) error { setRTimeout(conn) hashHello, err := ReadFullByLen(1, conn) if err != nil { return exception.Spawn(&err, "validate: read connection") } myHashHello := hash256(n.dbcHello) if !bytes.Equal(hashHello, myHashHello) { // MITM ? return INCONSISTENT_HASH } ver, err := ReadFullByLen(1, conn) if err != nil { return exception.Spawn(&err, "ver: read connection") } if err = compareVersion(ver); err != nil { return err } return nil }
func (n *d5cman) ResumeSession(p *tunParams, token []byte) (conn *Conn, err error) { var rawConn net.Conn rawConn, err = net.DialTimeout("tcp", n.sAddr, GENERAL_SO_TIMEOUT) if err != nil { exception.Spawn(&err, "resume: connnecting") return } conn = NewConn(rawConn, nullCipherKit) obf := makeDbcHello(TYPE_RES, preSharedKey(n.sPubKey)) w := newMsgWriter() w.WriteMsg(obf) w.WriteMsg(token) err = w.WriteTo(conn) if err != nil { exception.Spawn(&err, "resume: write") return } conn.SetupCipher(p.cipherFactory, token) conn.SetId(n.provider, false) return conn, nil }
// 1-send dbcHello,dhPub // dbcHello~256 | dhPubLen~2 | dhPub~? func (n *d5cman) requestDHExchange(conn *Conn) (err error) { // obfuscated header obf := makeDbcHello(TYPE_NEW, preSharedKey(n.sPubKey)) w := newMsgWriter().WriteMsg(obf) if len(obf) > DPH_P2 { n.dbcHello = obf[DPH_P2:] } else { n.dbcHello = obf } // dhke pub := n.dhKey.ExportPubKey() w.WriteL2Msg(pub) setWTimeout(conn) err = w.WriteTo(conn) exception.Spawn(&err, "dh: write connection") return }