func (t *Session) DataTunServe(fconn *Conn, buf []byte) { defer func() { var offline bool if atomic.AddInt32(&t.activeCnt, -1) <= 0 { offline = true t.mgr.clearTokens(t) t.mux.destroy() } var err = recover() if log.V(1) { log.Infof("Tun=%s was disconnected. %v\n", fconn.identifier, nvl(err, NULL)) if offline { log.Infof("Client=%s was offline\n", t.cid) } } if DEBUG { ex.CatchException(err) } }() atomic.AddInt32(&t.activeCnt, 1) if buf != nil { token := buf[:TKSZ] fconn.cipher = t.cipherFactory.NewCipher(token) buf = nil } else { // first negotiation had initialized cipher, the buf will be null log.Infof("Client=%s is online\n", t.cid) } if log.V(1) { log.Infof("Tun=%s is established\n", fconn.identifier) } t.mux.Listen(fconn, t.eventHandler, DT_PING_INTERVAL) }
func (t *Server) TunnelServe(conn *net.TCPConn) { fconn := NewConnWithHash(conn) defer func() { fconn.FreeHash() ex.CatchException(recover()) }() nego := &d5SNegotiation{Server: t} session, err := nego.negotiate(fconn) if session != nil { // unique fconn.identifier = fmt.Sprintf("%s@%s", session.uid, fconn.RemoteAddr()) } if err != nil { if err == DATATUN_SESSION { // dataTunnel go session.DataTunServe(fconn.Conn, nego.tokenBuf) } else { log.Warningln("Close abnormal connection from", conn.RemoteAddr(), err) SafeClose(conn) if session != nil { t.sessionMgr.clearTokens(session) } } } else if session != nil { // signalTunnel atomic.AddInt32(&t.stCnt, 1) log.Infof("Client(%s)-ST is established\n", fconn.identifier) var st = NewSignalTunnel(session.tun, 0) session.svr = t session.sigTun = st go st.start(session.eventHandler) } }
// without sPub field func NewD5Params(uri string) (*D5Params, error) { re := regexp.MustCompile("d5://(\\w+):(\\w+)@([.:a-zA-Z0-9-]+)#(\\w+)") ma := re.FindStringSubmatch(uri) if len(ma) != 5 { return nil, INVALID_D5PARAMS } _, y := availableCiphers[ma[4]] if !y { return nil, UNSUPPORTED_CIPHER.Apply(ma[4]) } d5sAddr, e := net.ResolveTCPAddr("tcp", ma[3]) if e != nil { return nil, D5SER_UNREACHABLE.Apply(e) } if log.V(2) { log.Infof("D5Params: %q\n", ma[1:]) } return &D5Params{ d5sAddrStr: ma[3], d5sAddr: d5sAddr, algo: ma[4], user: ma[1], pass: ma[2], }, nil }
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) } } }
func (c *Client) getToken() []byte { c.lock.Lock() defer func() { c.lock.Unlock() tlen := len(c.token) / TKSZ if tlen <= TOKENS_FLOOR && atomic.LoadInt32(&c.State) == 0 { atomic.AddInt32(&c.State, 1) if log.V(4) { log.Infof("Request new tokens, pool=%d\n", tlen) } c.sigTun.postCommand(TOKEN_REQUEST, nil) } }() for len(c.token) < TKSZ { log.Warningln("waiting for token. May be the requests are coming too fast.") c.waitTK.Wait() if atomic.LoadInt32(&c.State) < 0 { panic("Abandon the request beacause the tunSession was lost.") } } token := c.token[:TKSZ] c.token = c.token[TKSZ:] return token }
func (t *Session) DataTunServe(fconn *Conn, buf []byte) { var svr = t.svr defer func() { atomic.AddInt32(&svr.dtCnt, -1) SafeClose(fconn) err := recover() log.Infof("Client(%s)-DT was disconnected. %v\n", fconn.identifier, err) if DEBUG { ex.CatchException(err) } }() atomic.AddInt32(&svr.dtCnt, 1) token := buf[:TKSZ] fconn.cipher = t.cipherFactory.NewCipher(token) log.Infof("Client(%s)-DT is established\n", fconn.identifier) svr.mux.Listen(fconn, t.eventHandler, DT_PING_INTERVAL) }
// async request func (c *Client) requireTokens() { // non-working state can't require anything if atomic.CompareAndSwapInt32(&c.State, CLT_WORKING, CLT_PENDING) { if log.V(3) { log.Infof("Request new tokens, pool=%d\n", len(c.token)/TKSZ) } go c.mux.bestSend([]byte{FRAME_ACTION_TOKEN_REQUEST}, "requireTokens") } }
func (t *Session) onSTDisconnected() { tid := t.tun.identifier SafeClose(t.tun) atomic.AddInt32(&t.svr.stCnt, -1) log.Warningf("Client(%s)-ST was disconnected\n", tid) i := t.svr.sessionMgr.clearTokens(t) if log.V(4) { log.Infof("Clear tokens %d of %s\n", i, tid) } }
func (c *Client) putTokens(tokens []byte) { c.lock.Lock() defer c.lock.Unlock() c.token = append(c.token, tokens...) atomic.StoreInt32(&c.State, 0) c.waitTK.Broadcast() if log.V(4) { log.Infof("Recv tokens=%d pool=%d\n", len(tokens)/TKSZ, len(c.token)/TKSZ) } }
func (p *multiplexer) HandleRequest(prot string, client net.Conn, target string) { sid := _nextSID() if log.V(1) { log.Infof("%s->[%s] from=%s sid=%d\n", prot, target, ipAddr(client.RemoteAddr()), sid) } tun := p.pool.Select() ThrowIf(tun == nil, "No tun to deliveries request") key := sessionKey(tun, sid) edge := p.router.register(key, target, tun, client, true) // write edge p.relay(edge, tun, sid) // read edge }
func (t *signalTunnel) postCommand(cmd byte, args []byte) (n int, err error) { t.lock.Lock() defer t.lock.Unlock() buf := randArray(CMD_HEADER_LEN, CMD_HEADER_LEN) buf[0] = cmd binary.BigEndian.PutUint16(buf[2:], uint16(len(args))) if args != nil { buf = append(buf, args...) } if log.V(4) { log.Infof("send command packet=[% x]\n", buf) } t.tun.SetWriteDeadline(time.Now().Add(GENERAL_SO_TIMEOUT * 2)) n, err = t.tun.Write(buf) return }
func (c *Client) saveTokens(data []byte) { var tokens []byte switch data[0] { case FRAME_ACTION_TOKEN_REQUEST: log.Warningf("unexpected token request") return case FRAME_ACTION_TOKEN_REPLY: tokens = data[1:] } c.lock.Lock() defer c.lock.Unlock() c.token = append(c.token, tokens...) atomic.CompareAndSwapInt32(&c.State, CLT_PENDING, CLT_WORKING) c.pendingTK.notifyAll() if log.V(3) { log.Infof("Recv tokens=%d pool=%d\n", len(tokens)/TKSZ, len(c.token)/TKSZ) } }
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 }
// close for ending of queued task func (q *equeue) _close(force bool, close_code uint) { q.lock.Lock() defer q.lock.Unlock() e := q.edge if log.V(4) { switch close_code { case CLOSED_BY_ERR: log.Infoln("terminate", e.dest) case CLOSED_FORCE: log.Infoln("close", e.dest) case CLOSED_WRITE: log.Infof("closeW %s by peer\n", e.dest) } } q.buffer.Init() q.buffer = nil if force { e.closed = TCP_CLOSE_R | TCP_CLOSE_W SafeClose(e.conn) } else { closeW(e.conn) } }
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 } } } }