Chore: merge branch 'with-tun' into plus-pro
This commit is contained in:
@@ -19,12 +19,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
|
||||
client := newClient(c.RemoteAddr(), in)
|
||||
defer client.CloseIdleConnections()
|
||||
|
||||
var conn *N.BufferedConn
|
||||
if bufConn, ok := c.(*N.BufferedConn); ok {
|
||||
conn = bufConn
|
||||
} else {
|
||||
conn = N.NewBufferedConn(c)
|
||||
}
|
||||
conn := N.NewBufferedConn(c)
|
||||
|
||||
keepAlive := true
|
||||
trusted := cache == nil // disable authenticate if cache is nil
|
||||
@@ -66,15 +61,23 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
|
||||
|
||||
request.RequestURI = ""
|
||||
|
||||
RemoveHopByHopHeaders(request.Header)
|
||||
RemoveExtraHTTPHostPort(request)
|
||||
if isUpgradeRequest(request) {
|
||||
if resp = HandleUpgrade(conn, conn.RemoteAddr(), request, in); resp == nil {
|
||||
return // hijack connection
|
||||
}
|
||||
}
|
||||
|
||||
if request.URL.Scheme == "" || request.URL.Host == "" {
|
||||
resp = responseWith(request, http.StatusBadRequest)
|
||||
} else {
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
resp = responseWith(request, http.StatusBadGateway)
|
||||
if resp == nil {
|
||||
RemoveHopByHopHeaders(request.Header)
|
||||
RemoveExtraHTTPHostPort(request)
|
||||
|
||||
if request.URL.Scheme == "" || request.URL.Host == "" {
|
||||
resp = responseWith(request, http.StatusBadRequest)
|
||||
} else {
|
||||
resp, err = client.Do(request)
|
||||
if err != nil {
|
||||
resp = responseWith(request, http.StatusBadGateway)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +98,7 @@ func HandleConn(c net.Conn, in chan<- C.ConnContext, cache *cache.Cache[string,
|
||||
}
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
_ = conn.Close()
|
||||
}
|
||||
|
||||
func Authenticate(request *http.Request, cache *cache.Cache[string, bool]) *http.Response {
|
||||
|
||||
96
listener/http/upgrade.go
Normal file
96
listener/http/upgrade.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
N "github.com/Dreamacro/clash/common/net"
|
||||
C "github.com/Dreamacro/clash/constant"
|
||||
"github.com/Dreamacro/clash/transport/socks5"
|
||||
)
|
||||
|
||||
func isUpgradeRequest(req *http.Request) bool {
|
||||
return strings.EqualFold(req.Header.Get("Connection"), "Upgrade")
|
||||
}
|
||||
|
||||
func HandleUpgrade(localConn net.Conn, source net.Addr, request *http.Request, in chan<- C.ConnContext) (resp *http.Response) {
|
||||
removeProxyHeaders(request.Header)
|
||||
RemoveExtraHTTPHostPort(request)
|
||||
|
||||
address := request.Host
|
||||
if _, _, err := net.SplitHostPort(address); err != nil {
|
||||
port := "80"
|
||||
if request.TLS != nil {
|
||||
port = "443"
|
||||
}
|
||||
address = net.JoinHostPort(address, port)
|
||||
}
|
||||
|
||||
dstAddr := socks5.ParseAddr(address)
|
||||
if dstAddr == nil {
|
||||
return
|
||||
}
|
||||
|
||||
left, right := net.Pipe()
|
||||
|
||||
in <- inbound.NewMitm(dstAddr, source, request.Header.Get("User-Agent"), right)
|
||||
|
||||
var remoteServer *N.BufferedConn
|
||||
if request.TLS != nil {
|
||||
tlsConn := tls.Client(left, &tls.Config{
|
||||
ServerName: request.URL.Hostname(),
|
||||
})
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)
|
||||
defer cancel()
|
||||
if tlsConn.HandshakeContext(ctx) != nil {
|
||||
_ = localConn.Close()
|
||||
_ = left.Close()
|
||||
return
|
||||
}
|
||||
|
||||
remoteServer = N.NewBufferedConn(tlsConn)
|
||||
} else {
|
||||
remoteServer = N.NewBufferedConn(left)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = remoteServer.Close()
|
||||
}()
|
||||
|
||||
err := request.Write(remoteServer)
|
||||
if err != nil {
|
||||
_ = localConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
resp, err = http.ReadResponse(remoteServer.Reader(), request)
|
||||
if err != nil {
|
||||
_ = localConn.Close()
|
||||
return
|
||||
}
|
||||
|
||||
if resp.StatusCode == http.StatusSwitchingProtocols {
|
||||
removeProxyHeaders(resp.Header)
|
||||
|
||||
err = localConn.SetReadDeadline(time.Time{}) // set to not time out
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
err = resp.Write(localConn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
N.Relay(remoteServer, localConn) // blocking here
|
||||
_ = localConn.Close()
|
||||
resp = nil
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -8,15 +8,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// removeProxyHeaders remove Proxy-* headers
|
||||
func removeProxyHeaders(header http.Header) {
|
||||
header.Del("Proxy-Connection")
|
||||
header.Del("Proxy-Authenticate")
|
||||
header.Del("Proxy-Authorization")
|
||||
}
|
||||
|
||||
// RemoveHopByHopHeaders remove hop-by-hop header
|
||||
func RemoveHopByHopHeaders(header http.Header) {
|
||||
// Strip hop-by-hop header based on RFC:
|
||||
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1
|
||||
// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do
|
||||
|
||||
header.Del("Proxy-Connection")
|
||||
header.Del("Proxy-Authenticate")
|
||||
header.Del("Proxy-Authorization")
|
||||
removeProxyHeaders(header)
|
||||
|
||||
header.Del("TE")
|
||||
header.Del("Trailers")
|
||||
header.Del("Transfer-Encoding")
|
||||
|
||||
@@ -397,13 +397,12 @@ func ReCreateMitm(port int, tcpIn chan<- C.ConnContext) {
|
||||
certOption, err = cert.NewConfig(
|
||||
x509c,
|
||||
privateKey,
|
||||
cert.NewAutoGCCertsStorage(),
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
certOption.SetValidity(time.Hour * 24 * 90)
|
||||
certOption.SetValidity(time.Hour * 24 * 365 * 2) // 2 years
|
||||
certOption.SetOrganization("Clash ManInTheMiddle Proxy Services")
|
||||
|
||||
opt := &mitm.Option{
|
||||
|
||||
@@ -18,9 +18,11 @@ func newClient(source net.Addr, userAgent string, in chan<- C.ConnContext) *http
|
||||
Transport: &http.Transport{
|
||||
// excepted HTTP/2
|
||||
TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper),
|
||||
// from http.DefaultTransport
|
||||
MaxIdleConns: 100,
|
||||
IdleConnTimeout: 90 * time.Second,
|
||||
// only needed 1 connection
|
||||
MaxIdleConns: 1,
|
||||
MaxIdleConnsPerHost: 1,
|
||||
MaxConnsPerHost: 1,
|
||||
IdleConnTimeout: 60 * time.Second,
|
||||
TLSHandshakeTimeout: 10 * time.Second,
|
||||
ExpectContinueTimeout: 1 * time.Second,
|
||||
TLSClientConfig: &tls.Config{
|
||||
|
||||
@@ -44,13 +44,13 @@ startOver:
|
||||
readLoop:
|
||||
for {
|
||||
// use SetReadDeadline instead of Proxy-Connection keep-alive
|
||||
if err := conn.SetReadDeadline(time.Now().Add(95 * time.Second)); err != nil {
|
||||
break readLoop
|
||||
if err := conn.SetReadDeadline(time.Now().Add(65 * time.Second)); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
request, err := H.ReadRequest(conn.Reader())
|
||||
if err != nil {
|
||||
break readLoop
|
||||
break
|
||||
}
|
||||
|
||||
var response *http.Response
|
||||
@@ -71,7 +71,7 @@ readLoop:
|
||||
// Manual writing to support CONNECT for http 1.0 (workaround for uplay client)
|
||||
if _, err = fmt.Fprintf(session.conn, "HTTP/%d.%d %03d %s\r\n\r\n", session.request.ProtoMajor, session.request.ProtoMinor, http.StatusOK, "Connection established"); err != nil {
|
||||
handleError(opt, session, err)
|
||||
break readLoop // close connection
|
||||
break // close connection
|
||||
}
|
||||
|
||||
if strings.HasSuffix(session.request.URL.Host, ":80") {
|
||||
@@ -81,18 +81,18 @@ readLoop:
|
||||
b, err := conn.Peek(1)
|
||||
if err != nil {
|
||||
handleError(opt, session, err)
|
||||
break readLoop // close connection
|
||||
break // close connection
|
||||
}
|
||||
|
||||
// TLS handshake.
|
||||
if b[0] == 0x16 {
|
||||
tlsConn := tls.Server(conn, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Host))
|
||||
tlsConn := tls.Server(conn, opt.CertConfig.NewTLSConfigForHost(session.request.URL.Hostname()))
|
||||
|
||||
// Handshake with the local client
|
||||
if err = tlsConn.Handshake(); err != nil {
|
||||
session.response = session.NewErrorResponse(fmt.Errorf("handshake failed: %w", err))
|
||||
_ = writeResponse(session, false)
|
||||
break readLoop // close connection
|
||||
break // close connection
|
||||
}
|
||||
|
||||
c = tlsConn
|
||||
@@ -105,20 +105,27 @@ readLoop:
|
||||
|
||||
prepareRequest(c, session.request)
|
||||
|
||||
H.RemoveHopByHopHeaders(session.request.Header)
|
||||
H.RemoveExtraHTTPHostPort(session.request)
|
||||
|
||||
// hijack api
|
||||
if session.request.URL.Hostname() == opt.ApiHost {
|
||||
if err = handleApiRequest(session, opt); err != nil {
|
||||
handleError(opt, session, err)
|
||||
break readLoop
|
||||
}
|
||||
return
|
||||
break
|
||||
}
|
||||
|
||||
// forward websocket
|
||||
if isWebsocketRequest(request) {
|
||||
session.request.RequestURI = ""
|
||||
if session.response = H.HandleUpgrade(conn, source, request, in); session.response == nil {
|
||||
return // hijack connection
|
||||
}
|
||||
}
|
||||
|
||||
H.RemoveHopByHopHeaders(session.request.Header)
|
||||
H.RemoveExtraHTTPHostPort(session.request)
|
||||
|
||||
// hijack custom request and write back custom response if necessary
|
||||
if opt.Handler != nil {
|
||||
if opt.Handler != nil && session.response == nil {
|
||||
newReq, newRes := opt.Handler.HandleRequest(session)
|
||||
if newReq != nil {
|
||||
session.request = newReq
|
||||
@@ -128,28 +135,30 @@ readLoop:
|
||||
|
||||
if err = writeResponse(session, false); err != nil {
|
||||
handleError(opt, session, err)
|
||||
break readLoop
|
||||
break
|
||||
}
|
||||
return
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
session.request.RequestURI = ""
|
||||
if session.response == nil {
|
||||
session.request.RequestURI = ""
|
||||
|
||||
if session.request.URL.Host == "" {
|
||||
session.response = session.NewErrorResponse(ErrInvalidURL)
|
||||
} else {
|
||||
client = newClientBySourceAndUserAgentIfNil(client, session.request, source, in)
|
||||
if session.request.URL.Host == "" {
|
||||
session.response = session.NewErrorResponse(ErrInvalidURL)
|
||||
} else {
|
||||
client = newClientBySourceAndUserAgentIfNil(client, session.request, source, in)
|
||||
|
||||
// send the request to remote server
|
||||
session.response, err = client.Do(session.request)
|
||||
// send the request to remote server
|
||||
session.response, err = client.Do(session.request)
|
||||
|
||||
if err != nil {
|
||||
handleError(opt, session, err)
|
||||
session.response = session.NewErrorResponse(err)
|
||||
if errors.Is(err, ErrCertUnsupported) || strings.Contains(err.Error(), "x509: ") {
|
||||
_ = writeResponse(session, false)
|
||||
break readLoop
|
||||
if err != nil {
|
||||
handleError(opt, session, err)
|
||||
session.response = session.NewErrorResponse(fmt.Errorf("request failed: %w", err))
|
||||
if errors.Is(err, ErrCertUnsupported) || strings.Contains(err.Error(), "x509: ") {
|
||||
_ = writeResponse(session, false)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -157,7 +166,7 @@ readLoop:
|
||||
|
||||
if err = writeResponseWithHandler(session, opt); err != nil {
|
||||
handleError(opt, session, err)
|
||||
break readLoop // close connection
|
||||
break // close connection
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,13 +176,7 @@ readLoop:
|
||||
func writeResponseWithHandler(session *Session, opt *Option) error {
|
||||
if opt.Handler != nil {
|
||||
res := opt.Handler.HandleResponse(session)
|
||||
|
||||
if res != nil {
|
||||
body := res.Body
|
||||
defer func(body io.ReadCloser) {
|
||||
_ = body.Close()
|
||||
}(body)
|
||||
|
||||
session.response = res
|
||||
}
|
||||
}
|
||||
@@ -186,7 +189,7 @@ func writeResponse(session *Session, keepAlive bool) error {
|
||||
|
||||
if keepAlive {
|
||||
session.response.Header.Set("Connection", "keep-alive")
|
||||
session.response.Header.Set("Keep-Alive", "timeout=90")
|
||||
session.response.Header.Set("Keep-Alive", "timeout=60")
|
||||
}
|
||||
|
||||
return session.writeResponse()
|
||||
@@ -201,10 +204,6 @@ func handleApiRequest(session *Session, opt *Option) error {
|
||||
|
||||
session.response = session.NewResponse(http.StatusOK, bytes.NewReader(b))
|
||||
|
||||
defer func(body io.ReadCloser) {
|
||||
_ = body.Close()
|
||||
}(session.response.Body)
|
||||
|
||||
session.response.Close = true
|
||||
session.response.Header.Set("Content-Type", "application/x-x509-ca-cert")
|
||||
session.response.ContentLength = int64(len(b))
|
||||
@@ -230,11 +229,6 @@ func handleApiRequest(session *Session, opt *Option) error {
|
||||
b = fmt.Sprintf(b, session.request.URL.Path)
|
||||
|
||||
session.response = session.NewResponse(http.StatusNotFound, bytes.NewReader([]byte(b)))
|
||||
|
||||
defer func(body io.ReadCloser) {
|
||||
_ = body.Close()
|
||||
}(session.response.Body)
|
||||
|
||||
session.response.Close = true
|
||||
session.response.Header.Set("Content-Type", "text/html;charset=utf-8")
|
||||
session.response.ContentLength = int64(len(b))
|
||||
@@ -243,6 +237,12 @@ func handleApiRequest(session *Session, opt *Option) error {
|
||||
}
|
||||
|
||||
func handleError(opt *Option, session *Session, err error) {
|
||||
if session.response != nil {
|
||||
defer func() {
|
||||
_, _ = io.Copy(io.Discard, session.response.Body)
|
||||
_ = session.response.Body.Close()
|
||||
}()
|
||||
}
|
||||
if opt.Handler != nil {
|
||||
opt.Handler.HandleError(session, err)
|
||||
}
|
||||
|
||||
@@ -43,6 +43,9 @@ func (s *Session) writeResponse() error {
|
||||
if s.response == nil {
|
||||
return ErrInvalidResponse
|
||||
}
|
||||
defer func(resp *http.Response) {
|
||||
_ = resp.Body.Close()
|
||||
}(s.response)
|
||||
return s.response.Write(s.conn)
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,10 @@ var (
|
||||
ErrInvalidURL = errors.New("invalid URL")
|
||||
)
|
||||
|
||||
func isWebsocketRequest(req *http.Request) bool {
|
||||
return req.Header.Get("Connection") == "Upgrade" && req.Header.Get("Upgrade") == "websocket"
|
||||
}
|
||||
|
||||
func NewResponse(code int, body io.Reader, req *http.Request) *http.Response {
|
||||
if body == nil {
|
||||
body = &bytes.Buffer{}
|
||||
|
||||
@@ -27,10 +27,8 @@ func StartListener(device io.ReadWriteCloser, gateway, portal, broadcast netip.A
|
||||
}
|
||||
|
||||
func (t *StackListener) Close() error {
|
||||
_ = t.tcp.Close()
|
||||
_ = t.udp.Close()
|
||||
|
||||
return t.device.Close()
|
||||
return t.tcp.Close()
|
||||
}
|
||||
|
||||
func (t *StackListener) TCP() *nat.TCP {
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
package nat
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"net/netip"
|
||||
|
||||
"github.com/Dreamacro/clash/common/generics/list"
|
||||
)
|
||||
|
||||
const (
|
||||
portBegin = 30000
|
||||
portLength = 4096
|
||||
portLength = 10240
|
||||
)
|
||||
|
||||
var zeroTuple = tuple{}
|
||||
@@ -23,9 +24,9 @@ type binding struct {
|
||||
}
|
||||
|
||||
type table struct {
|
||||
tuples map[tuple]*list.Element
|
||||
ports [portLength]*list.Element
|
||||
available *list.List
|
||||
tuples map[tuple]*list.Element[*binding]
|
||||
ports [portLength]*list.Element[*binding]
|
||||
available *list.List[*binding]
|
||||
}
|
||||
|
||||
func (t *table) tupleOf(port uint16) tuple {
|
||||
@@ -38,7 +39,7 @@ func (t *table) tupleOf(port uint16) tuple {
|
||||
|
||||
t.available.MoveToFront(elm)
|
||||
|
||||
return elm.Value.(*binding).tuple
|
||||
return elm.Value.tuple
|
||||
}
|
||||
|
||||
func (t *table) portOf(tuple tuple) uint16 {
|
||||
@@ -49,12 +50,12 @@ func (t *table) portOf(tuple tuple) uint16 {
|
||||
|
||||
t.available.MoveToFront(elm)
|
||||
|
||||
return portBegin + elm.Value.(*binding).offset
|
||||
return portBegin + elm.Value.offset
|
||||
}
|
||||
|
||||
func (t *table) newConn(tuple tuple) uint16 {
|
||||
elm := t.available.Back()
|
||||
b := elm.Value.(*binding)
|
||||
b := elm.Value
|
||||
|
||||
delete(t.tuples, b.tuple)
|
||||
t.tuples[tuple] = elm
|
||||
@@ -67,9 +68,9 @@ func (t *table) newConn(tuple tuple) uint16 {
|
||||
|
||||
func newTable() *table {
|
||||
result := &table{
|
||||
tuples: make(map[tuple]*list.Element, portLength),
|
||||
ports: [portLength]*list.Element{},
|
||||
available: list.New(),
|
||||
tuples: make(map[tuple]*list.Element[*binding], portLength),
|
||||
ports: [portLength]*list.Element[*binding]{},
|
||||
available: list.New[*binding](),
|
||||
}
|
||||
|
||||
for idx := range result.ports {
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"net/netip"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Dreamacro/clash/adapter/inbound"
|
||||
@@ -28,6 +29,8 @@ type sysStack struct {
|
||||
device device.Device
|
||||
|
||||
closed bool
|
||||
once sync.Once
|
||||
wg sync.WaitGroup
|
||||
}
|
||||
|
||||
func (s *sysStack) Close() error {
|
||||
@@ -38,10 +41,12 @@ func (s *sysStack) Close() error {
|
||||
}()
|
||||
|
||||
s.closed = true
|
||||
if s.stack != nil {
|
||||
return s.stack.Close()
|
||||
}
|
||||
return nil
|
||||
|
||||
err := s.stack.Close()
|
||||
|
||||
s.wg.Wait()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Prefix, tcpIn chan<- C.ConnContext, udpIn chan<- *inbound.PacketAdapter) (ipstack.Stack, error) {
|
||||
@@ -67,16 +72,10 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||
_ = tcp.Close()
|
||||
}(stack.TCP())
|
||||
|
||||
defer log.Debugln("TCP: closed")
|
||||
|
||||
for !ipStack.closed {
|
||||
if err = stack.TCP().SetDeadline(time.Time{}); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
conn, err := stack.TCP().Accept()
|
||||
if err != nil {
|
||||
log.Debugln("Accept connection: %v", err)
|
||||
log.Debugln("[STACK] accept connection error: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -146,6 +145,8 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||
|
||||
tcpIn <- context.NewConnContext(conn, metadata)
|
||||
}
|
||||
|
||||
ipStack.wg.Done()
|
||||
}
|
||||
|
||||
udp := func() {
|
||||
@@ -153,14 +154,13 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||
_ = udp.Close()
|
||||
}(stack.UDP())
|
||||
|
||||
defer log.Debugln("UDP: closed")
|
||||
|
||||
for !ipStack.closed {
|
||||
buf := pool.Get(pool.UDPBufferSize)
|
||||
|
||||
n, lRAddr, rRAddr, err := stack.UDP().ReadFrom(buf)
|
||||
if err != nil {
|
||||
return
|
||||
_ = pool.Put(buf)
|
||||
break
|
||||
}
|
||||
|
||||
raw := buf[:n]
|
||||
@@ -209,17 +209,23 @@ func New(device device.Device, dnsHijack []netip.AddrPort, tunAddress netip.Pref
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
ipStack.wg.Done()
|
||||
}
|
||||
|
||||
go tcp()
|
||||
ipStack.once.Do(func() {
|
||||
ipStack.wg.Add(1)
|
||||
go tcp()
|
||||
|
||||
numUDPWorkers := 4
|
||||
if num := runtime.GOMAXPROCS(0); num > numUDPWorkers {
|
||||
numUDPWorkers = num
|
||||
}
|
||||
for i := 0; i < numUDPWorkers; i++ {
|
||||
go udp()
|
||||
}
|
||||
numUDPWorkers := 4
|
||||
if num := runtime.GOMAXPROCS(0); num > numUDPWorkers {
|
||||
numUDPWorkers = num
|
||||
}
|
||||
for i := 0; i < numUDPWorkers; i++ {
|
||||
ipStack.wg.Add(1)
|
||||
go udp()
|
||||
}
|
||||
})
|
||||
|
||||
return ipStack, nil
|
||||
}
|
||||
|
||||
@@ -145,6 +145,7 @@ func setAtLatest(stackType C.TUNStack, devName string) {
|
||||
case "darwin":
|
||||
// _, _ = cmd.ExecCmd("sysctl -w net.inet.ip.forwarding=1")
|
||||
// _, _ = cmd.ExecCmd("sysctl -w net.inet6.ip6.forwarding=1")
|
||||
_, _ = cmd.ExecCmd("sudo launchctl limit maxfiles 10240 unlimited")
|
||||
case "windows":
|
||||
_, _ = cmd.ExecCmd("ipconfig /renew")
|
||||
case "linux":
|
||||
|
||||
Reference in New Issue
Block a user