* Attempt canceling sessions by the client In some circumstances (like a Ctrl-C), the client knows it's shutting down and the TOFU token will be lost. In this case it makes sense to cancel the session rather than have it time out. This introduces a command enum to the handshake message which can be used for the client to send commands to the worker -- in this case the only specified command is to cancel the session. It doesn't occur if the session is already timed out or if we can't authorize connections as this generally means the session is already invalid.
81 lines
2.4 KiB
Go
81 lines
2.4 KiB
Go
package worker
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net"
|
|
"net/url"
|
|
"sync"
|
|
|
|
"nhooyr.io/websocket"
|
|
|
|
pbs "github.com/hashicorp/boundary/internal/gen/controller/servers/services"
|
|
)
|
|
|
|
func (w *Worker) handleTcpProxyV1(connCtx context.Context, clientAddr *net.TCPAddr, conn *websocket.Conn, si *sessionInfo, connectionId, endpoint string) {
|
|
si.RLock()
|
|
sessionId := si.lookupSessionResponse.GetAuthorization().GetSessionId()
|
|
si.RUnlock()
|
|
|
|
sessionUrl, err := url.Parse(endpoint)
|
|
if err != nil {
|
|
w.logger.Error("error parsing endpoint information", "error", err, "session_id", sessionId, "endpoint", endpoint)
|
|
conn.Close(websocket.StatusInternalError, "cannot parse endpoint url")
|
|
return
|
|
}
|
|
if sessionUrl.Scheme != "tcp" {
|
|
w.logger.Error("invalid scheme for tcp proxy", "error", err, "session_id", sessionId, "endpoint", endpoint)
|
|
conn.Close(websocket.StatusInternalError, "invalid scheme for type")
|
|
return
|
|
}
|
|
remoteConn, err := net.Dial("tcp", sessionUrl.Host)
|
|
if err != nil {
|
|
w.logger.Error("error dialing endpoint", "error", err, "endpoint", endpoint)
|
|
conn.Close(websocket.StatusInternalError, "endpoint dialing failed")
|
|
return
|
|
}
|
|
// Assert this for better Go 1.11 splice support
|
|
tcpRemoteConn := remoteConn.(*net.TCPConn)
|
|
|
|
endpointAddr := tcpRemoteConn.RemoteAddr().(*net.TCPAddr)
|
|
connectionInfo := &pbs.ConnectConnectionRequest{
|
|
ConnectionId: connectionId,
|
|
ClientTcpAddress: clientAddr.IP.String(),
|
|
ClientTcpPort: uint32(clientAddr.Port),
|
|
EndpointTcpAddress: endpointAddr.IP.String(),
|
|
EndpointTcpPort: uint32(endpointAddr.Port),
|
|
Type: "tcp",
|
|
}
|
|
|
|
connStatus, err := w.connectConnection(connCtx, connectionInfo)
|
|
if err != nil {
|
|
w.logger.Error("error marking connection as connected", "error", err)
|
|
conn.Close(websocket.StatusInternalError, "failed to mark connection as connected")
|
|
return
|
|
}
|
|
si.Lock()
|
|
si.connInfoMap[connectionId].status = connStatus
|
|
si.Unlock()
|
|
|
|
// Get a wrapped net.Conn so we can use io.Copy
|
|
netConn := websocket.NetConn(connCtx, conn, websocket.MessageBinary)
|
|
|
|
connWg := new(sync.WaitGroup)
|
|
connWg.Add(2)
|
|
go func() {
|
|
defer connWg.Done()
|
|
_, err := io.Copy(netConn, tcpRemoteConn)
|
|
netConn.Close()
|
|
tcpRemoteConn.Close()
|
|
w.logger.Debug("copy from client to endpoint done", "error", err)
|
|
}()
|
|
go func() {
|
|
defer connWg.Done()
|
|
_, err := io.Copy(tcpRemoteConn, netConn)
|
|
tcpRemoteConn.Close()
|
|
netConn.Close()
|
|
w.logger.Debug("copy from endpoint to client done", "error", err)
|
|
}()
|
|
connWg.Wait()
|
|
}
|