Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 24 additions & 6 deletions hack/test-port-forwarding.pl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
use warnings;

use Config qw(%Config);
use File::Spec::Functions qw(catfile);
use IO::Handle qw();
use Socket qw(inet_ntoa);
use Sys::Hostname qw(hostname);
Expand All @@ -33,6 +34,11 @@
chomp $ipv4;
}

my $instDir = qx(limactl list "$instance" --yq .dir);
chomp $instDir;
# platform independent way to add trailing path separator
my $sockDir = catfile($instDir, "sock", "");

# If $instance is a filename, add our portForwards to it to enable testing
if (-f $instance) {
open(my $fh, "+< $instance") or die "Can't open $instance for read/write: $!";
Expand Down Expand Up @@ -94,14 +100,17 @@
s/sshLocalPort/$sshLocalPort/g;
s/ipv4/$ipv4/g;
s/ipv6/$ipv6/g;
s/sockDir\//$sockDir/g;
# forward: 127.0.0.1 899 → 127.0.0.1 799
# ignore: 127.0.0.2 8888
/^(forward|ignore):\s+([0-9.:]+)\s+(\d+)(?:\s+→)?(?:\s+([0-9.:]+)(?:\s+(\d+))?)?/;
/^(forward|ignore):\s+([0-9.:]+)\s+(\d+)(?:\s+→)?(?:\s+(?:([0-9.:]+)(?:\s+(\d+))|(\S+))?)?/;
die "Cannot parse test '$_'" unless $1;
my %test; @test{qw(mode guest_ip guest_port host_ip host_port)} = ($1, $2, $3, $4, $5);
my %test; @test{qw(mode guest_ip guest_port host_ip host_port host_socket)} = ($1, $2, $3, $4, $5, $6);

$test{host_ip} ||= "127.0.0.1";
$test{host_port} ||= $test{guest_port};
if ($test{mode} eq "forward" && $test{host_port} < 1024 && $Config{osname} ne "darwin") {
$test{host_socket} ||= "";
if ($test{mode} eq "forward" && $test{host_socket} eq "" && $test{host_port} < 1024 && $Config{osname} ne "darwin") {
printf "🚧 Not supported on $Config{osname}: # $_\n";
next;
}
Expand All @@ -113,9 +122,13 @@
printf "🚧 Not supported for $instanceType machines: # $_\n";
next;
}
if ($test{host_socket} ne "" && $Config{osname} eq "cygwin") {
printf "🚧 Not supported on $Config{osname}: # $_\n";
next;
}

my $remote = JoinHostPort($test{guest_ip},$test{guest_port});
my $local = JoinHostPort($test{host_ip},$test{host_port});
my $local = $test{host_socket} eq "" ? JoinHostPort($test{host_ip},$test{host_port}) : $test{host_socket};
if ($test{mode} eq "ignore") {
$test{log_msg} = "Not forwarding TCP $remote";
}
Expand Down Expand Up @@ -163,7 +176,7 @@
# Try to reach each listener from the host
foreach my $test (@test) {
next if $test->{host_port} == $sshLocalPort;
my $nc = "nc -w 1 $test->{host_ip} $test->{host_port}";
my $nc = $test->{host_socket} eq "" ? "nc -w 1 $test->{host_ip} $test->{host_port}" : "nc -w 1 -U $test->{host_socket}";
open(my $netcat, "| $nc") or die "Can't run '$nc': $!";
print $netcat "$test->{log_msg}\n";
# Don't check for errors on close; macOS nc seems to return non-zero exit code even on success
Expand All @@ -175,7 +188,7 @@
seek($log, $ha_log_size, 0) or die "Can't seek $ha_log to $ha_log_size: $!";
my %seen;
while (<$log>) {
$seen{$1}++ if /(Forwarding TCP from .*? to (\d.*?|\[.*?\]):\d+)/;
$seen{$1}++ if /(Forwarding TCP from .*? to ((\d.*?|\[.*?\]):\d+|\/[^"]+))/;
$seen{$1}++ if /(Not forwarding TCP .*?:\d+)/;
}
close $log or die;
Expand Down Expand Up @@ -342,3 +355,8 @@ sub JoinHostPort {
- guestIPMustBeZero: true
guestPort: 8888
hostIP: 0.0.0.0

- guestPort: 5000
hostSocket: port5000.sock

# forward: 127.0.0.1 5000 → sockDir/port5000.sock
15 changes: 7 additions & 8 deletions pkg/limayaml/defaults.go
Original file line number Diff line number Diff line change
Expand Up @@ -902,14 +902,6 @@ func FillPortForwardDefaults(rule *limatype.PortForward, instDir string, user li
rule.GuestPortRange[1] = rule.GuestPort
}
}
if rule.HostPortRange[0] == 0 && rule.HostPortRange[1] == 0 {
if rule.HostPort == 0 {
rule.HostPortRange = rule.GuestPortRange
} else {
rule.HostPortRange[0] = rule.HostPort
rule.HostPortRange[1] = rule.HostPort
}
}
if rule.GuestSocket != "" {
if out, err := executeGuestTemplate(rule.GuestSocket, instDir, user, param); err == nil {
rule.GuestSocket = out.String()
Expand All @@ -926,6 +918,13 @@ func FillPortForwardDefaults(rule *limatype.PortForward, instDir string, user li
if !filepath.IsAbs(rule.HostSocket) {
rule.HostSocket = filepath.Join(instDir, filenames.SocketDir, rule.HostSocket)
}
} else if rule.HostPortRange[0] == 0 && rule.HostPortRange[1] == 0 {
if rule.HostPort == 0 {
rule.HostPortRange = rule.GuestPortRange
} else {
rule.HostPortRange[0] = rule.HostPort
rule.HostPortRange[1] = rule.HostPort
}
}
}

Expand Down
1 change: 1 addition & 0 deletions pkg/limayaml/defaults_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ func TestFillDefault(t *testing.T) {
expect.PortForwards[2].HostPort = 8888
expect.PortForwards[2].HostPortRange = [2]int{8888, 8888}

expect.PortForwards[3].HostPortRange = [2]int{0, 0}
expect.PortForwards[3].GuestSocket = fmt.Sprintf("%s | %s | %s | %s", user.HomeDir, user.Uid, user.Username, y.Param["ONE"])
expect.PortForwards[3].HostSocket = fmt.Sprintf("%s | %s | %s | %s | %s | %s", hostHome, instDir, instName, currentUser.Uid, currentUser.Username, y.Param["ONE"])

Expand Down
12 changes: 7 additions & 5 deletions pkg/limayaml/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,10 @@ func Validate(y *limatype.LimaYAML, warn bool) error {
if err := validatePort(fmt.Sprintf("%s.guestPortRange[%d]", field, j), rule.GuestPortRange[j]); err != nil {
errs = errors.Join(errs, err)
}
if err := validatePort(fmt.Sprintf("%s.hostPortRange[%d]", field, j), rule.HostPortRange[j]); err != nil {
errs = errors.Join(errs, err)
if rule.HostSocket == "" {
if err := validatePort(fmt.Sprintf("%s.hostPortRange[%d]", field, j), rule.HostPortRange[j]); err != nil {
errs = errors.Join(errs, err)
}
}
}
if rule.GuestPortRange[0] > rule.GuestPortRange[1] {
Expand All @@ -324,9 +326,6 @@ func Validate(y *limatype.LimaYAML, warn bool) error {
if rule.HostPortRange[0] > rule.HostPortRange[1] {
errs = errors.Join(errs, fmt.Errorf("field `%s.hostPortRange[1]` must be greater than or equal to field `%s.hostPortRange[0]`", field, field))
}
if rule.GuestPortRange[1]-rule.GuestPortRange[0] != rule.HostPortRange[1]-rule.HostPortRange[0] {
errs = errors.Join(errs, fmt.Errorf("field `%s.hostPortRange` must specify the same number of ports as field `%s.guestPortRange`", field, field))
}
if rule.GuestSocket != "" {
if !path.IsAbs(rule.GuestSocket) {
errs = errors.Join(errs, fmt.Errorf("field `%s.guestSocket` must be an absolute path, but is %q", field, rule.GuestSocket))
Expand All @@ -343,7 +342,10 @@ func Validate(y *limatype.LimaYAML, warn bool) error {
if rule.GuestSocket == "" && rule.GuestPortRange[1]-rule.GuestPortRange[0] > 0 {
errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` can only be mapped from a single port or socket. not a range", field))
}
} else if rule.GuestPortRange[1]-rule.GuestPortRange[0] != rule.HostPortRange[1]-rule.HostPortRange[0] {
errs = errors.Join(errs, fmt.Errorf("field `%s.hostPortRange` must specify the same number of ports as field `%s.guestPortRange`", field, field))
}

if len(rule.HostSocket) >= osutil.UnixPathMax {
errs = errors.Join(errs, fmt.Errorf("field `%s.hostSocket` must be less than UNIX_PATH_MAX=%d characters, but is %d",
field, osutil.UnixPathMax, len(rule.HostSocket)))
Expand Down
12 changes: 12 additions & 0 deletions pkg/portfwd/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
"errors"
"fmt"
"net"
"os"
"path/filepath"
"strings"
"sync"

Expand Down Expand Up @@ -146,3 +148,13 @@ func (p *ClosableListeners) forwardUDP(ctx context.Context, client *guestagentcl
func key(protocol, hostAddress, guestAddress string) string {
return fmt.Sprintf("%s-%s-%s", protocol, hostAddress, guestAddress)
}

func prepareUnixSocket(hostSocket string) error {
if err := os.RemoveAll(hostSocket); err != nil {
return fmt.Errorf("can't clean up %q: %w", hostSocket, err)
}
if err := os.MkdirAll(filepath.Dir(hostSocket), 0o755); err != nil {
return fmt.Errorf("can't create directory for local socket %q: %w", hostSocket, err)
}
return nil
}
14 changes: 14 additions & 0 deletions pkg/portfwd/listener_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,26 @@ import (
"context"
"fmt"
"net"
"path/filepath"
"strconv"

"github.com/sirupsen/logrus"
)

func Listen(ctx context.Context, listenConfig net.ListenConfig, hostAddress string) (net.Listener, error) {
if filepath.IsAbs(hostAddress) {
// Handle Unix domain sockets
if err := prepareUnixSocket(hostAddress); err != nil {
return nil, err
}
var lc net.ListenConfig
unixLis, err := lc.Listen(ctx, "unix", hostAddress)
if err != nil {
logrus.WithError(err).Errorf("failed to listen unix: %v", hostAddress)
return nil, err
}
return unixLis, nil
}
localIPStr, localPortStr, _ := net.SplitHostPort(hostAddress)
localIP := net.ParseIP(localIPStr)
localPort, _ := strconv.Atoi(localPortStr)
Expand Down
16 changes: 16 additions & 0 deletions pkg/portfwd/listener_others.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,25 @@ package portfwd
import (
"context"
"net"
"path/filepath"

"github.com/sirupsen/logrus"
)

func Listen(ctx context.Context, listenConfig net.ListenConfig, hostAddress string) (net.Listener, error) {
if filepath.IsAbs(hostAddress) {
// Handle Unix domain sockets
if err := prepareUnixSocket(hostAddress); err != nil {
return nil, err
}
var lc net.ListenConfig
unixLis, err := lc.Listen(ctx, "unix", hostAddress)
if err != nil {
logrus.WithError(err).Errorf("failed to listen unix: %v", hostAddress)
return nil, err
}
return unixLis, nil
}
return listenConfig.Listen(ctx, "tcp", hostAddress)
}

Expand Down
Loading