Newer
Older
// Copyright 2019 Path Network, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
ListenAddrStr string
TargetAddr4Str string
TargetAddr6Str string
ListenAddr netip.AddrPort
TargetAddr4 netip.AddrPort
TargetAddr6 netip.AddrPort
Mark int
Verbose int
allowedSubnetsPath string
AllowedSubnets []*net.IPNet
Listeners int
flag.StringVar(&Opts.Protocol, "p", "tcp", "Protocol that will be proxied: tcp, udp")
flag.StringVar(&Opts.ListenAddrStr, "l", "0.0.0.0:8443", "Address the proxy listens on")
flag.StringVar(&Opts.TargetAddr4Str, "4", "127.0.0.1:443", "Address to which IPv4 traffic will be forwarded to")
flag.StringVar(&Opts.TargetAddr6Str, "6", "[::1]:443", "Address to which IPv6 traffic will be forwarded to")
flag.IntVar(&Opts.Mark, "mark", 0, "The mark that will be set on outbound packets")
flag.IntVar(&Opts.Verbose, "v", 0, `0 - no logging of individual connections
1 - log errors occurring in individual connections
2 - log all state changes of individual connections`)
flag.StringVar(&Opts.allowedSubnetsPath, "allowed-subnets", "",
"Path to a file that contains allowed subnets of the proxy servers")
flag.IntVar(&Opts.Listeners, "listeners", 1,
"Number of listener sockets that will be opened for the listen address (Linux 3.9+)")
flag.IntVar(&Opts.udpCloseAfter, "close-after", 60, "Number of seconds after which UDP socket will be cleaned up")
func listen(listenerNum int, errors chan<- error) {
logger := Opts.Logger.With(slog.Int("listenerNum", listenerNum),
slog.String("protocol", Opts.Protocol), slog.String("listenAdr", Opts.ListenAddr.String()))
listenConfig.Control = func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
soReusePort := 15
if err := syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, soReusePort, 1); err != nil {
logger.Warn("failed to set SO_REUSEPORT - only one listener setup will succeed")
if Opts.Protocol == "tcp" {
TCPListen(&listenConfig, logger, errors)
} else {
UDPListen(&listenConfig, logger, errors)
if err != nil {
return err
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
_, ipNet, err := net.ParseCIDR(scanner.Text())
if err != nil {
return err
}
Opts.AllowedSubnets = append(Opts.AllowedSubnets, ipNet)
Opts.Logger.Info("allowed subnet", slog.String("subnet", ipNet.String()))
lvl := slog.LevelInfo
if Opts.Verbose > 0 {
lvl = slog.LevelDebug
Opts.Logger = slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: lvl}))
if err := loadAllowedSubnets(); err != nil {
Opts.Logger.Error("failed to load allowed subnets file", "path", Opts.allowedSubnetsPath, "error", err)
Opts.Logger.Error("--protocol has to be one of udp, tcp", slog.String("protocol", Opts.Protocol))
os.Exit(1)
Opts.Logger.Error("--mark has to be >= 0", slog.Int("mark", Opts.Mark))
os.Exit(1)
Opts.Logger.Error("-v has to be >= 0", slog.Int("verbose", Opts.Verbose))
os.Exit(1)
Opts.Logger.Error("--listeners has to be >= 1")
os.Exit(1)
var err error
if Opts.ListenAddr, err = netip.ParseAddrPort(Opts.ListenAddrStr); err != nil {
Opts.Logger.Error("listen address is malformed", "error", err)
os.Exit(1)
}
if Opts.TargetAddr4, err = netip.ParseAddrPort(Opts.TargetAddr4Str); err != nil {
Opts.Logger.Error("ipv4 target address is malformed", "error", err)
os.Exit(1)
}
if !Opts.TargetAddr4.Addr().Is4() {
Opts.Logger.Error("ipv4 target address is not IPv4")
os.Exit(1)
}
if Opts.TargetAddr6, err = netip.ParseAddrPort(Opts.TargetAddr6Str); err != nil {
Opts.Logger.Error("ipv6 target address is malformed", "error", err)
os.Exit(1)
}
if !Opts.TargetAddr6.Addr().Is6() {
Opts.Logger.Error("ipv6 target address is not IPv6")
os.Exit(1)
}
Opts.Logger.Error("--close-after has to be >= 0", slog.Int("close-after", Opts.udpCloseAfter))
os.Exit(1)
}
Opts.UDPCloseAfter = time.Duration(Opts.udpCloseAfter) * time.Second
listenErrors := make(chan error, Opts.Listeners)
for i := 0; i < Opts.Listeners; i++ {