commit 2019fa0504b90a8b3609d4bb69731c916de2c272
parent 6c87f537549ca5125fbe7faa7cd4e9353e031e8f
Author: Oliver Lowe <o@olowe.co>
Date: Sat, 10 Aug 2024 16:46:36 +1000
sdp: parse IP addresses as netip.Addr
Using Addr, we don't have to keep track of the address type ourselves.
We also get a guarantee that the values are actually valid IP
addresses.
Diffstat:
4 files changed, 44 insertions(+), 36 deletions(-)
diff --git a/sdp/encode.go b/sdp/encode.go
@@ -11,7 +11,11 @@ func (s Session) String() string {
if s.Origin.Username == "" {
s.Origin.Username = NoUsername
}
- fmt.Fprintf(buf, "o=%s %d %d IN %s %s\n", s.Origin.Username, s.Origin.ID, s.Origin.Version, s.Origin.AddressType, s.Origin.Address)
+ ipv := "IP6"
+ if s.Origin.Address.Is4() {
+ ipv = "IP4"
+ }
+ fmt.Fprintf(buf, "o=%s %d %d IN %s %s\n", s.Origin.Username, s.Origin.ID, s.Origin.Version, ipv, s.Origin.Address)
fmt.Fprintf(buf, "s=%s\n", s.Name)
if s.Info != "" {
diff --git a/sdp/example_test.go b/sdp/example_test.go
@@ -2,6 +2,7 @@ package sdp_test
import (
"fmt"
+ "net/netip"
"github.com/untangledco/streaming/sdp"
)
@@ -12,10 +13,9 @@ import (
func Example() {
session := sdp.Session{
Origin: sdp.Origin{
- ID: 3930287268, // example only; use sdp.Now()
- Version: 3930287268, // example only; use sdp.Now()
- AddressType: "IP6", // or "IP4"
- Address: "2001:db8::1",
+ ID: 3930287268, // example only; use sdp.Now()
+ Version: 3930287268, // example only; use sdp.Now()
+ Address: netip.MustParseAddr("2001:db8::1"),
},
Name: "A call from me to you",
}
diff --git a/sdp/sdp.go b/sdp/sdp.go
@@ -8,6 +8,7 @@ import (
"fmt"
"io"
"net/mail"
+ "net/netip"
"net/url"
"strconv"
"strings"
@@ -55,15 +56,8 @@ type Origin struct {
// value is a timestamp from Now().
Version int
- // AddressType describes the type of Address. This is almost
- // always "IP4" or "IP6" for IPv4 and IPv6 respectively.
- AddressType string // TODO(otl): only "IP4", "IP6" valid... new int type?
-
- // Address is the originating address of the session. This is
- // almost always a literal IPv4 or IPv6 address such as
- // "192.0.2.1" or "2001:db8::1".
- // TODO(otl): is a hostname valid? if not, use netip.Addr, then can drop AddressType.
- Address string
+ // Address is the originating address of the session.
+ Address netip.Addr
}
func ReadSession(rd io.Reader) (*Session, error) {
@@ -101,8 +95,13 @@ func parseOrigin(line string) (Origin, error) {
if fields[3] != "IN" {
return o, fmt.Errorf("unknown network class %q", fields[3])
}
- o.AddressType = fields[4]
- o.Address = fields[5]
+
+ // skip IP4/IP6 in fields[4]; netip handles the IP version for us.
+ addr, err := netip.ParseAddr(fields[5])
+ if err != nil {
+ return o, fmt.Errorf("parse address: %w", err)
+ }
+ o.Address = addr
return o, nil
}
@@ -311,16 +310,18 @@ func parseMedia(s string) (Media, error) {
// ConnInfo represents connection information.
type ConnInfo struct {
- Type string // TODO(otl): only "IP4", "IP6" valid... new int type?
- // TODO(otl): can this be a hostname? if not, maybe use netip.Addr
- Address string // IPv4, IPv6 literal
+ Address netip.Addr
// TODO(otl): what are these units? seconds?
TTL uint8 // time to live
Count int // number of addresses after Address
}
func (c *ConnInfo) String() string {
- s := fmt.Sprintf("c=%s %s %s", "IN", c.Type, c.Address)
+ ipv := "IP6"
+ if c.Address.Is4() {
+ ipv = "IP4"
+ }
+ s := fmt.Sprintf("c=%s %s %s", "IN", ipv, c.Address)
if c.TTL > 0 {
s += fmt.Sprintf("/%d", c.TTL)
}
@@ -339,13 +340,16 @@ func parseConnInfo(s string) (ConnInfo, error) {
return ConnInfo{}, fmt.Errorf("unsupported class %q, expected IN", fields[0])
}
- conn := ConnInfo{Type: fields[1]}
+ var conn ConnInfo
if fields[1] != "IP4" && fields[1] != "IP6" {
return conn, fmt.Errorf("unsupported network type %s", fields[2])
}
- conn.Type = fields[1]
addr := strings.Split(fields[2], "/")
- conn.Address = addr[0]
+ var err error
+ conn.Address, err = netip.ParseAddr(addr[0])
+ if err != nil {
+ return conn, fmt.Errorf("parse address: %w", err)
+ }
if len(addr) == 1 {
return conn, nil
}
@@ -359,7 +363,7 @@ func parseConnInfo(s string) (ConnInfo, error) {
}
}
- if conn.Type == "IP4" {
+ if conn.Address.Is4() {
if subfields[0] < 0 || subfields[0] > 255 {
return conn, fmt.Errorf("ttl: %d is outside uint8 range", subfields[0])
}
@@ -369,9 +373,9 @@ func parseConnInfo(s string) (ConnInfo, error) {
}
}
- if conn.Type == "IP6" && len(subfields) > 1 {
+ if conn.Address.Is6() && len(subfields) > 1 {
return conn, fmt.Errorf("parse address: only 1 subfield allowed, read %d", len(subfields))
- } else if conn.Type == "IP6" && len(subfields) == 1 {
+ } else if conn.Address.Is6() && len(subfields) == 1 {
conn.Count = subfields[0]
}
return conn, nil
diff --git a/sdp/sdp_test.go b/sdp/sdp_test.go
@@ -2,6 +2,7 @@ package sdp
import (
"net/mail"
+ "net/netip"
"net/url"
"os"
"reflect"
@@ -18,7 +19,7 @@ func TestReadSession(t *testing.T) {
name: "good.sdp",
want: Session{
Name: "Call to John Smith",
- Origin: Origin{"jdoe", 3724394400, 3724394405, "IP4", "198.51.100.1"},
+ Origin: Origin{"jdoe", 3724394400, 3724394405, netip.MustParseAddr("198.51.100.1")},
Info: "SDP Offer #1",
URI: &url.URL{
Scheme: "http",
@@ -31,8 +32,7 @@ func TestReadSession(t *testing.T) {
},
Phone: "+16175556011",
Connection: &ConnInfo{
- Type: "IP4",
- Address: "198.51.100.1",
+ Address: netip.MustParseAddr("198.51.100.1"),
},
Media: []Media{
Media{
@@ -52,7 +52,7 @@ func TestReadSession(t *testing.T) {
Port: 51372,
Transport: ProtoRTP,
Format: []string{"99"},
- Connection: &ConnInfo{"IP6", "2001:db8::2", 0, 0},
+ Connection: &ConnInfo{netip.MustParseAddr("2001:db8::2"), 0, 0},
Attributes: []string{"rtpmap:99", "h263-1998/90000"},
},
},
@@ -61,7 +61,7 @@ func TestReadSession(t *testing.T) {
{
name: "some_optional.sdp",
want: Session{
- Origin: Origin{"jdoe", 3724394400, 3724394405, "IP4", "198.51.100.1"},
+ Origin: Origin{"jdoe", 3724394400, 3724394405, netip.MustParseAddr("198.51.100.1")},
Name: "Call to John Smith",
Email: &mail.Address{
Name: "Jane Doe",
@@ -134,11 +134,11 @@ func TestConnInfo(t *testing.T) {
line string
want ConnInfo
}{
- {"ipv4", "IN IP4 192.0.2.1", ConnInfo{"IP4", "192.0.2.1", 0, 0}},
- {"ipv4 ttl", "IN IP4 233.252.0.1/127", ConnInfo{"IP4", "233.252.0.1", 127, 0}},
- {"ipv4 ttl count", "IN IP4 233.252.0.1/127/3", ConnInfo{"IP4", "233.252.0.1", 127, 3}},
- {"ipv6", "IN IP6 2001:db8::1", ConnInfo{"IP6", "2001:db8::1", 0, 0}},
- {"ipv6 count", "IN IP6 ff00::db8:0:101/3", ConnInfo{"IP6", "ff00::db8:0:101", 0, 3}},
+ {"ipv4", "IN IP4 192.0.2.1", ConnInfo{netip.MustParseAddr("192.0.2.1"), 0, 0}},
+ {"ipv4 ttl", "IN IP4 233.252.0.1/127", ConnInfo{netip.MustParseAddr("233.252.0.1"), 127, 0}},
+ {"ipv4 ttl count", "IN IP4 233.252.0.1/127/3", ConnInfo{netip.MustParseAddr("233.252.0.1"), 127, 3}},
+ {"ipv6", "IN IP6 2001:db8::1", ConnInfo{netip.MustParseAddr("2001:db8::1"), 0, 0}},
+ {"ipv6 count", "IN IP6 ff00::db8:0:101/3", ConnInfo{netip.MustParseAddr("ff00::db8:0:101"), 0, 3}},
}
for _, tt := range cases {