github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/services/wireguard/endpoint/proxyclient/client.go (about)

     1  /*
     2   * Copyright (C) 2022 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package proxyclient
    19  
    20  import (
    21  	"bufio"
    22  	"context"
    23  	"fmt"
    24  	"net/http"
    25  	"net/netip"
    26  	"strings"
    27  	"sync"
    28  	"time"
    29  
    30  	"github.com/rs/zerolog/log"
    31  	"golang.zx2c4.com/wireguard/conn"
    32  	"golang.zx2c4.com/wireguard/device"
    33  
    34  	"github.com/mysteriumnetwork/node/services/wireguard/endpoint/netstack"
    35  	"github.com/mysteriumnetwork/node/services/wireguard/endpoint/userspace"
    36  	"github.com/mysteriumnetwork/node/services/wireguard/wgcfg"
    37  )
    38  
    39  type client struct {
    40  	mu         sync.Mutex
    41  	Device     *device.Device
    42  	proxyClose func() error
    43  }
    44  
    45  // New create new WireGuard client which serves requests via proxy.
    46  func New() (*client, error) {
    47  	log.Debug().Msg("Creating proxy wg client")
    48  	return &client{}, nil
    49  }
    50  
    51  func (c *client) ReConfigureDevice(config wgcfg.DeviceConfig) error {
    52  	return c.ConfigureDevice(config)
    53  }
    54  
    55  func (c *client) ConfigureDevice(cfg wgcfg.DeviceConfig) error {
    56  	localAddr, err := netip.ParseAddr(cfg.Subnet.IP.String())
    57  	if err != nil {
    58  		return fmt.Errorf("could not parse local addr: %w", err)
    59  	}
    60  	if len(cfg.DNS) == 0 {
    61  		return fmt.Errorf("DNS addr list is empty")
    62  	}
    63  	dnsAddr, err := netip.ParseAddr(cfg.DNS[0])
    64  	if err != nil {
    65  		return fmt.Errorf("could not parse DNS addr: %w", err)
    66  	}
    67  	tunnel, tnet, err := netstack.CreateNetTUN([]netip.Addr{localAddr}, []netip.Addr{dnsAddr}, device.DefaultMTU)
    68  	if err != nil {
    69  		return fmt.Errorf("failed to create netstack device %s: %w", cfg.IfaceName, err)
    70  	}
    71  
    72  	logger := device.NewLogger(device.LogLevelVerbose, fmt.Sprintf("(%s) ", cfg.IfaceName))
    73  	wgDevice := device.NewDevice(tunnel, conn.NewDefaultBind(), logger)
    74  
    75  	log.Info().Msg("Applying interface configuration")
    76  	if err := wgDevice.IpcSetOperation(bufio.NewReader(strings.NewReader(cfg.Encode()))); err != nil {
    77  		wgDevice.Close()
    78  		return fmt.Errorf("could not set device uapi config: %w", err)
    79  	}
    80  
    81  	log.Info().Msg("Bringing device up")
    82  	wgDevice.Up()
    83  
    84  	c.mu.Lock()
    85  	c.Device = wgDevice
    86  	c.mu.Unlock()
    87  
    88  	if err := c.Proxy(tnet, cfg.ProxyPort); err != nil {
    89  		wgDevice.Close()
    90  		return err
    91  	}
    92  
    93  	return nil
    94  }
    95  
    96  func (c *client) DestroyDevice(iface string) error {
    97  	return c.Close()
    98  }
    99  
   100  func (c *client) PeerStats(iface string) (wgcfg.Stats, error) {
   101  	deviceState, err := userspace.ParseUserspaceDevice(c.Device.IpcGetOperation)
   102  	if err != nil {
   103  		return wgcfg.Stats{}, fmt.Errorf("could not parse device state: %w", err)
   104  	}
   105  
   106  	stats, statErr := userspace.ParseDevicePeerStats(deviceState)
   107  	if err != nil {
   108  		err = statErr
   109  		log.Warn().Err(err).Msg("Failed to parse device stats, will try again")
   110  	} else {
   111  		return stats, nil
   112  	}
   113  
   114  	return wgcfg.Stats{}, fmt.Errorf("could not parse device state: %w", err)
   115  }
   116  
   117  func (c *client) Close() (err error) {
   118  	c.mu.Lock()
   119  	defer c.mu.Unlock()
   120  
   121  	if c.proxyClose != nil {
   122  		c.proxyClose()
   123  	}
   124  
   125  	if c.Device != nil {
   126  		go func() {
   127  			time.Sleep(2 * time.Minute)
   128  			c.Device.Close()
   129  		}()
   130  	}
   131  	return nil
   132  }
   133  
   134  func (c *client) Proxy(tnet *netstack.Net, proxyPort int) error {
   135  	c.mu.Lock()
   136  	defer c.mu.Unlock()
   137  
   138  	server := http.Server{
   139  		Addr:              fmt.Sprintf(":%d", proxyPort),
   140  		Handler:           newProxyHandler(60*time.Second, tnet),
   141  		ReadTimeout:       0,
   142  		ReadHeaderTimeout: 0,
   143  		WriteTimeout:      0,
   144  		IdleTimeout:       0,
   145  	}
   146  
   147  	log.Info().Msgf("Starting proxy server at :%d ...", proxyPort)
   148  	c.proxyClose = func() error {
   149  		ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second)
   150  		defer cancel()
   151  		server.Shutdown(ctx)
   152  
   153  		return server.Close()
   154  	}
   155  
   156  	go func() {
   157  		err := server.ListenAndServe()
   158  		log.Error().Err(err).Msg("Shutting down proxy server...")
   159  	}()
   160  
   161  	return nil
   162  }