Repository: pion/turn
Pull request #45 (2019-06-02 09:31:43)

STUN v2 migration
WIP still doesn't build

codecov-io: # [Codecov](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=h1) Report
> Merging [#45](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=desc) into [master](https://codecov.io/gh/pion/turn/commit/a71d7b2bdbc76ae8be4bf3881eea8990791969fe?src=pr&el=desc) will **increase** coverage by `0.41%`.
> The diff coverage is `5.3%`.

[![Impacted file tree graph](https://codecov.io/gh/pion/turn/pull/45/graphs/tree.svg?width=650&token=1UomjQ7lwC&height=150&src=pr)](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #45      +/-   ##
==========================================
+ Coverage   11.48%   11.89%   +0.41%     
==========================================
  Files          15       16       +1     
  Lines         679      664      -15     
==========================================
+ Hits           78       79       +1     
+ Misses        584      568      -16     
  Partials       17       17
```


| [Impacted Files](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/allocation/permission.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9wZXJtaXNzaW9uLmdv) | `0% <ø> (ø)` | :arrow_up: |
| [turn.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-dHVybi5nbw==) | `0% <ø> (ø)` | :arrow_up: |
| [internal/allocation/channel\_bind.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9jaGFubmVsX2JpbmQuZ28=) | `0% <ø> (ø)` | :arrow_up: |
| [internal/allocation/allocation.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9hbGxvY2F0aW9uLmdv) | `5.2% <0%> (-0.17%)` | :arrow_down: |
| [internal/server/stun.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvc2VydmVyL3N0dW4uZ28=) | `0% <0%> (ø)` | :arrow_up: |
| [internal/server/server.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvc2VydmVyL3NlcnZlci5nbw==) | `0% <0%> (ø)` | :arrow_up: |
| [internal/allocation/reservation\_manager.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9yZXNlcnZhdGlvbl9tYW5hZ2VyLmdv) | `0% <0%> (ø)` | :arrow_up: |
| [internal/server/turn.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvc2VydmVyL3R1cm4uZ28=) | `0% <0%> (ø)` | :arrow_up: |
| [internal/allocation/five\_tuple.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9maXZlX3R1cGxlLmdv) | `100% <100%> (ø)` | :arrow_up: |
| [internal/allocation/allocation\_manager.go](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9hbGxvY2F0aW9uX21hbmFnZXIuZ28=) | `50.87% <100%> (+1.72%)` | :arrow_up: |
| ... and [3 more](https://codecov.io/gh/pion/turn/pull/45/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=footer). Last update [a71d7b2...ebdbd9e](https://codecov.io/gh/pion/turn/pull/45?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.travis.yml b/.travis.yml
index e3515b4..8c5db64 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -12,7 +12,7 @@ notifications:
     secure: IYjXoe03KykZ3v4GgUwGzfWRepO5DnJdxB87lSQ2IMsF6PBFSc3CaOX3GUclHIlzTdchR+PHj1jtEZZVSkgfp9amZBCcqbJTBOPG1YA6hxOvTpgeWIttMH0cmMxSCuCa4RfkuRH2+UXbjREMJ3ENau2CTMKReyW4Jddh9dREZohVmYuqN6uuBqCndYpt3Lm1Hv+T+vqxwTDdE/q0hwGMiwgvQm7N3K397e1q1mg+o4tMGwqyUIPnEPjaSKcEuOBa8Rqyl96nn+HGZK0zvNqUOxlzeRMM0VBcxe2s+zY/SuLj4OwNl1zEmIfY6Qj70t2cmT3xJvJprB4pCwR7q78b4lfpNu6rqCJPIZG/qDFT+XSuhDCmLlCO/+Uhtu11pgjV8UMNLTKJth+7hurH7oLNb7jYk9VYsiKhs41LICyDjJNzS5yPatF5xj0HOujb6Uh/pfI+9a+IpPSeXv1gBo8H3oWa6TfRhuTUS3Jc48p/jriZmgWgbKa1HKTaY9ENvAdZFfxJdrRg3Y4SKnjZcAPw7ijRIx1oaM3rHYbOTm/dj4ggho7EgTO3k8toQ5PKohrbBG5RERqHJvC47SXDt0fEjeGnAfN7Xtj0Pq8YyaFIj7CmCCGoI//2sWkK3AmjnwIuW0hUMsL3GsED+p0lsu6FX9wysJwy2Z2mTfIX/CXmB6w=
 
 before_script:
-  - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.13
+  - curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | bash -s -- -b $GOPATH/bin v1.15.0
 
 script:
   - golangci-lint run ./...
diff --git a/cmd/simple-turn/main.go b/cmd/simple-turn/main.go
index 4cb7530..58cfb50 100644
--- a/cmd/simple-turn/main.go
+++ b/cmd/simple-turn/main.go
@@ -2,11 +2,11 @@ package main
 
 import (
 	"log"
+	"net"
 	"os"
 	"regexp"
 	"strconv"
 
-	"github.com/pion/stun"
 	"github.com/pion/turn"
 )
 
@@ -14,7 +14,7 @@ type myTurnServer struct {
 	usersMap map[string]string
 }
 
-func (m *myTurnServer) AuthenticateRequest(username string, srcAddr *stun.TransportAddr) (password string, ok bool) {
+func (m *myTurnServer) AuthenticateRequest(username string, srcAddr net.Addr) (password string, ok bool) {
 	if password, ok := m.usersMap[username]; ok {
 		return password, true
 	}
diff --git a/go.mod b/go.mod
index 6879101..e61a03f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,9 @@ module github.com/pion/turn
 go 1.12
 
 require (
-	github.com/pion/stun v0.2.1
+	github.com/davecgh/go-spew v1.1.1
+	github.com/gortc/turn v0.7.1
+	github.com/pion/stun v0.3.0
 	github.com/pkg/errors v0.8.1
 	golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b
 )
diff --git a/go.sum b/go.sum
index 8545825..4918f18 100644
--- a/go.sum
+++ b/go.sum
@@ -1,14 +1,11 @@
-github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/pion/stun v0.2.1 h1:rSKJ0ynYkRalRD8BifmkaGLeepCFuGTwG6FxPsrPK8o=
-github.com/pion/stun v0.2.1/go.mod h1:TChCNKgwnFiFG/c9K+zqEdd6pO6tlODb9yN1W/zVfsE=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/gortc/turn v0.7.1 h1:+DKoYXx8d2syTplmZ0Q3BaoTKsgClkoUzT2HpoAeK1c=
+github.com/gortc/turn v0.7.1/go.mod h1:3FZ+LvCZKCKu6YYgwuYPqEi3FqCtdjfSFnFqVQNwfjk=
+github.com/pion/stun v0.3.0 h1:hKadT0I+ijxKkVXqU+rPHKLt+Sw6dQ8D+HhvuoYMoxQ=
+github.com/pion/stun v0.3.0/go.mod h1:xrCld6XM+6GWDZdvjPlLMsTU21rNxnO6UO8XsAvHr/M=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b h1:/zjbcJPEGAyu6Is/VBOALsgdi4z9+kz/Vtdm6S+beD0=
 golang.org/x/net v0.0.0-20190403144856-b630fd6fe46b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
diff --git a/internal/allocation/allocation.go b/internal/allocation/allocation.go
index 69b4514..0278b26 100644
--- a/internal/allocation/allocation.go
+++ b/internal/allocation/allocation.go
@@ -7,6 +7,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/gortc/turn"
 	"github.com/pion/stun"
 	"github.com/pion/turn/internal/ipnet"
 	"github.com/pkg/errors"
@@ -15,7 +16,7 @@ import (
 // Allocation is tied to a FiveTuple and relays traffic
 // use CreateAllocation and GetAllocation to operate
 type Allocation struct {
-	RelayAddr *stun.TransportAddr
+	RelayAddr net.Addr
 	Protocol  Protocol
 
 	TurnSocket  ipnet.PacketConn
@@ -37,7 +38,7 @@ func (a *Allocation) AddPermission(p *Permission) {
 	a.permissionsLock.Lock()
 	defer a.permissionsLock.Unlock()
 	for _, existingPermission := range a.permissions {
-		if p.Addr.Equal(existingPermission.Addr) {
+		if ipnet.AddrEqual(p.Addr, existingPermission.Addr) {
 			existingPermission.refresh()
 			return
 		}
@@ -48,13 +49,13 @@ func (a *Allocation) AddPermission(p *Permission) {
 	p.start()
 }
 
-// RemovePermission removes the TransportAddr from the allocation's permissions
-func (a *Allocation) RemovePermission(addr *stun.TransportAddr) bool {
+// RemovePermission removes the net.Addr from the allocation's permissions
+func (a *Allocation) RemovePermission(addr net.Addr) bool {
 	a.permissionsLock.Lock()
 	defer a.permissionsLock.Unlock()
 
 	for i := len(a.permissions) - 1; i >= 0; i-- {
-		if a.permissions[i].Addr.Equal(addr) {
+		if ipnet.AddrEqual(a.permissions[i].Addr, addr) {
 			a.permissions = append(a.permissions[:i], a.permissions[i+1:]...)
 			return true
 		}
@@ -64,11 +65,11 @@ func (a *Allocation) RemovePermission(addr *stun.TransportAddr) bool {
 }
 
 // GetPermission gets the Permission from the allocation
-func (a *Allocation) GetPermission(addr *stun.TransportAddr) *Permission {
+func (a *Allocation) GetPermission(addr net.Addr) *Permission {
 	a.permissionsLock.RLock()
 	defer a.permissionsLock.RUnlock()
 	for _, p := range a.permissions {
-		if p.Addr.Equal(addr) {
+		if ipnet.AddrEqual(p.Addr, addr) {
 			return p
 		}
 	}
@@ -134,12 +135,12 @@ func (a *Allocation) GetChannelByID(id uint16) *ChannelBind {
 	return nil
 }
 
-// GetChannelByAddr gets the ChannelBind from this allocation by stun.TransportAddr
-func (a *Allocation) GetChannelByAddr(addr *stun.TransportAddr) *ChannelBind {
+// GetChannelByAddr gets the ChannelBind from this allocation by net.Addr
+func (a *Allocation) GetChannelByAddr(addr net.Addr) *ChannelBind {
 	a.channelBindingsLock.RLock()
 	defer a.channelBindingsLock.RUnlock()
 	for _, cb := range a.channelBindings {
-		if cb.Peer.Equal(addr) {
+		if ipnet.AddrEqual(cb.Peer, addr) {
 			return cb
 		}
 	}
@@ -191,21 +192,26 @@ func (a *Allocation) packetHandler(m *Manager) {
 			return
 		}
 
-		if channel := a.GetChannelByAddr(&stun.TransportAddr{IP: cm.Dst, Port: a.RelayAddr.Port}); channel != nil {
+		if channel := a.GetChannelByAddr(&net.UDPAddr{IP: cm.Dst, Port: a.RelayAddr.(*net.UDPAddr).Port}); channel != nil {
 			channelData := make([]byte, 4)
 			binary.BigEndian.PutUint16(channelData[0:], channel.ID)
 			binary.BigEndian.PutUint16(channelData[2:], uint16(n))
 			channelData = append(channelData, buffer[:n]...)
 
-			if _, err = a.TurnSocket.WriteTo(channelData, a.fiveTuple.SrcAddr.Addr()); err != nil {
+			if _, err = a.TurnSocket.WriteTo(channelData, a.fiveTuple.SrcAddr); err != nil {
 				fmt.Printf("Failed to send ChannelData from allocation %v %v \n", srcAddr, err)
 			}
-		} else if p := a.GetPermission(&stun.TransportAddr{IP: srcAddr.(*net.UDPAddr).IP, Port: srcAddr.(*net.UDPAddr).Port}); p != nil {
-			dataAttr := stun.Data{Data: buffer[:n]}
-			xorPeerAddressAttr := stun.XorPeerAddress{XorAddress: stun.XorAddress{IP: srcAddr.(*net.UDPAddr).IP, Port: srcAddr.(*net.UDPAddr).Port}}
-			if err = stun.BuildAndSend(a.TurnSocket, a.fiveTuple.SrcAddr, stun.ClassIndication, stun.MethodData, stun.GenerateTransactionID(), &xorPeerAddressAttr, &dataAttr); err != nil {
+		} else if p := a.GetPermission(&net.UDPAddr{IP: srcAddr.(*net.UDPAddr).IP, Port: srcAddr.(*net.UDPAddr).Port}); p != nil {
+			dataAttr := turn.Data(buffer[:n])
+			peerAddressAttr := turn.PeerAddress{IP: srcAddr.(*net.UDPAddr).IP, Port: srcAddr.(*net.UDPAddr).Port}
+			msg, err := stun.Build(stun.TransactionID, stun.NewType(stun.MethodData, stun.ClassIndication), peerAddressAttr, dataAttr)
+			if err != nil {
 				fmt.Printf("Failed to send DataIndication from allocation %v %v \n", srcAddr, err)
 			}
+			if _, err = a.TurnSocket.WriteTo(msg.Raw, a.fiveTuple.SrcAddr); err != nil {
+				fmt.Printf("Failed to send DataIndication from allocation %v %v \n", srcAddr, err)
+			}
+
 		} else {
 			fmt.Printf("Packet unhandled in relay src %v \n", srcAddr)
 		}
diff --git a/internal/allocation/allocation_manager.go b/internal/allocation/allocation_manager.go
index d3274a6..8eda23e 100644
--- a/internal/allocation/allocation_manager.go
+++ b/internal/allocation/allocation_manager.go
@@ -6,7 +6,6 @@ import (
 	"sync"
 	"time"
 
-	"github.com/pion/stun"
 	"github.com/pion/turn/internal/ipnet"
 	"github.com/pkg/errors"
 )
@@ -66,11 +65,7 @@ func (m *Manager) CreateAllocation(fiveTuple *FiveTuple, turnSocket ipnet.Packet
 		return nil, err
 	}
 	a.RelaySocket = conn
-
-	a.RelayAddr, err = stun.NewTransportAddr(listener.LocalAddr())
-	if err != nil {
-		return nil, errors.Wrap(err, "failed to create connection")
-	}
+	a.RelayAddr = listener.LocalAddr()
 
 	a.lifetimeTimer = time.AfterFunc(time.Duration(lifetime)*time.Second, func() {
 		if err := listener.Close(); err != nil {
diff --git a/internal/allocation/channel_bind.go b/internal/allocation/channel_bind.go
index 06bd688..f278319 100644
--- a/internal/allocation/channel_bind.go
+++ b/internal/allocation/channel_bind.go
@@ -2,9 +2,8 @@ package allocation
 
 import (
 	"fmt"
+	"net"
 	"time"
-
-	"github.com/pion/stun"
 )
 
 const channelBindTimeout = time.Duration(10) * time.Minute
@@ -12,7 +11,7 @@ const channelBindTimeout = time.Duration(10) * time.Minute
 // ChannelBind represents a TURN Channel
 // https://tools.ietf.org/html/rfc5766#section-2.5
 type ChannelBind struct {
-	Peer          *stun.TransportAddr
+	Peer          net.Addr
 	ID            uint16
 	allocation    *Allocation
 	lifetimeTimer *time.Timer
diff --git a/internal/allocation/five_tuple.go b/internal/allocation/five_tuple.go
index 8aa5579..dd70744 100644
--- a/internal/allocation/five_tuple.go
+++ b/internal/allocation/five_tuple.go
@@ -1,6 +1,10 @@
 package allocation
 
-import "github.com/pion/stun"
+import (
+	"net"
+
+	"github.com/pion/turn/internal/ipnet"
+)
 
 // Protocol is an enum for relay protocol
 type Protocol int
@@ -19,13 +23,10 @@ const (
 // the server.
 type FiveTuple struct {
 	Protocol
-	SrcAddr *stun.TransportAddr
-	DstAddr *stun.TransportAddr
+	SrcAddr, DstAddr net.Addr
 }
 
 // Equal asserts if two FiveTuples are equal
 func (f *FiveTuple) Equal(b *FiveTuple) bool {
-	return f.SrcAddr.Equal(b.SrcAddr) &&
-		f.DstAddr.Equal(b.DstAddr) &&
-		f.Protocol == b.Protocol
+	return ipnet.AddrEqual(f.SrcAddr, b.SrcAddr) && ipnet.AddrEqual(f.DstAddr, b.DstAddr)
 }
diff --git a/internal/allocation/permission.go b/internal/allocation/permission.go
index 9a9dea8..6e96cc0 100644
--- a/internal/allocation/permission.go
+++ b/internal/allocation/permission.go
@@ -2,9 +2,8 @@ package allocation
 
 import (
 	"fmt"
+	"net"
 	"time"
-
-	"github.com/pion/stun"
 )
 
 const permissionTimeout = time.Duration(5) * time.Minute
@@ -13,7 +12,7 @@ const permissionTimeout = time.Duration(5) * time.Minute
 // filtering mechanism of NATs that comply with [RFC4787].
 // https://tools.ietf.org/html/rfc5766#section-2.3
 type Permission struct {
-	Addr          *stun.TransportAddr
+	Addr          net.Addr
 	allocation    *Allocation
 	lifetimeTimer *time.Timer
 }
diff --git a/internal/allocation/reservation_manager.go b/internal/allocation/reservation_manager.go
index 3e4fc42..f05bcba 100644
--- a/internal/allocation/reservation_manager.go
+++ b/internal/allocation/reservation_manager.go
@@ -1,11 +1,10 @@
 package allocation
 
 import (
+	"fmt"
 	"net"
 	"sync"
 	"time"
-
-	"github.com/pion/stun"
 )
 
 type reservation struct {
@@ -60,9 +59,9 @@ func GetRandomEvenPort() (int, error) {
 		return 0, err
 	}
 
-	addr, err := stun.NewTransportAddr(listener.LocalAddr())
-	if err != nil {
-		return 0, err
+	addr, ok := listener.LocalAddr().(*net.UDPAddr)
+	if !ok {
+		return 0, fmt.Errorf("failed to cast net.Addr to *net.UDPAddr")
 	} else if err := listener.Close(); err != nil {
 		return 0, err
 	} else if addr.Port%2 == 1 {
diff --git a/internal/client/stun.go b/internal/client/stun.go
index 7d79ca0..aaf2e0f 100644
--- a/internal/client/stun.go
+++ b/internal/client/stun.go
@@ -22,30 +22,25 @@ func (c *Client) SendSTUNRequest(serverIP net.IP, serverPort int) (interface{},
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to read packet from udp socket")
 	}
-	srcAddr, err := stun.NewTransportAddr(addr)
-	if err != nil {
-		return nil, errors.Wrap(err, "failed reading udp addr")
-	}
 
-	resp, err := stun.NewMessage(packet[:size])
-	if err != nil {
+	resp := &stun.Message{Raw: append([]byte{}, packet[:size]...)}
+	if err := resp.Decode(); err != nil {
 		return nil, errors.Wrap(err, "failed to handle reply")
 	}
 
-	attr, ok := resp.GetOneAttribute(stun.AttrXORMappedAddress)
-	if !ok {
-		return nil, errors.Errorf("got respond from STUN server that did not contain XORAddress")
-	}
-
-	var reflAddr stun.XorAddress
-	if err = reflAddr.Unpack(resp, attr); err != nil {
-		return nil, errors.Wrapf(err, "failed to unpack STUN XorAddress response")
+	var reflAddr stun.XORMappedAddress
+	if err := reflAddr.GetFrom(resp); err != nil {
+		return nil, err
 	}
 
-	return fmt.Sprintf("pkt_size=%d dst_ip=%s src_addr=%s refl_addr=%s:%d", size, cm.Dst, srcAddr, reflAddr.IP, reflAddr.Port), nil
+	return fmt.Sprintf("pkt_size=%d dst_ip=%s src_addr=%s refl_addr=%s:%d", size, cm.Dst, addr, reflAddr.IP, reflAddr.Port), nil
 }
 
 func sendStunRequest(conn net.PacketConn, serverIP net.IP, serverPort int) error {
-	serverAddress := stun.TransportAddr{IP: serverIP, Port: serverPort}
-	return stun.BuildAndSend(conn, &serverAddress, stun.ClassRequest, stun.MethodBinding, stun.GenerateTransactionID())
+	msg, err := stun.Build(stun.TransactionID, stun.BindingRequest)
+	if err != nil {
+		return err
+	}
+	_, err = conn.WriteTo(msg.Raw, &net.UDPAddr{IP: serverIP, Port: serverPort})
+	return err
 }
diff --git a/internal/ipnet/util.go b/internal/ipnet/util.go
new file mode 100644
index 0000000..ce40b97
--- /dev/null
+++ b/internal/ipnet/util.go
@@ -0,0 +1,32 @@
+package ipnet
+
+import (
+	"fmt"
+	"net"
+)
+
+// AddrIPPort extracts the IP and Port from a net.Addr
+func AddrIPPort(a net.Addr) (net.IP, int, error) {
+	aUDP, ok := a.(*net.UDPAddr)
+	if !ok {
+		return nil, 0, fmt.Errorf("failed to cast net.Addr to *net.UDPAddr")
+	}
+
+	return aUDP.IP, aUDP.Port, nil
+}
+
+// AddrEqual asserts that two net.Addrs are equal
+// Currently only supprots UDP but will be extended in the future to support others
+func AddrEqual(a, b net.Addr) bool {
+	aUDP, ok := a.(*net.UDPAddr)
+	if !ok {
+		return false
+	}
+
+	bUDP, ok := b.(*net.UDPAddr)
+	if !ok {
+		return false
+	}
+
+	return aUDP.IP.Equal(bUDP.IP) && aUDP.Port == bUDP.Port
+}
diff --git a/internal/server/server.go b/internal/server/server.go
index caf5cea..ece848b 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -1,9 +1,7 @@
 package server
 
 import (
-	"bytes"
 	"crypto/md5" // #nosec
-	"encoding/binary"
 	"fmt"
 	"io"
 	"log"
@@ -12,6 +10,7 @@ import (
 	"strconv"
 	"time"
 
+	"github.com/gortc/turn"
 	"github.com/pion/stun"
 	"github.com/pion/turn/internal/allocation"
 	"github.com/pion/turn/internal/ipnet"
@@ -20,7 +19,7 @@ import (
 
 // AuthHandler is a callback used to handle incoming auth requests, allowing users to customize Pion TURN
 // with custom behavior
-type AuthHandler func(username string, srcAddr *stun.TransportAddr) (password string, ok bool)
+type AuthHandler func(username string, srcAddr net.Addr) (password string, ok bool)
 
 // Server is an instance of the Pion TURN server
 type Server struct {
@@ -64,64 +63,54 @@ func (s *Server) Listen(address string, port int) error {
 			return errors.Wrap(err, "failed to read packet from udp socket")
 		}
 
-		srcAddr, err := stun.NewTransportAddr(addr)
-		if err != nil {
-			return errors.Wrap(err, "failed reading udp addr")
-		}
-		if err := s.handleUDPPacket(srcAddr, &stun.TransportAddr{IP: cm.Dst, Port: port}, size); err != nil {
+		if err := s.handleUDPPacket(addr, &net.UDPAddr{IP: cm.Dst, Port: port}, size); err != nil {
 			log.Println(err)
 		}
 	}
 }
 
-func (s *Server) handleUDPPacket(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, size int) error {
-	packetType, err := stun.GetPacketType(s.packet[:size])
-	if err != nil {
-		return err
-	}
-
-	switch packetType {
-	case stun.PacketTypeChannelData:
+func (s *Server) handleUDPPacket(srcAddr, dstAddr net.Addr, size int) error {
+	if turn.IsChannelData(s.packet[:size]) {
 		return s.handleDataPacket(srcAddr, dstAddr, size)
-	default:
-		return s.handleTURNPacket(srcAddr, dstAddr, size)
 	}
+
+	return s.handleTURNPacket(srcAddr, dstAddr, size)
 }
 
-func (s *Server) handleDataPacket(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, size int) error {
-	c, err := stun.NewChannelData(s.packet[:size])
-	if err != nil {
+func (s *Server) handleDataPacket(srcAddr, dstAddr net.Addr, size int) error {
+	c := turn.ChannelData{Raw: s.packet[:size]}
+	if err := c.Decode(); err != nil {
 		return errors.Wrap(err, "Failed to create channel data from packet")
 	}
 
-	err = s.handleChannelData(srcAddr, dstAddr, c)
+	err := s.handleChannelData(srcAddr, dstAddr, &c)
 	if err != nil {
-		return errors.Errorf("unable to handle ChannelData from %v: %v", srcAddr, err)
+		err = errors.Errorf("unable to handle ChannelData from %v: %v", srcAddr, err)
 	}
 
-	return nil
+	return err
 }
 
-func (s *Server) handleTURNPacket(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, size int) error {
-	m, err := stun.NewMessage(s.packet[:size])
-	if err != nil {
+func (s *Server) handleTURNPacket(srcAddr, dstAddr net.Addr, size int) error {
+	m := &stun.Message{Raw: append([]byte{}, s.packet[:size]...)}
+	if err := m.Decode(); err != nil {
 		return errors.Wrap(err, "failed to create stun message from packet")
 	}
 
-	h, err := s.getMessageHandler(m.Class, m.Method)
+	h, err := s.getMessageHandler(m.Type.Class, m.Type.Method)
 	if err != nil {
-		return errors.Errorf("unhandled STUN packet %v-%v from %v: %v", m.Method, m.Class, srcAddr, err)
+		return errors.Errorf("unhandled STUN packet %v-%v from %v: %v", m.Type.Method, m.Type.Class, srcAddr, err)
 	}
 
 	err = h(srcAddr, dstAddr, m)
 	if err != nil {
-		return errors.Errorf("failed to handle %v-%v from %v: %v", m.Method, m.Class, srcAddr, err)
+		return errors.Errorf("failed to handle %v-%v from %v: %v", m.Type.Method, m.Type.Class, srcAddr, err)
 	}
 
 	return nil
 }
 
-type messageHandler func(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error
+type messageHandler func(srcAddr, dstAddr net.Addr, m *stun.Message) error
 
 func (s *Server) getMessageHandler(class stun.MessageClass, method stun.Method) (messageHandler, error) {
 	switch class {
@@ -154,14 +143,6 @@ func (s *Server) getMessageHandler(class stun.MessageClass, method stun.Method)
 	}
 }
 
-// Is there really no stdlib for this?
-func min(a, b uint32) uint32 {
-	if a < b {
-		return a
-	}
-	return b
-}
-
 func randSeq(n int) string {
 	letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
 	b := make([]rune, n)
@@ -185,26 +166,17 @@ func buildNonce() string {
 	return fmt.Sprintf("%x", h.Sum(nil))
 }
 
-func assertMessageIntegrity(m *stun.Message, theirMi *stun.RawAttribute, ourKey []byte) error {
-	// Length to remove when comparing MessageIntegrity (so we can re-compute)
-	tailLength := 24
-	rawCopy := make([]byte, len(m.Raw))
-	copy(rawCopy, m.Raw)
-
-	if _, messageIntegrityAttrFound := m.GetOneAttribute(stun.AttrFingerprint); messageIntegrityAttrFound {
-		currLength := binary.BigEndian.Uint16(rawCopy[2:4])
-
-		binary.BigEndian.PutUint16(rawCopy[2:], currLength-8)
-		tailLength += 8
-	}
+func assertMessageIntegrity(m *stun.Message, ourKey []byte) error {
+	messageIntegrityAttr := stun.MessageIntegrity(ourKey)
+	return messageIntegrityAttr.Check(m)
+}
 
-	ourMi, err := stun.MessageIntegrityCalculateHMAC(ourKey, rawCopy[:len(rawCopy)-tailLength])
+func buildAndSend(conn net.PacketConn, dst net.Addr, attrs ...stun.Setter) error {
+	msg, err := stun.Build(attrs...)
 	if err != nil {
 		return err
 	}
 
-	if !bytes.Equal(ourMi, theirMi.Value) {
-		return errors.Errorf("MessageIntegrity mismatch %x %x", ourKey, theirMi.Value)
-	}
-	return nil
+	_, err = conn.WriteTo(msg.Raw, dst)
+	return err
 }
diff --git a/internal/server/stun.go b/internal/server/stun.go
index 0ad569f..95646cb 100644
--- a/internal/server/stun.go
+++ b/internal/server/stun.go
@@ -1,18 +1,23 @@
 package server
 
 import (
+	"net"
+
 	"github.com/pion/stun"
+	"github.com/pion/turn/internal/ipnet"
 )
 
-func (s *Server) handleBindingRequest(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error {
-	_ = dstAddr // Silence linter
-	return stun.BuildAndSend(s.connection, srcAddr, stun.ClassSuccessResponse, stun.MethodBinding, m.TransactionID,
-		&stun.XorMappedAddress{
-			XorAddress: stun.XorAddress{
-				IP:   srcAddr.IP,
-				Port: srcAddr.Port,
-			},
+func (s *Server) handleBindingRequest(srcAddr, dstAddr net.Addr, m *stun.Message) error {
+	ip, port, err := ipnet.AddrIPPort(srcAddr)
+	if err != nil {
+		return err
+	}
+
+	return buildAndSend(s.connection, srcAddr, &stun.Message{TransactionID: m.TransactionID}, stun.BindingSuccess,
+		&stun.XORMappedAddress{
+			IP:   ip,
+			Port: port,
 		},
-		&stun.Fingerprint{},
+		stun.Fingerprint,
 	)
 }
diff --git a/internal/server/turn.go b/internal/server/turn.go
index 12fc8ae..bdf17c8 100644
--- a/internal/server/turn.go
+++ b/internal/server/turn.go
@@ -2,42 +2,42 @@ package server
 
 import (
 	"crypto/md5" // #nosec
+	"net"
 	"strings"
+	"time"
 
+	"github.com/gortc/turn"
 	"github.com/pion/stun"
-	"github.com/pkg/errors"
 
 	"github.com/pion/turn/internal/allocation"
+	"github.com/pion/turn/internal/ipnet"
+
+	"github.com/pkg/errors"
 )
 
 const (
 	maximumLifetime = uint32(3600) // https://tools.ietf.org/html/rfc5766#section-6.2 defines 3600 recommendation
-	defaultLifetime = uint32(600)  // https://tools.ietf.org/html/rfc5766#section-2.2 defines 600 recommendation
 )
 
-type curriedSend func(class stun.MessageClass, method stun.Method, transactionID []byte, attrs ...stun.Attribute) error
+type curriedSend func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error
 
-func authenticateRequest(curriedSend curriedSend, m *stun.Message, callingMethod stun.Method, realm string, authHandler AuthHandler, srcAddr *stun.TransportAddr) (*stun.MessageIntegrity, string, error) {
-	handleErr := func(err error) (*stun.MessageIntegrity, string, error) {
+func authenticateRequest(curriedSend curriedSend, m *stun.Message, callingMethod stun.Method, realm string, authHandler AuthHandler, srcAddr net.Addr) (stun.MessageIntegrity, string, error) {
+	handleErr := func(err error) (stun.MessageIntegrity, string, error) {
 		if sendErr := curriedSend(stun.ClassErrorResponse, callingMethod, m.TransactionID,
-			&stun.Err400BadRequest,
+			&stun.ErrorCodeAttribute{Code: stun.CodeBadRequest},
 		); sendErr != nil {
 			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
 		}
-		return nil, "", err
+		return stun.MessageIntegrity{}, "", err
 	}
 
-	messageIntegrityAttr := &stun.MessageIntegrity{}
-	messageIntegrityRawAttr, messageIntegrityAttrFound := m.GetOneAttribute(stun.AttrMessageIntegrity)
-
-	if !messageIntegrityAttrFound {
+	if !m.Contains(stun.AttrMessageIntegrity) {
 		return nil, "", curriedSend(stun.ClassErrorResponse, callingMethod, m.TransactionID,
-			&stun.Err401Unauthorized,
-			&stun.Nonce{Nonce: buildNonce()},
-			&stun.Realm{Realm: realm},
+			&stun.ErrorCodeAttribute{Code: stun.CodeUnauthorized},
+			stun.NewNonce(buildNonce()),
+			stun.NewRealm(realm),
 		)
-	} else if err := messageIntegrityAttr.Unpack(m, messageIntegrityRawAttr); err != nil {
-		return handleErr(err)
+
 	}
 
 	var ourKey [16]byte
@@ -45,54 +45,38 @@ func authenticateRequest(curriedSend curriedSend, m *stun.Message, callingMethod
 	usernameAttr := &stun.Username{}
 	realmAttr := &stun.Realm{}
 
-	realmRawAttr, realmFound := m.GetOneAttribute(stun.AttrRealm)
-	if realmFound {
-		if err := realmAttr.Unpack(m, realmRawAttr); err != nil {
-			return handleErr(err)
-		}
-	} else {
-		return handleErr(errors.Errorf("Integrity found, but missing realm"))
+	if err := realmAttr.GetFrom(m); err != nil {
+		return handleErr(err)
 	}
 
-	nonceRawAttr, nonceFound := m.GetOneAttribute(stun.AttrNonce)
-	if nonceFound {
-		if err := nonceAttr.Unpack(m, nonceRawAttr); err != nil {
-			return handleErr(err)
-		}
-	} else {
-		return handleErr(errors.Errorf("Integrity found, but missing nonce"))
+	if err := nonceAttr.GetFrom(m); err != nil {
+		return handleErr(err)
 	}
 
-	usernameRawAttr, usernameFound := m.GetOneAttribute(stun.AttrUsername)
-	if usernameFound {
-		if err := usernameAttr.Unpack(m, usernameRawAttr); err != nil {
-			return handleErr(err)
-		}
-		password, ok := authHandler(usernameAttr.Username, srcAddr)
-		if !ok {
-			return handleErr(errors.Errorf("No user exists for %s", usernameAttr.Username))
-		}
+	if err := usernameAttr.GetFrom(m); err != nil {
+		return handleErr(err)
+	}
 
-		/* #nosec */
-		ourKey = md5.Sum([]byte(usernameAttr.Username + ":" + realmAttr.Realm + ":" + password))
-		if err := assertMessageIntegrity(m, messageIntegrityRawAttr, ourKey[:]); err != nil {
-			return handleErr(err)
-		}
-	} else {
-		return handleErr(errors.Errorf("Integrity found, but missing username"))
+	password, ok := authHandler(usernameAttr.String(), srcAddr)
+	if !ok {
+		return handleErr(errors.Errorf("No user exists for %s", usernameAttr.String()))
 	}
 
-	return &stun.MessageIntegrity{
-		Key: ourKey[:],
-	}, usernameAttr.Username, nil
+	/* #nosec */
+	ourKey = md5.Sum([]byte(usernameAttr.String() + ":" + realmAttr.String() + ":" + password))
+	if err := assertMessageIntegrity(m, ourKey[:]); err != nil {
+		return handleErr(err)
+	}
+
+	return stun.NewLongTermIntegrity(usernameAttr.String(), realmAttr.String(), password), usernameAttr.String(), nil
 }
 
-func assertDontFragment(curriedSend curriedSend, m *stun.Message, attr stun.Attribute) error {
-	if _, ok := m.GetOneAttribute(stun.AttrDontFragment); ok {
+func assertDontFragment(curriedSend curriedSend, m *stun.Message, attr stun.Setter) error {
+	if m.Contains(stun.AttrDontFragment) {
 		err := errors.Errorf("no support for DONT-FRAGMENT")
 		if sendErr := curriedSend(stun.ClassErrorResponse, stun.MethodAllocate, m.TransactionID,
-			&stun.Err420UnknownAttributes,
-			&stun.UnknownAttributes{Attributes: []stun.AttrType{stun.AttrDontFragment}},
+			&stun.ErrorCodeAttribute{Code: stun.CodeUnknownAttribute},
+			&stun.UnknownAttributes{stun.AttrDontFragment},
 			attr,
 		); sendErr != nil {
 			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
@@ -103,13 +87,13 @@ func assertDontFragment(curriedSend curriedSend, m *stun.Message, attr stun.Attr
 }
 
 // https://tools.ietf.org/html/rfc5766#section-6.2
-func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error {
-	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID []byte, attrs ...stun.Attribute) error {
-		return stun.BuildAndSend(s.connection, srcAddr, class, method, transactionID, attrs...)
+func (s *Server) handleAllocateRequest(srcAddr, dstAddr net.Addr, m *stun.Message) error {
+	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
+		return buildAndSend(s.connection, srcAddr, append([]stun.Setter{&stun.Message{TransactionID: transactionID}, stun.NewType(method, class)}, attrs...)...)
 	}
-	respondWithError := func(err error, messageIntegrity *stun.MessageIntegrity, errorCode *stun.ErrorCode) error {
+	respondWithError := func(err error, messageIntegrity stun.MessageIntegrity, errorCode stun.ErrorCode) error {
 		if sendErr := curriedSend(stun.ClassErrorResponse, stun.MethodAllocate, m.TransactionID,
-			errorCode,
+			&stun.ErrorCodeAttribute{Code: errorCode},
 			messageIntegrity,
 		); sendErr != nil {
 			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
@@ -142,7 +126,7 @@ func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stu
 	//    existing allocation.  If yes, the server rejects the request with
 	//    a 437 (Allocation Mismatch) error.
 	if allocation := s.manager.GetAllocation(fiveTuple); allocation != nil {
-		return respondWithError(errors.Errorf("Relay already allocated for 5-TUPLE"), messageIntegrity, &stun.Err437AllocationMismatch)
+		return respondWithError(errors.Errorf("Relay already allocated for 5-TUPLE"), messageIntegrity, stun.CodeAllocMismatch)
 	}
 
 	// 3. The server checks if the request contains a REQUESTED-TRANSPORT
@@ -151,15 +135,12 @@ func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stu
 	//    Request) error.  Otherwise, if the attribute is included but
 	//    specifies a protocol other that UDP, the server rejects the
 	//    request with a 442 (Unsupported Transport Protocol) error.
-	if requestedTransportRawAttr, ok := m.GetOneAttribute(stun.AttrRequestedTransport); true {
-		if !ok {
-			return respondWithError(errors.Errorf("Allocation request missing REQUESTED-TRANSPORT"), messageIntegrity, &stun.Err400BadRequest)
-		}
-
-		requestedTransportAttr := &stun.RequestedTransport{}
-		if err = requestedTransportAttr.Unpack(m, requestedTransportRawAttr); err != nil {
-			return respondWithError(err, messageIntegrity, &stun.Err400BadRequest)
-		}
+	var requestedTransport turn.RequestedTransport
+	if err = requestedTransport.GetFrom(m); err != nil {
+		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
+	}
+	if requestedTransport.Protocol != turn.ProtoUDP {
+		return respondWithError(err, messageIntegrity, stun.CodeUnsupportedTransProto)
 	}
 
 	// 4. The request may contain a DONT-FRAGMENT attribute.  If it does,
@@ -179,19 +160,16 @@ func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stu
 	//     corresponding relayed transport address is still available).  If
 	//     the token is not valid for some reason, the server rejects the
 	//     request with a 508 (Insufficient Capacity) error.
-	if requestedTransportRawAttr, ok := m.GetOneAttribute(stun.AttrReservationToken); ok {
-		if _, containsEvenPort := m.GetOneAttribute(stun.AttrEvenPort); containsEvenPort {
-			return respondWithError(errors.Errorf("Request must not contain RESERVATION-TOKEN and EVEN-PORT"), messageIntegrity, &stun.Err400BadRequest)
+	var reservationTokenAttr turn.ReservationToken
+	if err = reservationTokenAttr.GetFrom(m); err == nil {
+		var evenPort turn.EvenPort
+		if err = evenPort.GetFrom(m); err == nil {
+			return respondWithError(errors.Errorf("Request must not contain RESERVATION-TOKEN and EVEN-PORT"), messageIntegrity, stun.CodeBadRequest)
 		}
 
-		reservationTokenAttr := &stun.ReservationToken{}
-		if err = reservationTokenAttr.Unpack(m, requestedTransportRawAttr); err != nil {
-			return respondWithError(err, messageIntegrity, &stun.Err400BadRequest)
-		}
-
-		allocationPort, reservationFound := s.reservationManager.GetReservation(reservationTokenAttr.ReservationToken)
+		allocationPort, reservationFound := s.reservationManager.GetReservation(string(reservationTokenAttr))
 		if !reservationFound {
-			return respondWithError(errors.Errorf("No reservation found with token %s", reservationTokenAttr.ReservationToken), messageIntegrity, &stun.Err400BadRequest)
+			return respondWithError(errors.Errorf("No reservation found with token %s", string(reservationTokenAttr)), messageIntegrity, stun.CodeBadRequest)
 		}
 		requestedPort = allocationPort + 1
 	}
@@ -202,16 +180,12 @@ func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stu
 	//    below).  If the server cannot satisfy the request, then the
 	//    server rejects the request with a 508 (Insufficient Capacity)
 	//    error.
-	if evenPortRawAttr, ok := m.GetOneAttribute(stun.AttrEvenPort); ok {
-		evenPortAttr := stun.EvenPort{}
-		if err = evenPortAttr.Unpack(m, evenPortRawAttr); err != nil {
-			return respondWithError(err, messageIntegrity, &stun.Err400BadRequest)
-		}
-
+	var evenPort turn.EvenPort
+	if err = evenPort.GetFrom(m); err == nil {
 		randomPort := 0
 		randomPort, err = allocation.GetRandomEvenPort()
 		if err != nil {
-			return respondWithError(err, messageIntegrity, &stun.Err508InsufficentCapacity)
+			return respondWithError(err, messageIntegrity, stun.CodeInsufficientCapacity)
 		}
 		requestedPort = randomPort
 		reservationToken = randSeq(8)
@@ -230,18 +204,17 @@ func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stu
 	//    attribute follow the specification in [RFC5389].
 	// Check current usage vs redis usage of other servers
 	// if bad, redirect { stun.AttrErrorCode, 300 }
-
-	lifetimeDuration := defaultLifetime
-	if lifetimeRawAttr, ok := m.GetOneAttribute(stun.AttrLifetime); ok {
-		lifetimeAttr := stun.Lifetime{}
-		if err = lifetimeAttr.Unpack(m, lifetimeRawAttr); err == nil {
-			lifetimeDuration = min(lifetimeAttr.Duration, maximumLifetime)
+	lifetimeDuration := turn.DefaultLifetime
+	var lifetime turn.Lifetime
+	if err = lifetime.GetFrom(m); err == nil {
+		if uint32(lifetime.Duration/time.Second) < maximumLifetime {
+			lifetimeDuration = lifetime.Duration
 		}
 	}
 
-	a, err := s.manager.CreateAllocation(fiveTuple, s.connection, requestedPort, lifetimeDuration)
+	a, err := s.manager.CreateAllocation(fiveTuple, s.connection, requestedPort, uint32(lifetimeDuration/time.Second))
 	if err != nil {
-		return respondWithError(err, messageIntegrity, &stun.Err508InsufficentCapacity)
+		return respondWithError(err, messageIntegrity, stun.CodeInsufficientCapacity)
 	}
 
 	// Once the allocation is created, the server replies with a success
@@ -254,37 +227,47 @@ func (s *Server) handleAllocateRequest(srcAddr *stun.TransportAddr, dstAddr *stu
 	//     address was reserved).
 	//   * An XOR-MAPPED-ADDRESS attribute containing the client's IP address
 	//     and port (from the 5-tuple).
-	responseAttrs := []stun.Attribute{
-		&stun.XorRelayedAddress{
-			XorAddress: stun.XorAddress{
-				IP:   dstAddr.IP,
-				Port: a.RelayAddr.Port,
-			},
+
+	srcIP, srcPort, err := ipnet.AddrIPPort(srcAddr)
+	if err != nil {
+		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
+	}
+
+	_, relayPort, err := ipnet.AddrIPPort(a.RelayAddr)
+	if err != nil {
+		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
+	}
+
+	dstIP, _, err := ipnet.AddrIPPort(dstAddr)
+	if err != nil {
+		return respondWithError(err, messageIntegrity, stun.CodeBadRequest)
+	}
+
+	responseAttrs := []stun.Setter{
+		&turn.RelayedAddress{
+			IP:   dstIP,
+			Port: relayPort,
 		},
-		&stun.Lifetime{
+		&turn.Lifetime{
 			Duration: lifetimeDuration,
 		},
-		&stun.XorMappedAddress{
-			XorAddress: stun.XorAddress{
-				IP:   srcAddr.IP,
-				Port: srcAddr.Port,
-			},
+		&stun.XORMappedAddress{
+			IP:   srcIP,
+			Port: srcPort,
 		},
 	}
 
 	if reservationToken != "" {
-		s.reservationManager.CreateReservation(reservationToken, a.RelayAddr.Port)
-		responseAttrs = append(responseAttrs, &stun.ReservationToken{
-			ReservationToken: reservationToken,
-		})
+		s.reservationManager.CreateReservation(reservationToken, relayPort)
+		responseAttrs = append(responseAttrs, turn.ReservationToken([]byte(reservationToken)))
 	}
 
 	return curriedSend(stun.ClassSuccessResponse, stun.MethodAllocate, m.TransactionID, append(responseAttrs, messageIntegrity)...)
 }
 
-func (s *Server) handleRefreshRequest(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error {
-	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID []byte, attrs ...stun.Attribute) error {
-		return stun.BuildAndSend(s.connection, srcAddr, class, method, transactionID, attrs...)
+func (s *Server) handleRefreshRequest(srcAddr, dstAddr net.Addr, m *stun.Message) error {
+	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
+		return buildAndSend(s.connection, srcAddr, append([]stun.Setter{&stun.Message{TransactionID: transactionID}, stun.NewType(method, class)}, attrs...)...)
 	}
 	messageIntegrity, _, err := authenticateRequest(curriedSend, m, stun.MethodCreatePermission, s.realm, s.authHandler, srcAddr)
 	if err != nil {
@@ -300,26 +283,26 @@ func (s *Server) handleRefreshRequest(srcAddr *stun.TransportAddr, dstAddr *stun
 		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
 	}
 
-	lifetimeDuration := defaultLifetime
-	if lifetimeRawAttr, ok := m.GetOneAttribute(stun.AttrLifetime); ok {
-		lifetimeAttr := stun.Lifetime{}
-		if err := lifetimeAttr.Unpack(m, lifetimeRawAttr); err == nil {
-			lifetimeDuration = min(lifetimeAttr.Duration, maximumLifetime)
+	lifetimeDuration := turn.DefaultLifetime
+	var lifetime turn.Lifetime
+	if err = lifetime.GetFrom(m); err == nil {
+		if uint32(lifetime.Duration*time.Second) < maximumLifetime {
+			lifetimeDuration = lifetime.Duration
 		}
 	}
-	a.Refresh(lifetimeDuration)
+	a.Refresh(uint32(lifetimeDuration * time.Second))
 
 	return curriedSend(stun.ClassSuccessResponse, stun.MethodRefresh, m.TransactionID,
-		&stun.Lifetime{
+		&turn.Lifetime{
 			Duration: lifetimeDuration,
 		},
 		messageIntegrity,
 	)
 }
 
-func (s *Server) handleCreatePermissionRequest(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error {
-	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID []byte, attrs ...stun.Attribute) error {
-		return stun.BuildAndSend(s.connection, srcAddr, class, method, transactionID, attrs...)
+func (s *Server) handleCreatePermissionRequest(srcAddr, dstAddr net.Addr, m *stun.Message) error {
+	curriedSend := func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
+		return buildAndSend(s.connection, srcAddr, append([]stun.Setter{&stun.Message{TransactionID: transactionID}, stun.NewType(method, class)}, attrs...)...)
 	}
 
 	a := s.manager.GetAllocation(&allocation.FiveTuple{
@@ -336,19 +319,24 @@ func (s *Server) handleCreatePermissionRequest(srcAddr *stun.TransportAddr, dstA
 		return err
 	}
 	addCount := 0
-	if xpas, ok := m.GetAllAttributes(stun.AttrXORPeerAddress); ok {
-		for _, addr := range xpas {
-			peerAddress := stun.XorPeerAddress{}
-			if err := peerAddress.Unpack(m, addr); err == nil {
-				a.AddPermission(&allocation.Permission{
-					Addr: &stun.TransportAddr{
-						IP:   peerAddress.XorAddress.IP,
-						Port: peerAddress.XorAddress.Port,
-					}})
-				addCount++
-			}
+
+	if err := m.ForEach(stun.AttrXORPeerAddress, func(m *stun.Message) error {
+		var peerAddress turn.PeerAddress
+		if err := peerAddress.GetFrom(m); err != nil {
+			return err
 		}
+
+		a.AddPermission(&allocation.Permission{
+			Addr: &net.UDPAddr{
+				IP:   peerAddress.IP,
+				Port: peerAddress.Port,
+			}})
+		addCount++
+		return nil
+	}); err != nil {
+		addCount = 0
 	}
+
 	respClass := stun.ClassSuccessResponse
 	if addCount == 0 {
 		respClass = stun.ClassErrorResponse
@@ -358,7 +346,7 @@ func (s *Server) handleCreatePermissionRequest(srcAddr *stun.TransportAddr, dstA
 		messageIntegrity)
 }
 
-func (s *Server) handleSendIndication(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error {
+func (s *Server) handleSendIndication(srcAddr, dstAddr net.Addr, m *stun.Message) error {
 	a := s.manager.GetAllocation(&allocation.FiveTuple{
 		SrcAddr:  srcAddr,
 		DstAddr:  dstAddr,
@@ -368,39 +356,32 @@ func (s *Server) handleSendIndication(srcAddr *stun.TransportAddr, dstAddr *stun
 		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
 	}
 
-	dataAttr := stun.Data{}
-	dataRawAttr, ok := m.GetOneAttribute(stun.AttrData)
-	if !ok {
-		return nil
-	}
-	if err := dataAttr.Unpack(m, dataRawAttr); err != nil {
+	dataAttr := turn.Data{}
+	if err := dataAttr.GetFrom(m); err != nil {
 		return err
 	}
 
-	xorPeerAddress := stun.XorPeerAddress{}
-	xorPeerAddressRawAttr, ok := m.GetOneAttribute(stun.AttrXORPeerAddress)
-	if !ok {
-		return nil
-	}
-	if err := xorPeerAddress.Unpack(m, xorPeerAddressRawAttr); err != nil {
+	peerAddress := turn.PeerAddress{}
+	if err := peerAddress.GetFrom(m); err != nil {
 		return err
 	}
 
-	msgDst := &stun.TransportAddr{IP: xorPeerAddress.XorAddress.IP, Port: xorPeerAddress.XorAddress.Port}
+	msgDst := &net.UDPAddr{IP: peerAddress.IP, Port: peerAddress.Port}
 	if perm := a.GetPermission(msgDst); perm == nil {
 		return errors.Errorf("Unable to handle send-indication, no permission added: %v", msgDst)
 	}
 
-	l, err := a.RelaySocket.WriteTo(dataAttr.Data, msgDst.Addr())
-	if l != len(dataAttr.Data) {
-		return errors.Errorf("packet write smaller than packet %d != %d (expected) err: %v", l, len(dataAttr.Data), err)
+	l, err := a.RelaySocket.WriteTo(dataAttr, msgDst)
+	if l != len(dataAttr) {
+		return errors.Errorf("packet write smaller than packet %d != %d (expected) err: %v", l, len(dataAttr), err)
 	}
 	return err
 }
 
-func (s *Server) handleChannelBindRequest(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, m *stun.Message) error {
-	errorSend := func(err error, attrs ...stun.Attribute) error {
-		if sendErr := stun.BuildAndSend(s.connection, srcAddr, stun.ClassErrorResponse, stun.MethodChannelBind, m.TransactionID, attrs...); sendErr != nil {
+func (s *Server) handleChannelBindRequest(srcAddr, dstAddr net.Addr, m *stun.Message) error {
+	errorSend := func(err error, attrs ...stun.Setter) error {
+		sendErr := buildAndSend(s.connection, srcAddr, append([]stun.Setter{&stun.Message{TransactionID: m.TransactionID}, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse)}, attrs...)...)
+		if sendErr != nil {
 			err = errors.Errorf(strings.Join([]string{sendErr.Error(), err.Error()}, "\n"))
 		}
 		return err
@@ -415,39 +396,32 @@ func (s *Server) handleChannelBindRequest(srcAddr *stun.TransportAddr, dstAddr *
 		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
 	}
 
-	messageIntegrity, _, err := authenticateRequest(func(class stun.MessageClass, method stun.Method, transactionID []byte, attrs ...stun.Attribute) error {
-		return stun.BuildAndSend(s.connection, srcAddr, class, method, transactionID, attrs...)
+	messageIntegrity, _, err := authenticateRequest(func(class stun.MessageClass, method stun.Method, transactionID [stun.TransactionIDSize]byte, attrs ...stun.Setter) error {
+		return buildAndSend(s.connection, srcAddr, append([]stun.Setter{&stun.Message{TransactionID: m.TransactionID}, stun.NewType(method, class)}, attrs...)...)
 	}, m, stun.MethodChannelBind, s.realm, s.authHandler, srcAddr)
 	if err != nil {
-		return err
+		return errorSend(err, stun.CodeBadRequest)
 	}
 
-	channel := stun.ChannelNumber{}
-	peerAddr := stun.XorPeerAddress{}
-	if cn, ok := m.GetOneAttribute(stun.AttrChannelNumber); ok {
-		if err = channel.Unpack(m, cn); err != nil {
-			return errorSend(err, &stun.Err400BadRequest)
-		}
-	} else {
-		return errorSend(errors.Errorf("ChannelBind missing channel attribute"), &stun.Err400BadRequest)
+	var channel turn.ChannelNumber
+	if err = channel.GetFrom(m); err != nil {
+		return errorSend(err, stun.CodeBadRequest)
 	}
-	if xpa, ok := m.GetOneAttribute(stun.AttrXORPeerAddress); ok {
-		if err = peerAddr.Unpack(m, xpa); err != nil {
-			return errorSend(err, &stun.Err400BadRequest)
-		}
-	} else {
-		return errorSend(errors.Errorf("ChannelBind missing XORPeerAddress attribute"), &stun.Err400BadRequest)
+
+	peerAddr := turn.PeerAddress{}
+	if err = peerAddr.GetFrom(m); err != nil {
+		return errorSend(err, stun.CodeBadRequest)
 	}
 
-	err = a.AddChannelBind(&allocation.ChannelBind{ID: channel.ChannelNumber, Peer: &stun.TransportAddr{IP: peerAddr.XorAddress.IP, Port: peerAddr.XorAddress.Port}})
+	err = a.AddChannelBind(&allocation.ChannelBind{ID: uint16(channel), Peer: &net.UDPAddr{IP: peerAddr.IP, Port: peerAddr.Port}})
 	if err != nil {
-		return errorSend(err, &stun.Err400BadRequest)
+		return errorSend(err, stun.CodeBadRequest)
 	}
 
-	return stun.BuildAndSend(s.connection, srcAddr, stun.ClassSuccessResponse, stun.MethodChannelBind, m.TransactionID, messageIntegrity)
+	return buildAndSend(s.connection, srcAddr, &stun.Message{TransactionID: m.TransactionID}, stun.BindingSuccess, messageIntegrity)
 }
 
-func (s *Server) handleChannelData(srcAddr *stun.TransportAddr, dstAddr *stun.TransportAddr, c *stun.ChannelData) error {
+func (s *Server) handleChannelData(srcAddr, dstAddr net.Addr, c *turn.ChannelData) error {
 	a := s.manager.GetAllocation(&allocation.FiveTuple{
 		SrcAddr:  srcAddr,
 		DstAddr:  dstAddr,
@@ -457,12 +431,12 @@ func (s *Server) handleChannelData(srcAddr *stun.TransportAddr, dstAddr *stun.Tr
 		return errors.Errorf("No allocation found for %v:%v", srcAddr, dstAddr)
 	}
 
-	channel := a.GetChannelByID(c.ChannelNumber)
+	channel := a.GetChannelByID(uint16(c.Number))
 	if channel == nil {
-		return errors.Errorf("No channel bind found for %x \n", c.ChannelNumber)
+		return errors.Errorf("No channel bind found for %x \n", uint16(c.Number))
 	}
 
-	l, err := a.RelaySocket.WriteTo(c.Data, channel.Peer.Addr())
+	l, err := a.RelaySocket.WriteTo(c.Data, channel.Peer)
 	if err != nil {
 		return errors.Wrap(err, "failed writing to socket")
 	}
diff --git a/turn.go b/turn.go
index 3e1f882..7596cff 100644
--- a/turn.go
+++ b/turn.go
@@ -5,14 +5,13 @@ import (
 
 	"net"
 
-	"github.com/pion/stun"
 	"github.com/pion/turn/internal/client"
 	"github.com/pion/turn/internal/server"
 )
 
 // Server is the interface for Pion TURN server callbacks
 type Server interface {
-	AuthenticateRequest(username string, srcAddr *stun.TransportAddr) (password string, ok bool)
+	AuthenticateRequest(username string, srcAddr net.Addr) (password string, ok bool)
 }
 
 // Client is the interface for Pion STUN requests


Test patch:
diff --git a/internal/allocation/allocation_manager_test.go b/internal/allocation/allocation_manager_test.go
index a2259d9..e54519d 100644
--- a/internal/allocation/allocation_manager_test.go
+++ b/internal/allocation/allocation_manager_test.go
@@ -5,7 +5,6 @@ import (
 	"net"
 	"testing"
 
-	"github.com/pion/stun"
 	"github.com/pion/turn/internal/ipnet"
 )
 
@@ -81,7 +80,7 @@ func subTestCreateAllocationDuplicateFiveTuple(t *testing.T, turnSocket ipnet.Pa
 func randomFiveTuple() *FiveTuple {
 	/* #nosec */
 	return &FiveTuple{
-		SrcAddr: &stun.TransportAddr{IP: nil, Port: rand.Int()},
-		DstAddr: &stun.TransportAddr{IP: nil, Port: rand.Int()},
+		SrcAddr: &net.UDPAddr{IP: nil, Port: rand.Int()},
+		DstAddr: &net.UDPAddr{IP: nil, Port: rand.Int()},
 	}
 }