func forwardLocalConn(logger lager.Logger, localConn net.Conn, conn *ssh.ServerConn, forwardIP string, forwardPort uint32) { defer localConn.Close() var req forwardTCPIPChannelRequest req.ForwardIP = forwardIP req.ForwardPort = forwardPort host, port, err := net.SplitHostPort(localConn.RemoteAddr().String()) if err != nil { logger.Error("failed-to-split-host-port", err) return } req.OriginIP = host _, err = fmt.Sscanf(port, "%d", &req.OriginPort) if err != nil { logger.Error("failed-to-parse-port", err) return } channel, reqs, err := conn.OpenChannel("forwarded-tcpip", ssh.Marshal(req)) if err != nil { logger.Error("failed-to-open-channel", err) return } defer channel.Close() go func() { for r := range reqs { logger.Info("ignoring-request", lager.Data{ "type": r.Type, }) r.Reply(false, nil) } }() wg := new(sync.WaitGroup) pipe := func(to io.WriteCloser, from io.ReadCloser) { // if either end breaks, close both ends to ensure they're both unblocked, // otherwise io.Copy can block forever if e.g. reading after write end has // gone away defer to.Close() defer from.Close() defer wg.Done() io.Copy(to, from) } wg.Add(1) go pipe(localConn, channel) wg.Add(1) go pipe(channel, localConn) wg.Wait() }
func handleRegs(reqs <-chan *ssh.Request, sshConn *ssh.ServerConn) { defer sshConn.Close() for req := range reqs { if req.Type == "keepalive" && req.WantReply { req.Reply(true, nil) continue } var payload tcpipforwardPayload if err := ssh.Unmarshal(req.Payload, &payload); err != nil { fmt.Println("ERROR", err) continue } addr := fmt.Sprintf("%s:%d", payload.Addr, payload.Port) ln, err := net.Listen("tcp", addr) if err != nil { fmt.Println("Unable to listen on address: ", addr) req.Reply(false, nil) continue } defer ln.Close() reply := (payload.Port == 0) && req.WantReply if !reply { req.Reply(true, nil) } else { req.Reply(false, nil) } go func() { fmt.Println("Listening on address: ", ln.Addr().String()) quit := make(chan bool) go func() { go func() { t := time.NewTicker(30 * time.Second) defer t.Stop() for { <-t.C _, _, err := sshConn.SendRequest("keepalive", true, nil) if err != nil { fmt.Println("closed", sshConn) sshConn.Close() return } } }() for { select { case <-quit: return default: conn, err := ln.Accept() if err != nil { continue } go func(conn net.Conn) { p := forwardedTCPPayload{} var err error var portnum int p.Addr = payload.Addr p.Port = payload.Port p.OriginAddr, portnum, err = getHostPortFromAddr(conn.RemoteAddr()) if err != nil { conn.Close() return } p.OriginPort = uint32(portnum) ch, reqs, err := sshConn.OpenChannel("forwarded-tcpip", ssh.Marshal(p)) if err != nil { conn.Close() log.Println("Open forwarded Channel: ", err.Error()) return } go ssh.DiscardRequests(reqs) go func(ch ssh.Channel, conn net.Conn) { close := func() { ch.Close() conn.Close() } go copyConnections(conn, ch, close) }(ch, conn) }(conn) } } }() sshConn.Wait() fmt.Println("Stop forwarding/listening on ", ln.Addr()) quit <- true }() } }