// Run runs the the tunnel session func (s *Session) Run() (err error) { defer s.recoverPanic("Session.Run") go func() { defer s.recoverPanic("Session.mux.Wait") code, err, debug := s.mux.Wait() s.Info("Session mux shutdown with code %v error %v debug %v", code, err, debug) }() defer s.mux.Close() // A tunnel session starts with an auth stream if err = s.handleAuth(); err != nil { return } // then we handle new streams sent from the client for { stream, err := s.mux.Accept() if err != nil { s.Shutdown() return s.Error("Failed to accept stream: %v", err) } go s.handleStream(conn.Wrap(stream, "stream", s.id)) } }
// Accept returns the next stream initiated by the server over the underlying muxado session func (s *RawSession) Accept() (conn.Conn, error) { raw, err := s.mux.Accept() if err != nil { return nil, err } return conn.Wrap(raw, "proxy", s.id), nil }
func (s *Session) handleAuth() error { // accept ann auth stream raw, err := s.mux.Accept() if err != nil { return s.Error("Failed to accept auth stream: %v", err) } defer raw.Close() stream := conn.Wrap(raw, "session", "auth") // read the Auth message if err = proto.ReadMsgInto(stream, &s.auth); err != nil { return s.Error("Failed to read auth message; %v", err) } failAuth := func(e error) error { _ = proto.WriteMsg(stream, &proto.AuthResp{Error: e.Error()}) return e } // generate a client identifier s.id = s.auth.ClientId if s.id == "" { // it's a new session, assign an ID if s.id, err = util.SecureRandId(16); err != nil { return failAuth(fmt.Errorf("Failed generate client identifier: %v", err)) } } // put ourselves in the registry s.registry.register(s) // set logging prefix s.Logger.AddTags(s.id) // agree on protocol version // if proto.Version not in s.auth.Version if sort.SearchStrings(s.auth.Version, proto.Version) == len(s.auth.Version) { return failAuth(fmt.Errorf("No acceptable protocol version. Requested: %v, capable: %v", s.auth.Version, proto.Version)) } // auth hook if err = s.hooks.OnAuth(s, s.auth); err != nil { return failAuth(err) } // Respond to authentication authResp := &proto.AuthResp{ Version: proto.Version, ClientId: s.id, } if err = proto.WriteMsg(stream, authResp); err != nil { return failAuth(fmt.Errorf("Failed to write authentication response: %v", err)) } return nil }
// Opens a new proxy stream to the client and writes a StartProxy message // with the given client address and tunnel url. func (s *Session) openProxy(clientAddr, tunnelUrl string) (pxy conn.Conn, err error) { // open a new proxy stream pxyStream, err := s.mux.Open() if err != nil { return } pxy = conn.Wrap(pxyStream) // tell the client we're going to start using this proxy connection startProxy := &proto.StartProxy{ ClientAddr: clientAddr, Url: tunnelUrl, } if err = proto.WriteMsg(pxy, startProxy); err != nil { return } pxy.AddTags(tunnelUrl) return }
// Listens for new public connections from the internet. func (t *Tunnel) listen(listener net.Listener) { defer t.recoverPanic("Tunnel.listen") t.Info("Listening for connections on %s", listener.Addr()) for { // accept public connections publicConn, err := listener.Accept() if err != nil { // not an error, we're shutting down this tunnel if atomic.LoadInt32(&t.closing) == 1 { return } t.Error("Failed to accept new connection: %v", err) continue } go t.handlePublic(conn.Wrap(publicConn, t.url)) } }
func (s *RawSession) req(tag string, req interface{}, resp interface{}) (err error) { stream, err := s.mux.Open() if err != nil { return } defer stream.Close() // log what happens on the stream c := conn.Wrap(stream, tag, s.id) // send the unlisten request if err = proto.WriteMsg(c, req); err != nil { return } // read out the unlisten response if err = proto.ReadMsgInto(c, resp); err != nil { return } return }