commit ced539f234af4fbf460787b5f50eb78098406774 Author: meskio meskio@torproject.org Date: Fri Jun 25 18:37:31 2021 +0200
Refactor webRTCConn to its own file --- proxy/snowflake.go | 109 ---------------------------------------------- proxy/webrtcconn.go | 121 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 109 deletions(-)
diff --git a/proxy/snowflake.go b/proxy/snowflake.go index f7eacf8..12d97d3 100644 --- a/proxy/snowflake.go +++ b/proxy/snowflake.go @@ -13,7 +13,6 @@ import ( "net/http" "net/url" "os" - "regexp" "strings" "sync" "time" @@ -23,8 +22,6 @@ import ( "git.torproject.org/pluggable-transports/snowflake.git/common/util" "git.torproject.org/pluggable-transports/snowflake.git/common/websocketconn" "github.com/gorilla/websocket" - "github.com/pion/ice/v2" - "github.com/pion/sdp/v3" "github.com/pion/webrtc/v3" )
@@ -60,117 +57,11 @@ var ( client http.Client )
-var remoteIPPatterns = []*regexp.Regexp{ - /* IPv4 */ - regexp.MustCompile(`(?m)^c=IN IP4 ([\d.]+)(?:(?:/\d+)?/\d+)?(:? |\r?\n)`), - /* IPv6 */ - regexp.MustCompile(`(?m)^c=IN IP6 ([0-9A-Fa-f:.]+)(?:/\d+)?(:? |\r?\n)`), -} - // Checks whether an IP address is a remote address for the client func isRemoteAddress(ip net.IP) bool { return !(util.IsLocal(ip) || ip.IsUnspecified() || ip.IsLoopback()) }
-func remoteIPFromSDP(str string) net.IP { - // Look for remote IP in "a=candidate" attribute fields - // https://tools.ietf.org/html/rfc5245#section-15.1 - var desc sdp.SessionDescription - err := desc.Unmarshal([]byte(str)) - if err != nil { - log.Println("Error parsing SDP: ", err.Error()) - return nil - } - for _, m := range desc.MediaDescriptions { - for _, a := range m.Attributes { - if a.IsICECandidate() { - c, err := ice.UnmarshalCandidate(a.Value) - if err == nil { - ip := net.ParseIP(c.Address()) - if ip != nil && isRemoteAddress(ip) { - return ip - } - } - } - } - } - // Finally look for remote IP in "c=" Connection Data field - // https://tools.ietf.org/html/rfc4566#section-5.7 - for _, pattern := range remoteIPPatterns { - m := pattern.FindStringSubmatch(str) - if m != nil { - // Ignore parsing errors, ParseIP returns nil. - ip := net.ParseIP(m[1]) - if ip != nil && isRemoteAddress(ip) { - return ip - } - - } - } - - return nil -} - -type webRTCConn struct { - dc *webrtc.DataChannel - pc *webrtc.PeerConnection - pr *io.PipeReader - - lock sync.Mutex // Synchronization for DataChannel destruction - once sync.Once // Synchronization for PeerConnection destruction - - bytesLogger BytesLogger -} - -func (c *webRTCConn) Read(b []byte) (int, error) { - return c.pr.Read(b) -} - -func (c *webRTCConn) Write(b []byte) (int, error) { - c.bytesLogger.AddInbound(len(b)) - c.lock.Lock() - defer c.lock.Unlock() - if c.dc != nil { - c.dc.Send(b) - } - return len(b), nil -} - -func (c *webRTCConn) Close() (err error) { - c.once.Do(func() { - err = c.pc.Close() - }) - return -} - -func (c *webRTCConn) LocalAddr() net.Addr { - return nil -} - -func (c *webRTCConn) RemoteAddr() net.Addr { - //Parse Remote SDP offer and extract client IP - clientIP := remoteIPFromSDP(c.pc.RemoteDescription().SDP) - if clientIP == nil { - return nil - } - return &net.IPAddr{IP: clientIP, Zone: ""} -} - -func (c *webRTCConn) SetDeadline(t time.Time) error { - // nolint: golint - return fmt.Errorf("SetDeadline not implemented") -} - -func (c *webRTCConn) SetReadDeadline(t time.Time) error { - // nolint: golint - return fmt.Errorf("SetReadDeadline not implemented") -} - -func (c *webRTCConn) SetWriteDeadline(t time.Time) error { - // nolint: golint - return fmt.Errorf("SetWriteDeadline not implemented") -} - func genSessionID() string { buf := make([]byte, sessionIDLength) _, err := rand.Read(buf) diff --git a/proxy/webrtcconn.go b/proxy/webrtcconn.go new file mode 100644 index 0000000..5d95919 --- /dev/null +++ b/proxy/webrtcconn.go @@ -0,0 +1,121 @@ +package main + +import ( + "fmt" + "io" + "log" + "net" + "regexp" + "sync" + "time" + + "github.com/pion/ice/v2" + "github.com/pion/sdp/v3" + "github.com/pion/webrtc/v3" +) + +var remoteIPPatterns = []*regexp.Regexp{ + /* IPv4 */ + regexp.MustCompile(`(?m)^c=IN IP4 ([\d.]+)(?:(?:/\d+)?/\d+)?(:? |\r?\n)`), + /* IPv6 */ + regexp.MustCompile(`(?m)^c=IN IP6 ([0-9A-Fa-f:.]+)(?:/\d+)?(:? |\r?\n)`), +} + +type webRTCConn struct { + dc *webrtc.DataChannel + pc *webrtc.PeerConnection + pr *io.PipeReader + + lock sync.Mutex // Synchronization for DataChannel destruction + once sync.Once // Synchronization for PeerConnection destruction + + bytesLogger BytesLogger +} + +func (c *webRTCConn) Read(b []byte) (int, error) { + return c.pr.Read(b) +} + +func (c *webRTCConn) Write(b []byte) (int, error) { + c.bytesLogger.AddInbound(len(b)) + c.lock.Lock() + defer c.lock.Unlock() + if c.dc != nil { + c.dc.Send(b) + } + return len(b), nil +} + +func (c *webRTCConn) Close() (err error) { + c.once.Do(func() { + err = c.pc.Close() + }) + return +} + +func (c *webRTCConn) LocalAddr() net.Addr { + return nil +} + +func (c *webRTCConn) RemoteAddr() net.Addr { + //Parse Remote SDP offer and extract client IP + clientIP := remoteIPFromSDP(c.pc.RemoteDescription().SDP) + if clientIP == nil { + return nil + } + return &net.IPAddr{IP: clientIP, Zone: ""} +} + +func (c *webRTCConn) SetDeadline(t time.Time) error { + // nolint: golint + return fmt.Errorf("SetDeadline not implemented") +} + +func (c *webRTCConn) SetReadDeadline(t time.Time) error { + // nolint: golint + return fmt.Errorf("SetReadDeadline not implemented") +} + +func (c *webRTCConn) SetWriteDeadline(t time.Time) error { + // nolint: golint + return fmt.Errorf("SetWriteDeadline not implemented") +} + +func remoteIPFromSDP(str string) net.IP { + // Look for remote IP in "a=candidate" attribute fields + // https://tools.ietf.org/html/rfc5245#section-15.1 + var desc sdp.SessionDescription + err := desc.Unmarshal([]byte(str)) + if err != nil { + log.Println("Error parsing SDP: ", err.Error()) + return nil + } + for _, m := range desc.MediaDescriptions { + for _, a := range m.Attributes { + if a.IsICECandidate() { + c, err := ice.UnmarshalCandidate(a.Value) + if err == nil { + ip := net.ParseIP(c.Address()) + if ip != nil && isRemoteAddress(ip) { + return ip + } + } + } + } + } + // Finally look for remote IP in "c=" Connection Data field + // https://tools.ietf.org/html/rfc4566#section-5.7 + for _, pattern := range remoteIPPatterns { + m := pattern.FindStringSubmatch(str) + if m != nil { + // Ignore parsing errors, ParseIP returns nil. + ip := net.ParseIP(m[1]) + if ip != nil && isRemoteAddress(ip) { + return ip + } + + } + } + + return nil +}