func (c *Client) startDataTun(again bool) { var connected bool defer func() { if connected { atomic.AddInt32(&c.dtCnt, -1) } if err := recover(); err != nil { log.Errorf("DTun failed to connect(%s). Retry after %s\n", err, RETRY_INTERVAL) c.eventHandler(evt_dt_closed, true) if DEBUG { ex.CatchException(err) } } }() if again { time.Sleep(RETRY_INTERVAL) } for { if atomic.LoadInt32(&c.State) == 0 { conn := c.createDataTun() connected = true if log.V(1) { log.Infof("DTun(%s) is established\n", conn.sign()) } atomic.AddInt32(&c.dtCnt, 1) c.mux.Listen(conn, c.eventHandler, c.tp.dtInterval) log.Errorf("DTun(%s) was disconnected. Reconnect after %s\n", conn.sign(), RETRY_INTERVAL) break } else { c.pendingSema.acquire(RETRY_INTERVAL) } } }
// |------------- tun params ------------| // | len~2 | version~4 | interval~2 | reserved~? | tokens~20N ; hash~20 func (nego *d5SNegotiation) respondTestWithToken(sconn *hashedConn, session *Session) (err error) { var headLen = TUN_PARAMS_LEN + 2 // tun params tpBuf := randArray(headLen, headLen) binary.BigEndian.PutUint16(tpBuf, uint16(TUN_PARAMS_LEN+GENERATE_TOKEN_NUM*TKSZ)) ofs := 2 copy(tpBuf[ofs:], ito4b(VERSION)) ofs += 4 binary.BigEndian.PutUint16(tpBuf[ofs:], uint16(CTL_PING_INTERVAL)) ofs += 2 binary.BigEndian.PutUint16(tpBuf[ofs:], uint16(DT_PING_INTERVAL)) ofs += 2 tpBuf[ofs] = PARALLEL_TUN_QTY _, err = sconn.Write(tpBuf) ThrowErr(err) tokens := nego.sessionMgr.createTokens(session, GENERATE_TOKEN_NUM) _, err = sconn.Write(tokens) ThrowErr(err) rHash := sconn.RHashSum() wHash := sconn.WHashSum() oHash := make([]byte, TKSZ) _, err = sconn.Read(oHash) ThrowErr(err) if !bytes.Equal(wHash, oHash) { log.Errorln("Remote hash/r not equals self/w") log.Errorf("rHash: [% x] wHash: [% x]\n", rHash, wHash) log.Errorf("oHash: [% x]\n", oHash) return INCONSISTENT_HASH } _, err = sconn.Write(rHash) ThrowErr(err) return }
func (p *multiplexer) connectToDest(frm *frame, key string, tun *Conn) { defer func() { ex.CatchException(recover()) }() var ( dstConn net.Conn err error target = string(frm.data) ) dstConn, err = net.DialTimeout("tcp", target, GENERAL_SO_TIMEOUT) frm.length = 0 if err != nil { p.router.removePreRegistered(key) log.Errorf("Cannot connect to [%s] for %s error: %s\n", target, key, err) frm.action = FRAME_ACTION_OPEN_N tunWrite2(tun, frm) } else { if log.V(1) { log.Infoln("OPEN", target, "for", key) } dstConn.SetReadDeadline(ZERO_TIME) edge := p.router.register(key, target, tun, dstConn, false) // write edge frm.action = FRAME_ACTION_OPEN_Y if tunWrite2(tun, frm) == nil { p.relay(edge, tun, frm.sid) // read edge } else { // send open_y failed SafeClose(tun) } } }
// return header=1 + TKSZ*many func (s *SessionMgr) createTokens(session *Session, many int) []byte { s.lock.Lock() defer s.lock.Unlock() var ( tokens = make([]byte, 1+many*TKSZ) i64buf = make([]byte, 8) _tokens = tokens[1:] sha = sha1.New() ) rand.Seed(time.Now().UnixNano()) sha.Write([]byte(session.uid)) for i := 0; i < many; i++ { binary.BigEndian.PutUint64(i64buf, uint64(rand.Int63())) sha.Write(i64buf) binary.BigEndian.PutUint64(i64buf, uint64(time.Now().UnixNano())) sha.Write(i64buf) pos := i * TKSZ sha.Sum(_tokens[pos:pos]) token := _tokens[pos : pos+TKSZ] key := fmt.Sprintf("%x", token) if _, y := s.container[key]; y { i-- continue } s.container[key] = session session.tokens[key] = true } if log.V(4) { log.Errorf("sessionMap created=%d len=%d\n", many, len(s.container)) } return tokens }
func (nego *d5CNegotiation) validateAndGetTokens(sconn *hashedConn, t *tunParams) (err error) { buf, err := ReadFullByLen(2, sconn) ThrowErr(err) tVer := VERSION oVer := binary.BigEndian.Uint32(buf) if oVer > tVer { oVerStr := fmt.Sprintf("%d.%d.%04d", oVer>>24, (oVer>>16)&0xFF, oVer&0xFFFF) tVer >>= 16 oVer >>= 16 if tVer == oVer { log.Warningf("Caution !!! Please upgrade to new version, remote is v%s\n", oVerStr) } else { return INCOMPATIBLE_VERSION.Apply(oVerStr) } } ofs := 4 t.stInterval = int(binary.BigEndian.Uint16(buf[ofs:])) ofs += 2 t.dtInterval = int(binary.BigEndian.Uint16(buf[ofs:])) ofs += 2 t.tunQty = int(buf[ofs]) t.token = buf[TUN_PARAMS_LEN:] if log.V(2) { n := len(buf) - TUN_PARAMS_LEN log.Infof("Got tokens length=%d\n", n/TKSZ) } rHash := sconn.RHashSum() wHash := sconn.WHashSum() _, err = sconn.Write(rHash) ThrowErr(err) oHash := make([]byte, TKSZ) _, err = sconn.Read(oHash) if !bytes.Equal(wHash, oHash) { log.Errorln("Server hash/r is inconsistence with the client/w") log.Errorf("rHash: [% x] wHash: [% x]\n", rHash, wHash) log.Errorf("oHash: [% x]\n", oHash) return INCONSISTENT_HASH } return }
func (p *multiplexer) relay(edge *edgeConn, tun *Conn, sid uint16) { var ( buf = make([]byte, FRAME_MAX_LEN) nr int er error code byte src = edge.conn ) defer func() { if edge.closed&TCP_CLOSE_R == 0 { // only positively _frame(buf, FRAME_ACTION_CLOSE_W, sid, nil) tunWrite1(tun, buf[:FRAME_HEADER_LEN]) // tell peer to closeW edge.closed |= TCP_CLOSE_R } if code == FRAME_ACTION_OPEN_Y { closeR(src) } else { // remote open failed SafeClose(src) } }() if edge.positive { // for client: // new connection must send OPEN first. _len := _frame(buf, FRAME_ACTION_OPEN, sid, []byte(edge.dest)) if tunWrite1(tun, buf[:_len]) != nil { SafeClose(tun) return } select { case code = <-edge.ready: edge.initEqueue() // client delayed starting queue case <-time.After(WAITING_OPEN_TIMEOUT): log.Errorf("waiting open-signal(sid=%d) timeout for %s\n", sid, edge.dest) } if code != FRAME_ACTION_OPEN_Y { return } } for { nr, er = src.Read(buf[FRAME_HEADER_LEN:]) if nr > 0 { _frame(buf, FRAME_ACTION_DATA, sid, uint16(nr)) nr += FRAME_HEADER_LEN if tunWrite1(tun, buf[:nr]) != nil { SafeClose(tun) return } } if er != nil { return } } }
func (c *Client) initialNegotiation() (tun *Conn) { var tp = new(tunParams) var err error tun, err = c.nego.negotiate(tp) if err != nil { log.Errorf("Failed to connect %s, Retry after %s\n", c.nego.RemoteName(), RETRY_INTERVAL) return nil } c.token = tp.token tp.token = nil c.tp = tp log.Infoln("Login to the gateway", c.nego.RemoteName(), "successfully") return tun }
func (m *clientMgr) selectClient() *t.Client { if m.num > 1 { i := rand.Intn(m.num<<4) >> 4 for _, v := range m.indexChain[i : i+m.num-1] { if w := m.clients[v]; w != nil && atomic.LoadInt32(&w.State) >= 0 { return w } } } else { if w := m.clients[0]; w != nil && atomic.LoadInt32(&w.State) >= 0 { return w } } log.Errorf("No available tunnels for servicing new request") time.Sleep(t.REST_INTERVAL) return nil }
func (c *Client) eventHandler(e event, msg ...interface{}) { var mlen = len(msg) switch e { case evt_st_closed: atomic.StoreInt32(&c.State, -1) c.clearTokens() log.Errorf("Lost connection of gateway %s. Reconnect after %s\n", c.nego.RemoteName(), RETRY_INTERVAL) go c.StartSigTun(mlen > 0) case evt_st_ready: atomic.StoreInt32(&c.State, 0) log.Infoln("Tunnel negotiated with gateway", msg[0], "successfully") go c.startMultiplexer() case evt_dt_closed: go c.startDataTun(mlen > 0) case evt_st_msg: if mlen == 1 { go c.commandHandler(msg[0].(byte), nil) } else { go c.commandHandler(msg[0].(byte), msg[1].([]byte)) } case evt_st_active: c.sigTun.active(msg[0].(int64)) } }
func (p *multiplexer) relay(edge *edgeConn, tun *Conn, sid uint16) { var ( buf = make([]byte, FRAME_MAX_LEN) code byte src = edge.conn ) defer func() { if edge.closed&TCP_CLOSE_R == 0 { // only positively _frame(buf, FRAME_ACTION_CLOSE_W, sid, nil) tunWrite1(tun, buf[:FRAME_HEADER_LEN]) // tell peer to closeW edge.closed |= TCP_CLOSE_R } if code == FRAME_ACTION_OPEN_Y { closeR(src) } else { // remote open failed SafeClose(src) } }() if edge.positive { // for client _len := _frame(buf, FRAME_ACTION_OPEN, sid, []byte(edge.dest)) if tunWrite1(tun, buf[:_len]) != nil { SafeClose(tun) return } } var ( tn int // total nr int er error _fast_open = /* FAST_OPEN && */ p.isClient ) for { if _fast_open { v, y := reflect.ValueOf(edge.ready).TryRecv() if y { code = v.Interface().(byte) switch code { case FRAME_ACTION_OPEN_Y: _fast_open = false // read forever case FRAME_ACTION_OPEN_N: return } } else { if tn >= FAST_OPEN_BUF_MAX_SIZE { // must waiting for signal select { case code = <-edge.ready: case <-time.After(WAITING_OPEN_TIMEOUT): log.Errorf("waiting open-signal sid=%d timeout for %s\n", sid, edge.dest) } // timeout or open-signal received if code == FRAME_ACTION_OPEN_Y { _fast_open = false // read forever } else { return } } // else could read again } } nr, er = src.Read(buf[FRAME_HEADER_LEN:]) if nr > 0 { tn += nr _frame(buf, FRAME_ACTION_DATA, sid, uint16(nr)) if tunWrite1(tun, buf[:nr+FRAME_HEADER_LEN]) != nil { SafeClose(tun) return } } if er != nil { return } } }
func (c *Client) StartTun(mustRestart bool) { var ( wait bool tun *Conn rn = atomic.LoadInt32(&c.round) ) for { if wait { time.Sleep(RETRY_INTERVAL) } if rn < atomic.LoadInt32(&c.round) { return } if mustRestart { mustRestart = false if atomic.SwapInt32(&c.State, CLT_PENDING) >= CLT_WORKING { tun, rn = c.restart() if tun == nil { return } } else { return } } if atomic.LoadInt32(&c.State) == CLT_WORKING { // negotiation conn executed here firstly will not be null // otherwise must be null then create new one. if tun == nil { var err error tun, err = c.createDataTun() if err != nil { if DEBUG { ex.CatchException(err) } log.Errorf("Failed to connect %s. Reconnect after %s\n", err, RETRY_INTERVAL) wait = true continue } } if log.V(1) { log.Infof("Tun=%s is established\n", tun.sign()) } atomic.AddInt32(&c.dtCnt, 1) c.mux.Listen(tun, c.eventHandler, c.tp.dtInterval) dtcnt := atomic.AddInt32(&c.dtCnt, -1) log.Errorf("Tun=%s was disconnected, Reconnect after %s\n", tun.sign(), RETRY_INTERVAL) if atomic.LoadInt32(&c.mux.pingCnt) <= 0 { // dirty tokens: used abandoned tokens c.clearTokens() } if dtcnt <= 0 { // restart: all connections were disconnected log.Errorf("Connections %s were lost\n", tun.identifier) go c.StartTun(true) return } else { // reconnect // waiting and don't use old tun wait = true tun = nil } } else { // can't create tun and waiting for release if !c.pendingConn.acquire(RETRY_INTERVAL) { return } } } }