Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
lima-vm
GitHub Repository: lima-vm/lima
Path: blob/master/pkg/portfwdserver/server.go
2604 views
1
// SPDX-FileCopyrightText: Copyright The Lima Authors
2
// SPDX-License-Identifier: Apache-2.0
3
4
package portfwdserver
5
6
import (
7
"context"
8
"errors"
9
"io"
10
"net"
11
"time"
12
13
"github.com/containers/gvisor-tap-vsock/pkg/tcpproxy"
14
"github.com/sirupsen/logrus"
15
16
"github.com/lima-vm/lima/v2/pkg/guestagent/api"
17
)
18
19
type TunnelServer struct{}
20
21
func NewTunnelServer() *TunnelServer {
22
return &TunnelServer{}
23
}
24
25
func (s *TunnelServer) Start(stream api.GuestService_TunnelServer) error {
26
ctx := stream.Context()
27
// Receive the handshake message to start tunnel
28
in, err := stream.Recv()
29
if err != nil {
30
if errors.Is(err, io.EOF) {
31
return nil
32
}
33
return err
34
}
35
36
// We simply forward data form GRPC stream to net.Conn for both tcp and udp. So simple proxy is sufficient
37
var dialer net.Dialer
38
conn, err := dialer.DialContext(ctx, in.Protocol, in.GuestAddr)
39
if err != nil {
40
return err
41
}
42
rw := &GRPCServerRW{stream: stream, id: in.Id, closeCh: make(chan any, 1)}
43
go func() {
44
<-ctx.Done()
45
rw.Close()
46
}()
47
48
proxy := tcpproxy.DialProxy{DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
49
return conn, nil
50
}}
51
go proxy.HandleConn(rw)
52
53
// The stream will be closed when this function returns.
54
// Wait here until rw.Close(), rw.CloseRead(), or rw.CloseWrite() is called.
55
// We can't close rw.closeCh since the calling order of Close* methods is not guaranteed.
56
<-rw.closeCh
57
logrus.Debugf("closed GRPCServerRW for id: %s", in.Id)
58
59
return nil
60
}
61
62
type GRPCServerRW struct {
63
id string
64
stream api.GuestService_TunnelServer
65
closeCh chan any
66
}
67
68
var _ net.Conn = (*GRPCServerRW)(nil)
69
70
func (g *GRPCServerRW) Write(p []byte) (n int, err error) {
71
err = g.stream.Send(&api.TunnelMessage{Id: g.id, Data: p})
72
return len(p), err
73
}
74
75
func (g *GRPCServerRW) Read(p []byte) (n int, err error) {
76
in, err := g.stream.Recv()
77
if err != nil {
78
return 0, err
79
}
80
copy(p, in.Data)
81
return len(in.Data), nil
82
}
83
84
func (g *GRPCServerRW) Close() error {
85
logrus.Debugf("closing GRPCServerRW for id: %s", g.id)
86
g.closeCh <- struct{}{}
87
return nil
88
}
89
90
// By adding CloseRead and CloseWrite methods, GRPCServerRW can work with
91
// other than containers/gvisor-tap-vsock/pkg/tcpproxy, e.g., inetaf/tcpproxy, bicopy.Bicopy.
92
93
func (g *GRPCServerRW) CloseRead() error {
94
logrus.Debugf("closing read GRPCServerRW for id: %s", g.id)
95
g.closeCh <- struct{}{}
96
return nil
97
}
98
99
func (g *GRPCServerRW) CloseWrite() error {
100
logrus.Debugf("closing write GRPCServerRW for id: %s", g.id)
101
g.closeCh <- struct{}{}
102
return nil
103
}
104
105
func (g *GRPCServerRW) LocalAddr() net.Addr {
106
return &net.UnixAddr{Name: "grpc", Net: "unixpacket"}
107
}
108
109
func (g *GRPCServerRW) RemoteAddr() net.Addr {
110
return &net.UnixAddr{Name: "grpc", Net: "unixpacket"}
111
}
112
113
func (g *GRPCServerRW) SetDeadline(_ time.Time) error {
114
return nil
115
}
116
117
func (g *GRPCServerRW) SetReadDeadline(_ time.Time) error {
118
return nil
119
}
120
121
func (g *GRPCServerRW) SetWriteDeadline(_ time.Time) error {
122
return nil
123
}
124
125