1package snowflake_proxy 2 3import ( 4 "fmt" 5 "io" 6 "log" 7 "net" 8 "regexp" 9 "sync" 10 "time" 11 12 "github.com/pion/ice/v2" 13 "github.com/pion/sdp/v3" 14 "github.com/pion/webrtc/v3" 15) 16 17var remoteIPPatterns = []*regexp.Regexp{ 18 /* IPv4 */ 19 regexp.MustCompile(`(?m)^c=IN IP4 ([\d.]+)(?:(?:\/\d+)?\/\d+)?(:? |\r?\n)`), 20 /* IPv6 */ 21 regexp.MustCompile(`(?m)^c=IN IP6 ([0-9A-Fa-f:.]+)(?:\/\d+)?(:? |\r?\n)`), 22} 23 24type webRTCConn struct { 25 dc *webrtc.DataChannel 26 pc *webrtc.PeerConnection 27 pr *io.PipeReader 28 29 lock sync.Mutex // Synchronization for DataChannel destruction 30 once sync.Once // Synchronization for PeerConnection destruction 31 32 bytesLogger bytesLogger 33} 34 35func (c *webRTCConn) Read(b []byte) (int, error) { 36 return c.pr.Read(b) 37} 38 39func (c *webRTCConn) Write(b []byte) (int, error) { 40 c.bytesLogger.AddInbound(len(b)) 41 c.lock.Lock() 42 defer c.lock.Unlock() 43 if c.dc != nil { 44 c.dc.Send(b) 45 } 46 return len(b), nil 47} 48 49func (c *webRTCConn) Close() (err error) { 50 c.once.Do(func() { 51 err = c.pc.Close() 52 }) 53 return 54} 55 56func (c *webRTCConn) LocalAddr() net.Addr { 57 return nil 58} 59 60func (c *webRTCConn) RemoteAddr() net.Addr { 61 //Parse Remote SDP offer and extract client IP 62 clientIP := remoteIPFromSDP(c.pc.RemoteDescription().SDP) 63 if clientIP == nil { 64 return nil 65 } 66 return &net.IPAddr{IP: clientIP, Zone: ""} 67} 68 69func (c *webRTCConn) SetDeadline(t time.Time) error { 70 // nolint: golint 71 return fmt.Errorf("SetDeadline not implemented") 72} 73 74func (c *webRTCConn) SetReadDeadline(t time.Time) error { 75 // nolint: golint 76 return fmt.Errorf("SetReadDeadline not implemented") 77} 78 79func (c *webRTCConn) SetWriteDeadline(t time.Time) error { 80 // nolint: golint 81 return fmt.Errorf("SetWriteDeadline not implemented") 82} 83 84func remoteIPFromSDP(str string) net.IP { 85 // Look for remote IP in "a=candidate" attribute fields 86 // https://tools.ietf.org/html/rfc5245#section-15.1 87 var desc sdp.SessionDescription 88 err := desc.Unmarshal([]byte(str)) 89 if err != nil { 90 log.Println("Error parsing SDP: ", err.Error()) 91 return nil 92 } 93 for _, m := range desc.MediaDescriptions { 94 for _, a := range m.Attributes { 95 if a.IsICECandidate() { 96 c, err := ice.UnmarshalCandidate(a.Value) 97 if err == nil { 98 ip := net.ParseIP(c.Address()) 99 if ip != nil && isRemoteAddress(ip) { 100 return ip 101 } 102 } 103 } 104 } 105 } 106 // Finally look for remote IP in "c=" Connection Data field 107 // https://tools.ietf.org/html/rfc4566#section-5.7 108 for _, pattern := range remoteIPPatterns { 109 m := pattern.FindStringSubmatch(str) 110 if m != nil { 111 // Ignore parsing errors, ParseIP returns nil. 112 ip := net.ParseIP(m[1]) 113 if ip != nil && isRemoteAddress(ip) { 114 return ip 115 } 116 117 } 118 } 119 120 return nil 121} 122