fix(listener/tap): support custom TAP adapter name on Windows (issue #772)

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.
This commit is contained in:
ginuerzh
2026-06-08 20:05:57 +08:00
parent 28e5922fce
commit 078cdbcb81
2 changed files with 32 additions and 5 deletions
+9
View File
@@ -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 {
+23 -5
View File
@@ -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
}