mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 12:17:06 +00:00
167 lines
4 KiB
Go
167 lines
4 KiB
Go
package jingle
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
func appendTransportAttrs(t *Transport, attrs *[]kv) error {
|
|
if t == nil {
|
|
return errors.New("content missing transport")
|
|
}
|
|
if t.Ufrag == "" {
|
|
return errors.New("transport missing ufrag")
|
|
}
|
|
if t.Pwd == "" {
|
|
return errors.New("transport missing pwd")
|
|
}
|
|
*attrs = append(*attrs, kv{"ice-ufrag", t.Ufrag})
|
|
*attrs = append(*attrs, kv{"ice-pwd", t.Pwd})
|
|
|
|
var iceOptions []string
|
|
if t.Trickle != nil {
|
|
iceOptions = append(iceOptions, "trickle")
|
|
}
|
|
if t.Renomination != nil {
|
|
iceOptions = append(iceOptions, "renomination")
|
|
}
|
|
if len(iceOptions) == 0 {
|
|
iceOptions = defaultICEOptions
|
|
}
|
|
*attrs = append(*attrs, kv{"ice-options", strings.Join(iceOptions, " ")})
|
|
|
|
fp := t.Fingerprint
|
|
if fp == nil {
|
|
return errors.New("transport missing DTLS fingerprint")
|
|
}
|
|
if fp.Hash == "" || fp.Text == "" {
|
|
return errors.New("fingerprint missing hash or body")
|
|
}
|
|
if strings.ContainsAny(fp.Hash, " \t\r\n") {
|
|
return fmt.Errorf("fingerprint hash contains whitespace: %q", fp.Hash)
|
|
}
|
|
*attrs = append(*attrs, kv{"fingerprint", fp.Hash + " " + fp.Text})
|
|
if fp.Setup != "" {
|
|
*attrs = append(*attrs, kv{"setup", fp.Setup})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// falls back to session-level fingerprint/setup when the media block omits
|
|
// them (matches Conversations SessionDescription)
|
|
func buildJingleTransport(m *mediaSection, sessAttrs []kv) (*Transport, error) {
|
|
t := &Transport{
|
|
Ufrag: mmFirst(m.attrs, "ice-ufrag", ""),
|
|
Pwd: mmFirst(m.attrs, "ice-pwd", ""),
|
|
}
|
|
|
|
fpRaw := mmFirst(m.attrs, "fingerprint", mmFirst(sessAttrs, "fingerprint", ""))
|
|
setupVal := mmFirst(m.attrs, "setup", mmFirst(sessAttrs, "setup", ""))
|
|
if fpRaw != "" && setupVal != "" {
|
|
sp := strings.SplitN(fpRaw, " ", 2)
|
|
if len(sp) >= 2 {
|
|
t.Fingerprint = &Fingerprint{
|
|
Hash: sp[0],
|
|
Setup: setupVal,
|
|
Text: sp[1],
|
|
}
|
|
}
|
|
}
|
|
|
|
if iceOptions := mmFirst(m.attrs, "ice-options", ""); iceOptions != "" {
|
|
for _, opt := range strings.Fields(iceOptions) {
|
|
if !wellKnownICEOptions[opt] {
|
|
continue
|
|
}
|
|
switch opt {
|
|
case "trickle":
|
|
t.Trickle = &ICEOptionTrickle{}
|
|
case "renomination":
|
|
t.Renomination = &ICEOptionRenomination{}
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, val := range mmGetAll(m.attrs, "candidate") {
|
|
cand, err := candidateFromSDP(val)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if cand != nil {
|
|
t.Candidates = append(t.Candidates, *cand)
|
|
}
|
|
}
|
|
return t, nil
|
|
}
|
|
|
|
func candidateToSDP(c *Candidate) (string, error) {
|
|
for _, kv := range []struct{ name, val string }{
|
|
{"foundation", c.Foundation},
|
|
{"component", c.Component},
|
|
{"protocol", c.Protocol},
|
|
{"priority", c.Priority},
|
|
{"ip", c.IP},
|
|
{"port", c.Port},
|
|
} {
|
|
if kv.val == "" {
|
|
return "", fmt.Errorf("candidate missing %s", kv.name)
|
|
}
|
|
}
|
|
proto := strings.ToLower(c.Protocol)
|
|
if proto != "udp" {
|
|
return "", fmt.Errorf("'%s' is not a supported protocol", proto)
|
|
}
|
|
var extra []string
|
|
if c.Type != "" {
|
|
extra = append(extra, "typ", c.Type)
|
|
}
|
|
if c.RelAddr != "" {
|
|
extra = append(extra, "raddr", c.RelAddr)
|
|
}
|
|
if c.RelPort != "" {
|
|
extra = append(extra, "rport", c.RelPort)
|
|
}
|
|
if c.Generation != "" {
|
|
extra = append(extra, "generation", c.Generation)
|
|
}
|
|
line := c.Foundation + " " + c.Component + " " + proto + " " + c.Priority + " " + c.IP + " " + c.Port
|
|
if len(extra) > 0 {
|
|
line += " " + strings.Join(extra, " ")
|
|
}
|
|
return line, nil
|
|
}
|
|
|
|
func candidateFromSDP(value string) (*Candidate, error) {
|
|
parts := strings.Fields(value)
|
|
if len(parts) < 6 {
|
|
return nil, nil
|
|
}
|
|
cand := &Candidate{
|
|
Foundation: parts[0],
|
|
Component: parts[1],
|
|
Protocol: strings.ToLower(parts[2]),
|
|
Priority: parts[3],
|
|
IP: parts[4],
|
|
Port: parts[5],
|
|
}
|
|
if cand.Protocol != "udp" {
|
|
return nil, fmt.Errorf("'%s' is not a supported protocol", cand.Protocol)
|
|
}
|
|
extra := parts[6:]
|
|
for i := 0; i+1 < len(extra); i += 2 {
|
|
k := extra[i]
|
|
v := extra[i+1]
|
|
switch k {
|
|
case "typ":
|
|
cand.Type = v
|
|
case "raddr":
|
|
cand.RelAddr = v
|
|
case "rport":
|
|
cand.RelPort = v
|
|
case "generation":
|
|
cand.Generation = v
|
|
}
|
|
}
|
|
return cand, nil
|
|
}
|