From 078cdbcb8169ee198151d2c7ec7832c6e8fb4a58 Mon Sep 17 00:00:00 2001 From: ginuerzh Date: Mon, 8 Jun 2026 20:05:57 +0800 Subject: [PATCH] fix(listener/tap): support custom TAP adapter name on Windows (issue #772) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The songgao/water library's InterfaceName parameter is a selection filter that searches the Windows registry for an existing adapter with a matching friendly name — it cannot name a new adapter during creation. Passing name=tap0 caused the library to fail before even creating the adapter. Fix by omitting InterfaceName from water.New(), letting Windows auto-name the adapter, then renaming via netsh if a custom name was specified. Also add directory validation to file handler to catch misconfig early. --- handler/file/handler.go | 9 +++++++++ listener/tap/tap_windows.go | 28 +++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/handler/file/handler.go b/handler/file/handler.go index 9729139d..6d54f07d 100644 --- a/handler/file/handler.go +++ b/handler/file/handler.go @@ -4,6 +4,7 @@ package file import ( "context" + "fmt" "io" "net" "net/http" @@ -57,6 +58,14 @@ func (h *fileHandler) Init(md md.Metadata) (err error) { return } + if h.md.dir != "" { + if info, err := os.Stat(h.md.dir); err != nil { + return fmt.Errorf("file handler: directory %q: %w", h.md.dir, err) + } else if !info.IsDir() { + return fmt.Errorf("file handler: %q is not a directory", h.md.dir) + } + } + fs := http.FileServer(http.Dir(h.md.dir)) h.handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.Method == http.MethodPut { diff --git a/listener/tap/tap_windows.go b/listener/tap/tap_windows.go index b0973f4a..f328f603 100644 --- a/listener/tap/tap_windows.go +++ b/listener/tap/tap_windows.go @@ -14,12 +14,16 @@ import ( func (l *tapListener) createTap() (dev io.ReadWriteCloser, name string, ip net.IP, err error) { ip, ipNet, _ := net.ParseCIDR(l.md.config.Net) + // On Windows, InterfaceName is a selection filter, not a name assignment. + // Passing it to water.New() causes the library to look for an existing + // adapter with that friendly name in the registry, which fails if no + // adapter with that name exists yet. Instead, let Windows auto-name the + // new adapter, then rename it afterwards if the user requested a name. ifce, err := water.New(water.Config{ DeviceType: water.TAP, PlatformSpecificParams: water.PlatformSpecificParams{ - ComponentID: l.md.config.ComponentID, - InterfaceName: l.md.config.Name, - Network: l.md.config.Net, + ComponentID: l.md.config.ComponentID, + Network: l.md.config.Net, }, }) if err != nil { @@ -29,10 +33,24 @@ func (l *tapListener) createTap() (dev io.ReadWriteCloser, name string, ip net.I dev = ifce name = ifce.Name() + // Rename the adapter if the user specified a custom name. + if l.md.config.Name != "" && l.md.config.Name != name { + renameCmd := fmt.Sprintf("netsh interface set interface name=%s newname=%s", + name, l.md.config.Name) + l.logger.Debug(renameCmd) + args := strings.Split(renameCmd, " ") + if er := exec.Command(args[0], args[1:]...).Run(); er == nil { + name = l.md.config.Name + } else { + l.logger.Warnf("failed to rename adapter from %s to %s: %v", + name, l.md.config.Name, er) + } + } + if ip != nil && ipNet != nil { cmd := fmt.Sprintf("netsh interface ip set address name=%s "+ "source=static addr=%s mask=%s gateway=none", - ifce.Name(), ip.String(), ipMask(ipNet.Mask)) + name, ip.String(), ipMask(ipNet.Mask)) l.logger.Debug(cmd) args := strings.Split(cmd, " ") @@ -42,7 +60,7 @@ func (l *tapListener) createTap() (dev io.ReadWriteCloser, name string, ip net.I } } - if err = l.addRoutes(ifce.Name(), l.md.config.Routes...); err != nil { + if err = l.addRoutes(name, l.md.config.Routes...); err != nil { return }