// 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, debug1 := s.mux.Wait() // s.Info("Session mux shutdown with code %v error %v debug %v", code, err, debug) log.Println(fmt.Sprintf("[INFO] Session mux shutdown with code %v error %v debug %v", code, err, debug1)) }() 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() err := fmt.Errorf(fmt.Sprintf("Failed to accept stream: %v", err)) log.Println(fmt.Sprintf("[ERROR] %v", err)) return 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 }
// 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 }
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 }
// 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()) log.Printf("[INFO] Listening for connections on %s\n", 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) log.Printf("[ERROR] Failed to accept new connection: %v\n", err) continue } go t.handlePublic(conn.Wrap(publicConn, t.url)) } }
func (s *Session) handleAuth() error { // accept ann auth stream raw, err := s.mux.Accept() if err != nil { err := fmt.Errorf(fmt.Sprintf("Failed to accept auth stream: %v", err)) log.Println(fmt.Sprintf("[ERROR] %v", err)) // return s.Error("Failed to accept auth stream: %v", err) return err } defer raw.Close() stream := conn.Wrap(raw, "session", "auth") // read the Auth message if err = proto.ReadMsgInto(stream, &s.auth); err != nil { err := fmt.Errorf(fmt.Sprintf("Failed to read auth message; %v", err)) log.Println(fmt.Sprintf("[ERROR] %v", err)) // return s.Error("Failed to read auth message; %v", err) return 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 }