Skip to content

Commit 00c6b4b

Browse files
authored
feat: add Windows support with named pipes (#1069)
Signed-off-by: Chris Gianelloni <[email protected]>
1 parent d3d2167 commit 00c6b4b

File tree

8 files changed

+122
-17
lines changed

8 files changed

+122
-17
lines changed

.github/workflows/go-test.yml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@ jobs:
1717
strategy:
1818
matrix:
1919
go-version: [1.24.x, 1.25.x]
20-
# We want to make sure that this builds on Linux and Darwin
21-
platform: [ubuntu-latest, macos-latest]
20+
# We want to make sure that this builds on Linux, Darwin, and Windows
21+
platform: [ubuntu-latest, macos-latest, windows-latest]
2222
include:
2323
- platform: ubuntu-latest
2424
cache_path: ~/.cache/go-build
2525
- platform: macos-latest
2626
cache_path: ~/Library/Caches/go-build
27+
- platform: windows-latest
28+
cache_path: ~\AppData\Local\go-build
2729
runs-on: ${{ matrix.platform }}
2830
steps:
2931
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 https://github.com/actions/checkout/releases/tag/v6.0.0
@@ -38,5 +40,7 @@ jobs:
3840
key: ${{ runner.os }}-go-${{ matrix.go-version }}-${{ hashFiles('**/go.sum') }}
3941
restore-keys: |
4042
${{ runner.os }}-go-${{ matrix.go-version }}-
43+
- name: go-build
44+
run: go build ./cmd/dingo
4145
- name: go-test
4246
run: go test ./...

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
⚠️ This is a work in progress and is currently under heavy development
1515

16+
**Note:** On Windows systems, named pipes are used instead of Unix sockets for node-to-client communication.
17+
1618
<div align="center">
1719
<img src="./.github/dingo-20241210.png" alt="dingo screenshot" width="640">
1820
</div>

connmanager/listener.go

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"context"
1919
"fmt"
2020
"net"
21+
"runtime"
2122

2223
"github.com/blinklabs-io/dingo/event"
2324
ouroboros "github.com/blinklabs-io/gouroboros"
@@ -44,19 +45,32 @@ func (c *ConnectionManager) startListeners() error {
4445
func (c *ConnectionManager) startListener(l ListenerConfig) error {
4546
// Create listener if none is provided
4647
if l.Listener == nil {
47-
listenConfig := net.ListenConfig{}
48-
if l.ReuseAddress {
49-
listenConfig.Control = socketControl
50-
}
51-
listener, err := listenConfig.Listen(
52-
context.Background(),
53-
l.ListenNetwork,
54-
l.ListenAddress,
55-
)
56-
if err != nil {
57-
return fmt.Errorf("failed to open listening socket: %w", err)
48+
// On Windows, the "unix" network type is repurposed to create named pipes
49+
// for compatibility with configurations that specify "unix" network on Unix systems.
50+
if runtime.GOOS == "windows" && l.ListenNetwork == "unix" {
51+
listener, err := createPipeListener(
52+
l.ListenNetwork,
53+
l.ListenAddress,
54+
)
55+
if err != nil {
56+
return fmt.Errorf("failed to open listening pipe: %w", err)
57+
}
58+
l.Listener = listener
59+
} else {
60+
listenConfig := net.ListenConfig{}
61+
if l.ReuseAddress {
62+
listenConfig.Control = socketControl
63+
}
64+
listener, err := listenConfig.Listen(
65+
context.Background(),
66+
l.ListenNetwork,
67+
l.ListenAddress,
68+
)
69+
if err != nil {
70+
return fmt.Errorf("failed to open listening socket: %w", err)
71+
}
72+
l.Listener = listener
5873
}
59-
l.Listener = listener
6074
if l.UseNtC {
6175
c.config.Logger.Info(
6276
"listening for ouroboros node-to-client connections on " + l.ListenAddress,
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1-
// Copyright 2024 Blink Labs Software
1+
//go:build !windows
2+
3+
// Copyright 2025 Blink Labs Software
24
//
35
// Licensed under the Apache License, Version 2.0 (the "License");
46
// you may not use this file except in compliance with the License.
@@ -15,6 +17,8 @@
1517
package connmanager
1618

1719
import (
20+
"errors"
21+
"net"
1822
"syscall"
1923

2024
"golang.org/x/sys/unix"
@@ -53,3 +57,8 @@ func socketControl(network, address string, c syscall.RawConn) error {
5357
}
5458
return nil
5559
}
60+
61+
// createPipeListener should never be called on non-Windows systems
62+
func createPipeListener(_, _ string) (net.Listener, error) {
63+
return nil, errors.New("pipe listener not supported on non-Windows systems")
64+
}

connmanager/listener_windows.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//go:build windows
2+
3+
// Copyright 2025 Blink Labs Software
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package connmanager
18+
19+
import (
20+
"errors"
21+
"net"
22+
"strings"
23+
"syscall"
24+
25+
"github.com/Microsoft/go-winio"
26+
)
27+
28+
// socketControl is a no-op on Windows
29+
func socketControl(_network, _address string, _c syscall.RawConn) error {
30+
return nil
31+
}
32+
33+
type UnixConnAddr struct {
34+
addr string
35+
}
36+
37+
func (a UnixConnAddr) Network() string { return "pipe" }
38+
39+
func (a UnixConnAddr) String() string { return a.addr }
40+
41+
type UnixConn struct {
42+
net.Conn
43+
remoteAddr UnixConnAddr
44+
}
45+
46+
func NewUnixConn(conn net.Conn) (*UnixConn, error) {
47+
if conn == nil {
48+
return nil, errors.New("connection is nil")
49+
}
50+
if conn.RemoteAddr() == nil {
51+
return nil, errors.New("connection has no remote address")
52+
}
53+
return &UnixConn{
54+
Conn: conn,
55+
remoteAddr: UnixConnAddr{addr: conn.RemoteAddr().String()},
56+
}, nil
57+
}
58+
59+
func (u *UnixConn) RemoteAddr() net.Addr {
60+
return u.remoteAddr
61+
}
62+
63+
// createPipeListener creates a named pipe listener on Windows
64+
func createPipeListener(_, address string) (net.Listener, error) {
65+
// Adjust address to named pipe format if not already
66+
if !strings.HasPrefix(address, `\\.\pipe\`) {
67+
address = `\\.\pipe\` + address
68+
}
69+
return winio.ListenPipe(address, nil)
70+
}

connmanager/unix.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
//go:build !windows
2+
13
// Copyright 2025 Blink Labs Software
24
//
35
// Licensed under the Apache License, Version 2.0 (the "License");

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
connectrpc.com/connect v1.19.1
1010
connectrpc.com/grpchealth v1.4.0
1111
connectrpc.com/grpcreflect v1.3.0
12+
github.com/Microsoft/go-winio v0.6.2
1213
github.com/aws/aws-sdk-go-v2/config v1.32.1
1314
github.com/aws/aws-sdk-go-v2/service/s3 v1.92.0
1415
github.com/blinklabs-io/gouroboros v0.140.0

internal/node/node.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
_ "net/http/pprof" // #nosec G108
2323
"os"
2424
"os/signal"
25+
"runtime"
2526
"syscall"
2627
"time"
2728

@@ -39,8 +40,10 @@ func Run(cfg *config.Config, logger *slog.Logger) error {
3940
"component", "node",
4041
)
4142
// TODO: make this safer, check PID, create parent, etc. (#276)
42-
if _, err := os.Stat(cfg.SocketPath); err == nil {
43-
os.Remove(cfg.SocketPath)
43+
if runtime.GOOS != "windows" {
44+
if _, err := os.Stat(cfg.SocketPath); err == nil {
45+
os.Remove(cfg.SocketPath)
46+
}
4447
}
4548
var nodeCfg *cardano.CardanoNodeConfig
4649
if cfg.CardanoConfig != "" {

0 commit comments

Comments
 (0)