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