Files
OpenFlux/netbind/netbind.go
p1neappleXpressandClaude Opus 4.8 38867720ac integrate: fold openfluxfork features onto the encrypted-logging hotfix
Base is hotfix/oflx-encrypted-logging-context; in every place both trees
touched the same code, the hotfix version wins (3-level logger, heard-first
routing + MarkStalled, replay window, boards JSON API, robust SOCKS5 auth).

Brought over from meepo161/openfluxfork@fork-main (the mobile/ Android bridge
is intentionally left in the Android fork):
- provision/ + node_wizard.go + --node-wizard: SSH VDS exit-node install.
- deploy/node-install.sh + .github/workflows/node-release.yml: pinned,
  hash-verified node release.
- netbind/ + tun_windows.go + --inbound=tun on Windows (Wintun): full tunnel
  with the core's own sockets bound to the real interface; dial sites in
  direct/boards/vyandex/cupsonline/mailru/yandex now dial bound.
- transport/yandex/cookies.go + --yandex-cookies-file: Netscape cookie import
  into the shared vyandex jar (sessions and single-transport alike).
- yandex: CheckVolgaDocument (doc suitability for the wizard) and the PoW
  captcha fix (a rejected challenge is a captcha, not a bad document).
- --http-proxy: HTTP CONNECT proxy over the same tunnel as SOCKS5.
- ipcStatusLoop: per-second IPC Status frame with the active carrier
  (Session.LiveTransports / ActiveTransport).

Builds for darwin, windows and linux; go vet and the full test suite pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-27 02:47:49 +03:00

78 lines
2.4 KiB
Go

// Package netbind keeps the core's own connections off its own tunnel.
//
// With the Windows full tunnel (--inbound=tun) the default route points at
// the OpenFlux adapter, but the carriers (Yandex, the node's direct port)
// must still leave through the real network, or they would loop into the
// tunnel they carry. Android gets this by excluding the app from its VPN;
// here every outbound socket of the core is bound to the physical
// interface instead (IP_UNICAST_IF), which holds whatever the routes say.
// Dial sites use Dialer or DialContext; Bind also covers the defaults
// (http.DefaultTransport, websocket.DefaultDialer, net.DefaultResolver).
package netbind
import (
"context"
"net"
"net/http"
"sync/atomic"
"syscall"
"time"
"github.com/gorilla/websocket"
)
type controlFunc = func(network, address string, c syscall.RawConn) error
var current atomic.Pointer[controlFunc]
// control applies the current binding, if any.
func control(network, address string, c syscall.RawConn) error {
if f := current.Load(); f != nil {
return (*f)(network, address, c)
}
return nil
}
// Dialer is a net.Dialer that follows the binding.
func Dialer(timeout time.Duration) *net.Dialer {
return Wrap(&net.Dialer{Timeout: timeout, KeepAlive: 30 * time.Second})
}
// Wrap makes d follow the binding (keeping its own Control, if any).
func Wrap(d *net.Dialer) *net.Dialer {
own := d.Control
d.Control = func(network, address string, c syscall.RawConn) error {
if own != nil {
if err := own(network, address, c); err != nil {
return err
}
}
return control(network, address, c)
}
return d
}
// DialContext dials with a default Dialer (30 s timeout).
func DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return Dialer(30*time.Second).DialContext(ctx, network, address)
}
// Bound reports whether sockets are bound to an interface now.
func Bound() bool { return current.Load() != nil }
// install sets the binding and points the standard library's defaults at
// bound dialers, so code that never names a dialer is covered too.
func install(f controlFunc) {
current.Store(&f)
if t, ok := http.DefaultTransport.(*http.Transport); ok {
t.DialContext = DialContext
}
websocket.DefaultDialer.NetDialContext = DialContext
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return Dialer(10*time.Second).DialContext(ctx, network, address)
},
}
}