1package util
2
3import (
4	"encoding/json"
5	"errors"
6	"net"
7
8	"github.com/pion/ice/v2"
9	"github.com/pion/sdp/v3"
10	"github.com/pion/webrtc/v3"
11)
12
13func SerializeSessionDescription(desc *webrtc.SessionDescription) (string, error) {
14	bytes, err := json.Marshal(*desc)
15	if err != nil {
16		return "", err
17	}
18	return string(bytes), nil
19}
20
21func DeserializeSessionDescription(msg string) (*webrtc.SessionDescription, error) {
22	var parsed map[string]interface{}
23	err := json.Unmarshal([]byte(msg), &parsed)
24	if err != nil {
25		return nil, err
26	}
27	if _, ok := parsed["type"]; !ok {
28		return nil, errors.New("cannot deserialize SessionDescription without type field")
29	}
30	if _, ok := parsed["sdp"]; !ok {
31		return nil, errors.New("cannot deserialize SessionDescription without sdp field")
32	}
33
34	var stype webrtc.SDPType
35	switch parsed["type"].(string) {
36	default:
37		return nil, errors.New("Unknown SDP type")
38	case "offer":
39		stype = webrtc.SDPTypeOffer
40	case "pranswer":
41		stype = webrtc.SDPTypePranswer
42	case "answer":
43		stype = webrtc.SDPTypeAnswer
44	case "rollback":
45		stype = webrtc.SDPTypeRollback
46	}
47
48	return &webrtc.SessionDescription{
49		Type: stype,
50		SDP:  parsed["sdp"].(string),
51	}, nil
52}
53
54// Stolen from https://github.com/golang/go/pull/30278
55func IsLocal(ip net.IP) bool {
56	if ip4 := ip.To4(); ip4 != nil {
57		// Local IPv4 addresses are defined in https://tools.ietf.org/html/rfc1918
58		return ip4[0] == 10 ||
59			(ip4[0] == 172 && ip4[1]&0xf0 == 16) ||
60			(ip4[0] == 192 && ip4[1] == 168) ||
61			// Carrier-Grade NAT as per https://tools.ietf.org/htm/rfc6598
62			(ip4[0] == 100 && ip4[1]&0xc0 == 64) ||
63			// Dynamic Configuration as per https://tools.ietf.org/htm/rfc3927
64			(ip4[0] == 169 && ip4[1] == 254)
65	}
66	// Local IPv6 addresses are defined in https://tools.ietf.org/html/rfc4193
67	return len(ip) == net.IPv6len && ip[0]&0xfe == 0xfc
68}
69
70// Removes local LAN address ICE candidates
71func StripLocalAddresses(str string) string {
72	var desc sdp.SessionDescription
73	err := desc.Unmarshal([]byte(str))
74	if err != nil {
75		return str
76	}
77	for _, m := range desc.MediaDescriptions {
78		attrs := make([]sdp.Attribute, 0)
79		for _, a := range m.Attributes {
80			if a.IsICECandidate() {
81				c, err := ice.UnmarshalCandidate(a.Value)
82				if err == nil && c.Type() == ice.CandidateTypeHost {
83					ip := net.ParseIP(c.Address())
84					if ip != nil && (IsLocal(ip) || ip.IsUnspecified() || ip.IsLoopback()) {
85						/* no append in this case */
86						continue
87					}
88				}
89			}
90			attrs = append(attrs, a)
91		}
92		m.Attributes = attrs
93	}
94	bts, err := desc.Marshal()
95	if err != nil {
96		return str
97	}
98	return string(bts)
99}
100