From bb11646e73c3677073024867af13db05394f4504 Mon Sep 17 00:00:00 2001 From: theodorsm Date: Wed, 18 Dec 2024 00:09:15 +0100 Subject: [PATCH] Add CovertDTLSConfig --- client/README.md | 4 +++- client/lib/rendezvous.go | 21 +++++++++--------- client/lib/snowflake.go | 10 +++++---- client/lib/webrtc.go | 29 +++++++++++++++---------- client/snowflake.go | 13 +++++------ common/covertdtls/covert_dtls_config.go | 24 ++++++++++++++++++++ go.mod | 4 ++-- go.sum | 4 ++-- proxy/README.md | 4 ++-- proxy/lib/snowflake.go | 19 +++++++++++----- proxy/main.go | 13 ++++------- 11 files changed, 89 insertions(+), 56 deletions(-) create mode 100644 common/covertdtls/covert_dtls_config.go diff --git a/client/README.md b/client/README.md index 1529e8d..e141dba 100644 --- a/client/README.md +++ b/client/README.md @@ -35,7 +35,7 @@ UseBridges 1 ClientTransportPlugin snowflake exec ./client -log snowflake.log -Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ fronts=foursquare.com,github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn +Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerprint=2B280B23E1107BB62ABFC40DDCC8824814F80A72 url=https://snowflake-broker.torproject.net.global.prod.fastly.net/ fronts=foursquare.com,github.githubassets.com ice=stun:stun.l.google.com:19302,stun:stun.antisip.com:3478,stun:stun.bluesip.net:3478,stun:stun.dus.net:3478,stun:stun.epygi.com:3478,stun:stun.sonetel.com:3478,stun:stun.uls.co.za:3478,stun:stun.voipgate.com:3478,stun:stun.voys.nl:3478 utls-imitate=hellorandomizedalpn covertdtls-config=randomizemimic ``` `fingerprint=` is the fingerprint of bridge that the client will ultimately be connecting to. @@ -48,6 +48,8 @@ Bridge snowflake 192.0.2.3:80 2B280B23E1107BB62ABFC40DDCC8824814F80A72 fingerpri `utls-imitate=` configuration instructs the client to use fingerprinting resistance when connecting when rendez-vous'ing with the broker. +`covertdtls-config=` configuration for randomization or mimicking (Firefox/Chrome browser) of DTLS Client Hello messages. String can be "randomize", "mimic" or "randomizemimc" + To bootstrap Tor, run: ``` tor -f torrc diff --git a/client/lib/rendezvous.go b/client/lib/rendezvous.go index e7245e2..08711da 100644 --- a/client/lib/rendezvous.go +++ b/client/lib/rendezvous.go @@ -17,6 +17,7 @@ import ( utls "github.com/refraction-networking/utls" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/certs" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/nat" @@ -177,10 +178,9 @@ type WebRTCDialer struct { webrtcConfig *webrtc.Configuration max int - eventLogger event.SnowflakeEventReceiver - proxy *url.URL - dtlsRandomize bool - dtlsMimic bool + eventLogger event.SnowflakeEventReceiver + proxy *url.URL + covertDTLSConfig *covertdtls.CovertDTLSConfig } // Deprecated: Use NewWebRTCDialerWithEventsAndProxy instead @@ -214,7 +214,7 @@ func NewWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrt // NewWebRTCDialerWithEventsAndProxy constructs a new WebRTCDialer setting DTLS mimicking and randomization. func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers []webrtc.ICEServer, max int, eventLogger event.SnowflakeEventReceiver, proxy *url.URL, - dtlsRandomize bool, dtlsMimic bool, + covertDTLSConfig *covertdtls.CovertDTLSConfig, ) *WebRTCDialer { config := webrtc.Configuration{ ICEServers: iceServers, @@ -225,10 +225,9 @@ func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers [ webrtcConfig: &config, max: max, - eventLogger: eventLogger, - proxy: proxy, - dtlsRandomize: dtlsRandomize, - dtlsMimic: dtlsMimic, + eventLogger: eventLogger, + proxy: proxy, + covertDTLSConfig: covertDTLSConfig, } } @@ -236,8 +235,8 @@ func NewCovertWebRTCDialerWithEventsAndProxy(broker *BrokerChannel, iceServers [ func (w WebRTCDialer) Catch() (*WebRTCPeer, error) { // TODO: [#25591] Fetch ICE server information from Broker. // TODO: [#25596] Consider TURN servers here too. - if w.dtlsRandomize || w.dtlsMimic { - return NewCovertWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.dtlsRandomize, w.dtlsMimic) + if w.covertDTLSConfig != nil { + return NewCovertWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy, w.covertDTLSConfig) } return NewWebRTCPeerWithEventsAndProxy(w.webrtcConfig, w.BrokerChannel, w.eventLogger, w.proxy) } diff --git a/client/lib/snowflake.go b/client/lib/snowflake.go index 0ab09e4..db0f800 100644 --- a/client/lib/snowflake.go +++ b/client/lib/snowflake.go @@ -40,6 +40,7 @@ import ( "github.com/xtaci/kcp-go/v5" "github.com/xtaci/smux" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/nat" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/turbotunnel" @@ -118,8 +119,7 @@ type ClientConfig struct { BridgeFingerprint string // CommunicationProxy is the proxy address for network communication CommunicationProxy *url.URL - DTLSRandomize bool - DTLSMimic bool + CovertDTLSConfig string } // NewSnowflakeClient creates a new Snowflake transport client that can spawn multiple @@ -165,8 +165,10 @@ func NewSnowflakeClient(config ClientConfig) (*Transport, error) { eventsLogger := event.NewSnowflakeEventDispatcher() var transport *Transport - if config.DTLSRandomize || config.DTLSMimic { - transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, config.DTLSRandomize, config.DTLSMimic), eventDispatcher: eventsLogger} + // TODO: Add fingerprint config + if config.CovertDTLSConfig != "" { + covertDTLSConfig := covertdtls.ParseConfigString(config.CovertDTLSConfig) + transport = &Transport{dialer: NewCovertWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy, &covertDTLSConfig), eventDispatcher: eventsLogger} } else { transport = &Transport{dialer: NewWebRTCDialerWithEventsAndProxy(broker, iceServers, max, eventsLogger, config.CommunicationProxy), eventDispatcher: eventsLogger} } diff --git a/client/lib/webrtc.go b/client/lib/webrtc.go index 5754b59..d6a40e4 100644 --- a/client/lib/webrtc.go +++ b/client/lib/webrtc.go @@ -19,6 +19,7 @@ import ( "github.com/theodorsm/covert-dtls/pkg/randomize" "github.com/theodorsm/covert-dtls/pkg/utils" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/proxy" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/util" @@ -95,7 +96,7 @@ func NewWebRTCPeerWithEventsAndProxy( connection.eventsLogger = eventsLogger connection.proxy = proxy - err := connection.connect(config, broker, false, false) + err := connection.connect(config, broker, nil) if err != nil { connection.Close() return nil, err @@ -107,7 +108,7 @@ func NewWebRTCPeerWithEventsAndProxy( func NewCovertWebRTCPeerWithEventsAndProxy( config *webrtc.Configuration, broker *BrokerChannel, eventsLogger event.SnowflakeEventReceiver, proxy *url.URL, - dtlsRandomize bool, dtlsMimic bool, + covertDTLSConfig *covertdtls.CovertDTLSConfig, ) (*WebRTCPeer, error) { if eventsLogger == nil { eventsLogger = event.NewSnowflakeEventDispatcher() @@ -132,7 +133,7 @@ func NewCovertWebRTCPeerWithEventsAndProxy( connection.eventsLogger = eventsLogger connection.proxy = proxy - err := connection.connect(config, broker, dtlsRandomize, dtlsMimic) + err := connection.connect(config, broker, covertDTLSConfig) if err != nil { connection.Close() return nil, err @@ -206,9 +207,9 @@ func (c *WebRTCPeer) checkForStaleness(timeout time.Duration) { // connect does the bulk of the work: gather ICE candidates, send the SDP offer to broker, // receive an answer from broker, and wait for data channel to open -func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel, dtlsRandomize bool, dtlsMimic bool) error { +func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel, covertDTLSConfig *covertdtls.CovertDTLSConfig) error { log.Println(c.id, " connecting...") - err := c.preparePeerConnection(config, broker.keepLocalAddresses, dtlsRandomize, dtlsMimic) + err := c.preparePeerConnection(config, broker.keepLocalAddresses, covertDTLSConfig) localDescription := c.pc.LocalDescription() c.eventsLogger.OnNewSnowflakeEvent(event.EventOnOfferCreated{ WebRTCLocalDescription: localDescription, @@ -252,8 +253,7 @@ func (c *WebRTCPeer) connect(config *webrtc.Configuration, broker *BrokerChannel func (c *WebRTCPeer) preparePeerConnection( config *webrtc.Configuration, keepLocalAddresses bool, - dtlsRandomize bool, - dtlsMimic bool, + covertDTLSConfig *covertdtls.CovertDTLSConfig, ) error { var err error s := webrtc.SettingEngine{} @@ -287,14 +287,21 @@ func (c *WebRTCPeer) preparePeerConnection( s.SetNet(vnet) - if dtlsRandomize { - rand := randomize.RandomizedMessageClientHello{RandomALPN: true} - s.SetDTLSClientHelloMessageHook(rand.Hook) - } else if dtlsMimic { + if covertDTLSConfig.Mimic { mimic := &mimicry.MimickedClientHello{} + if covertDTLSConfig.Randomize { + err = mimic.LoadRandomFingerprint() + if err != nil { + log.Printf("NewPeerConnection ERROR: %s", err) + return err + } + } profiles := utils.DefaultSRTPProtectionProfiles() s.SetSRTPProtectionProfiles(profiles...) s.SetDTLSClientHelloMessageHook(mimic.Hook) + } else if covertDTLSConfig.Randomize { + rand := randomize.RandomizedMessageClientHello{RandomALPN: true} + s.SetDTLSClientHelloMessageHook(rand.Hook) } api := webrtc.NewAPI(webrtc.WithSettingEngine(s)) diff --git a/client/snowflake.go b/client/snowflake.go index 5474a47..3a43d51 100644 --- a/client/snowflake.go +++ b/client/snowflake.go @@ -123,6 +123,9 @@ func socksAcceptLoop(ln *pt.SocksListener, config sf.ClientConfig, shutdown chan if arg, ok := conn.Req.Args.Get("fingerprint"); ok { config.BridgeFingerprint = arg } + if arg, ok := conn.Req.Args.Get("covertdtls-config"); ok { + config.CovertDTLSConfig = arg + } transport, err := sf.NewSnowflakeClient(config) if err != nil { conn.Reject() @@ -174,8 +177,7 @@ func main() { max := flag.Int("max", DefaultSnowflakeCapacity, "capacity for number of multiplexed WebRTC peers") versionFlag := flag.Bool("version", false, "display version info to stderr and quit") - dtlsRandomize := flag.Bool("dtls-randomize", false, "randomize DTLS client hello") - dtlsMimic := flag.Bool("dtls-mimic", false, "mimic DTLS client hello of Chrome and Firefox") + covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic") // Deprecated oldLogToStateDir := flag.Bool("logToStateDir", false, "use -log-to-state-dir instead") @@ -188,10 +190,6 @@ func main() { os.Exit(0) } - if *dtlsMimic && *dtlsRandomize { - log.Fatal("Cannot both Randomize and Mimic DTLS client hello") - } - log.SetFlags(log.LstdFlags | log.LUTC) // Don't write to stderr; versions of tor earlier than about 0.3.5.6 do @@ -246,8 +244,7 @@ func main() { ICEAddresses: iceAddresses, KeepLocalAddresses: *keepLocalAddresses || *oldKeepLocalAddresses, Max: *max, - DTLSRandomize: *dtlsRandomize, - DTLSMimic: *dtlsMimic, + CovertDTLSConfig: *covertDTLSConfig, } // Begin goptlib client process. diff --git a/common/covertdtls/covert_dtls_config.go b/common/covertdtls/covert_dtls_config.go new file mode 100644 index 0000000..74e6a92 --- /dev/null +++ b/common/covertdtls/covert_dtls_config.go @@ -0,0 +1,24 @@ +package covertdtls + +import ( + "github.com/theodorsm/covert-dtls/pkg/fingerprints" + "strings" +) + +type CovertDTLSConfig struct { + Randomize bool + Mimic bool + Fingerprint fingerprints.ClientHelloFingerprint +} + +func ParseConfigString(str string) CovertDTLSConfig { + config := CovertDTLSConfig{} + str = strings.ToLower(str) + if strings.Contains(str, "random") { + config.Randomize = true + } + if strings.Contains(str, "mimic") { + config.Mimic = true + } + return config +} diff --git a/go.mod b/go.mod index 824b291..17a92f9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,6 @@ require ( github.com/golang/mock v1.6.0 github.com/gorilla/websocket v1.5.3 github.com/miekg/dns v1.1.62 - github.com/pion/dtls/v3 v3.0.4 github.com/pion/ice/v4 v4.0.3 github.com/pion/sdp/v3 v3.0.9 github.com/pion/stun/v3 v3.0.0 @@ -21,7 +20,7 @@ require ( github.com/refraction-networking/utls v1.6.7 github.com/smartystreets/goconvey v1.8.1 github.com/stretchr/testify v1.10.0 - github.com/theodorsm/covert-dtls v0.0.2-0.20241215210721-995fe9f65413 + github.com/theodorsm/covert-dtls v0.0.2-0.20241217214738-5d8466562b2b github.com/txthinking/socks5 v0.0.0-20230325130024-4230056ae301 github.com/xtaci/kcp-go/v5 v5.6.8 github.com/xtaci/smux v1.5.31 @@ -58,6 +57,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pion/datachannel v1.5.9 // indirect + github.com/pion/dtls/v3 v3.0.4 // indirect github.com/pion/interceptor v0.1.37 // indirect github.com/pion/logging v0.2.2 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect diff --git a/go.sum b/go.sum index 4d8b993..4a2c90d 100644 --- a/go.sum +++ b/go.sum @@ -163,8 +163,8 @@ github.com/templexxx/cpu v0.1.0 h1:wVM+WIJP2nYaxVxqgHPD4wGA2aJ9rvrQRV8CvFzNb40= github.com/templexxx/cpu v0.1.0/go.mod h1:w7Tb+7qgcAlIyX4NhLuDKt78AHA5SzPmq0Wj6HiEnnk= github.com/templexxx/xorsimd v0.4.2 h1:ocZZ+Nvu65LGHmCLZ7OoCtg8Fx8jnHKK37SjvngUoVI= github.com/templexxx/xorsimd v0.4.2/go.mod h1:HgwaPoDREdi6OnULpSfxhzaiiSUY4Fi3JPn1wpt28NI= -github.com/theodorsm/covert-dtls v0.0.2-0.20241215210721-995fe9f65413 h1:gR1xoHiOzqQ4bm5EPFk1YVVYNJlPSrz5zu+/yVwNV0A= -github.com/theodorsm/covert-dtls v0.0.2-0.20241215210721-995fe9f65413/go.mod h1:0Gj7OgRe9suVAMrNuuxMczZWVpa4LLuRjduo9d5g6Tc= +github.com/theodorsm/covert-dtls v0.0.2-0.20241217214738-5d8466562b2b h1:bp8+LC6XFBGUEonmXOIQXW9WjQrgGEZ/68ZrKFO0xdQ= +github.com/theodorsm/covert-dtls v0.0.2-0.20241217214738-5d8466562b2b/go.mod h1:0Gj7OgRe9suVAMrNuuxMczZWVpa4LLuRjduo9d5g6Tc= github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/txthinking/runnergroup v0.0.0-20210608031112-152c7c4432bf h1:7PflaKRtU4np/epFxRXlFhlzLXZzKFrH5/I4so5Ove0= diff --git a/proxy/README.md b/proxy/README.md index 032c28b..169ff65 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -46,10 +46,10 @@ Usage of ./proxy: The URL of the broker server that the proxy will be using to find clients (default "https://snowflake-broker.torproject.net/") -capacity uint maximum concurrent clients (default is to accept an unlimited number of clients) + -covertdtls-config string + Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic -disable-stats-logger disable the exposing mechanism for stats using logs - -dtls-mimic - mimic DTLS client hello of Chrome and Firefox -dtls-randomize randomize DTLS client hello -ephemeral-ports-range range diff --git a/proxy/lib/snowflake.go b/proxy/lib/snowflake.go index b91fa83..c649a6f 100644 --- a/proxy/lib/snowflake.go +++ b/proxy/lib/snowflake.go @@ -48,6 +48,7 @@ import ( "github.com/theodorsm/covert-dtls/pkg/randomize" "github.com/theodorsm/covert-dtls/pkg/utils" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/messages" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/namematcher" @@ -170,8 +171,7 @@ type SnowflakeProxy struct { periodicProxyStats *periodicProxyStats bytesLogger bytesLogger - DTLSRandomize bool - DTLSMimic bool + CovertDTLSConfig covertdtls.CovertDTLSConfig } // Checks whether an IP address is a remote address for the client @@ -430,14 +430,21 @@ func (sf *SnowflakeProxy) makeWebRTCAPI() *webrtc.API { settingsEngine.SetDTLSInsecureSkipHelloVerify(true) - if sf.DTLSRandomize { - rand := randomize.RandomizedMessageClientHello{RandomALPN: true} - settingsEngine.SetDTLSClientHelloMessageHook(rand.Hook) - } else if sf.DTLSMimic { + if sf.CovertDTLSConfig.Mimic { mimic := &mimicry.MimickedClientHello{} + if sf.CovertDTLSConfig.Randomize { + err := mimic.LoadRandomFingerprint() + if err != nil { + log.Printf("makeWebRTCAPI ERROR: %s", err) + return nil + } + } profiles := utils.DefaultSRTPProtectionProfiles() settingsEngine.SetSRTPProtectionProfiles(profiles...) settingsEngine.SetDTLSClientHelloMessageHook(mimic.Hook) + } else if sf.CovertDTLSConfig.Randomize { + rand := randomize.RandomizedMessageClientHello{RandomALPN: true} + settingsEngine.SetDTLSClientHelloMessageHook(rand.Hook) } return webrtc.NewAPI(webrtc.WithSettingEngine(settingsEngine)) diff --git a/proxy/main.go b/proxy/main.go index 8ff8981..d031d0e 100644 --- a/proxy/main.go +++ b/proxy/main.go @@ -12,6 +12,7 @@ import ( "time" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/ptutil/safelog" + "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/covertdtls" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/event" "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/common/version" sf "gitlab.torproject.org/tpo/anti-censorship/pluggable-transports/snowflake/v2/proxy/lib" @@ -46,8 +47,7 @@ func main() { verboseLogging := flag.Bool("verbose", false, "increase log verbosity") ephemeralPortsRangeFlag := flag.String("ephemeral-ports-range", "", "Set the `range` of ports used for client connections (format:\":\").\nIf omitted, the ports will be chosen automatically.") versionFlag := flag.Bool("version", false, "display version info to stderr and quit") - dtlsRandomize := flag.Bool("dtls-randomize", false, "randomize DTLS client hello") - dtlsMimic := flag.Bool("dtls-mimic", false, "mimic DTLS client hello of Chrome and Firefox") + covertDTLSConfig := flag.String("covertdtls-config", "", "Configuration of dtls mimicking and randomization: mimic, randomize, randomizemimic") var ephemeralPortsRange []uint16 = []uint16{0, 0} @@ -66,10 +66,6 @@ func main() { log.Fatal("Cannot keep local address candidates when outbound address is specified") } - if *dtlsMimic && *dtlsRandomize { - log.Fatal("Cannot both Randomize and Mimic DTLS client hello") - } - eventLogger := event.NewSnowflakeEventDispatcher() if *ephemeralPortsRangeFlag != "" { @@ -117,9 +113,8 @@ func main() { AllowProxyingToPrivateAddresses: *allowProxyingToPrivateAddresses, AllowNonTLSRelay: *allowNonTLSRelay, - SummaryInterval: *summaryInterval, - DTLSRandomize: *dtlsRandomize, - DTLSMimic: *dtlsMimic, + SummaryInterval: *summaryInterval, + CovertDTLSConfig: covertdtls.ParseConfigString(*covertDTLSConfig), } var logOutput = io.Discard