Repository: pion/turn
Pull request #163 (2020-09-17 03:22:27)

Update CI configs to v0.4.7
Update lint scripts and CI configs.

codecov[bot]: # [Codecov](https://codecov.io/gh/pion/turn/pull/163?src=pr&el=h1) Report
> Merging [#163](https://codecov.io/gh/pion/turn/pull/163?src=pr&el=desc) into [master](https://codecov.io/gh/pion/turn/commit/a6478d272b25211e92c61c2ec92e58f5050241cd?el=desc) will **decrease** coverage by `6.26%`.
> The diff coverage is `n/a`.

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

```diff
@@            Coverage Diff             @@
##           master     #163      +/-   ##
==========================================
- Coverage   46.40%   40.13%   -6.27%     
==========================================
  Files          35       25      -10     
  Lines        1821     1019     -802     
==========================================
- Hits          845      409     -436     
+ Misses        906      602     -304     
+ Partials       70        8      -62     
```

| Flag | Coverage Δ | |
|---|---|---|
| #go | `?` | |
| #wasm | `40.13% <ø> (ø)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Impacted Files](https://codecov.io/gh/pion/turn/pull/163?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [internal/allocation/allocation\_manager.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9hbGxvY2F0aW9uX21hbmFnZXIuZ28=) | `0.00% <0.00%> (-49.49%)` | :arrow_down: |
| [internal/allocation/allocation.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9hbGxvY2F0aW9uLmdv) | `39.83% <0.00%> (-46.35%)` | :arrow_down: |
| [internal/allocation/channel\_bind.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9jaGFubmVsX2JpbmQuZ28=) | `25.00% <0.00%> (-41.67%)` | :arrow_down: |
| [internal/allocation/permission.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-aW50ZXJuYWwvYWxsb2NhdGlvbi9wZXJtaXNzaW9uLmdv) | `45.45% <0.00%> (-9.10%)` | :arrow_down: |
| [server.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-c2VydmVyLmdv) | | |
| [relay\_address\_generator\_static.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-cmVsYXlfYWRkcmVzc19nZW5lcmF0b3Jfc3RhdGljLmdv) | | |
| [client.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-Y2xpZW50Lmdv) | | |
| [internal/server/stun.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-aW50ZXJuYWwvc2VydmVyL3N0dW4uZ28=) | | |
| [internal/server/server.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-aW50ZXJuYWwvc2VydmVyL3NlcnZlci5nbw==) | | |
| [server\_config.go](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree#diff-c2VydmVyX2NvbmZpZy5nbw==) | | |
| ... and [4 more](https://codecov.io/gh/pion/turn/pull/163/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pion/turn/pull/163?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/163?src=pr&el=footer). Last update [a6478d2...55a4044](https://codecov.io/gh/pion/turn/pull/163?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Sean-Der: I will take this one!
Sean-Der: I am taking this one!

Patch:
diff --git a/.golangci.yml b/.golangci.yml
index 570f17b..4213697 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -3,6 +3,80 @@ linters-settings:
     check-shadowing: true
   misspell:
     locale: US
+  exhaustive:
+    default-signifies-exhaustive: true
+
+linters:
+  enable:
+    - asciicheck       # Simple linter to check that your code does not contain non-ASCII identifiers
+    - bodyclose        # checks whether HTTP response body is closed successfully
+    - deadcode         # Finds unused code
+    - depguard         # Go linter that checks if package imports are in a list of acceptable packages
+    - dogsled          # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f())
+    - dupl             # Tool for code clone detection
+    - errcheck         # Errcheck is a program for checking for unchecked errors in go programs. These unchecked errors can be critical bugs in some cases
+    - exhaustive       # check exhaustiveness of enum switch statements
+    - exportloopref    # checks for pointers to enclosing loop variables
+    - gci              # Gci control golang package import order and make it always deterministic.
+    - gochecknoglobals # Checks that no globals are present in Go code
+    - gochecknoinits   # Checks that no init functions are present in Go code
+    - gocognit         # Computes and checks the cognitive complexity of functions
+    - goconst          # Finds repeated strings that could be replaced by a constant
+    - gocritic         # The most opinionated Go source code linter
+    - godox            # Tool for detection of FIXME, TODO and other comment keywords
+    - goerr113         # Golang linter to check the errors handling expressions
+    - gofmt            # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification
+    - gofumpt          # Gofumpt checks whether code was gofumpt-ed.
+    - goheader         # Checks is file header matches to pattern
+    - goimports        # Goimports does everything that gofmt does. Additionally it checks unused imports
+    - golint           # Golint differs from gofmt. Gofmt reformats Go source code, whereas golint prints out style mistakes
+    - gomodguard       # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations.
+    - goprintffuncname # Checks that printf-like functions are named with `f` at the end
+    - gosec            # Inspects source code for security problems
+    - gosimple         # Linter for Go source code that specializes in simplifying a code
+    - govet            # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string
+    - ineffassign      # Detects when assignments to existing variables are not used
+    - misspell         # Finds commonly misspelled English words in comments
+    - nakedret         # Finds naked returns in functions greater than a specified function length
+    - noctx            # noctx finds sending http request without context.Context
+    - scopelint        # Scopelint checks for unpinned variables in go programs
+    - staticcheck      # Staticcheck is a go vet on steroids, applying a ton of static analysis checks
+    - structcheck      # Finds unused struct fields
+    - stylecheck       # Stylecheck is a replacement for golint
+    - typecheck        # Like the front-end of a Go compiler, parses and type-checks Go code
+    - unconvert        # Remove unnecessary type conversions
+    - unparam          # Reports unused function parameters
+    - unused           # Checks Go code for unused constants, variables, functions and types
+    - varcheck         # Finds unused global variables and constants
+    - whitespace       # Tool for detection of leading and trailing whitespace
+  disable:
+    - funlen           # Tool for detection of long functions
+    - gocyclo          # Computes and checks the cyclomatic complexity of functions
+    - godot            # Check if comments end in a period
+    - gomnd            # An analyzer to detect magic numbers.
+    - lll              # Reports long lines
+    - maligned         # Tool to detect Go structs that would take less memory if their fields were sorted
+    - nestif           # Reports deeply nested if statements
+    - nlreturn         # nlreturn checks for a new line before return and branch statements to increase code clarity
+    - nolintlint       # Reports ill-formed or insufficient nolint directives
+    - prealloc         # Finds slice declarations that could potentially be preallocated
+    - rowserrcheck     # checks whether Err of rows is checked successfully
+    - sqlclosecheck    # Checks that sql.Rows and sql.Stmt are closed.
+    - testpackage      # linter that makes you use a separate _test package
+    - wsl              # Whitespace Linter - Forces you to use empty lines!
+
+issues:
+  exclude-rules:
+    # Allow complex tests, better to be self contained
+    - path: _test\.go
+      linters:
+        - gocognit
+
+    # Allow complex main function in examples
+    - path: examples
+      text: "of func `main` is high"
+      linters:
+        - gocognit
 
 run:
   skip-dirs-use-default: false
diff --git a/README.md b/README.md
index 3efb4a0..3039cae 100644
--- a/README.md
+++ b/README.md
@@ -91,3 +91,5 @@ Check out the [CONTRIBUTING.md](CONTRIBUTING.md) to join the group of amazing pe
 
 ### License
 MIT License - see [LICENSE.md](LICENSE.md) for full text
+
+
diff --git a/client.go b/client.go
index c89d78e..b04e7d1 100644
--- a/client.go
+++ b/client.go
@@ -18,7 +18,7 @@ import (
 const (
 	defaultRTO        = 200 * time.Millisecond
 	maxRtxCount       = 7              // total 7 requests (Rc)
-	maxDataBufferSize = math.MaxUint16 //message size limit for Chromium
+	maxDataBufferSize = math.MaxUint16 // message size limit for Chromium
 )
 
 //              interval [msec]
@@ -78,7 +78,7 @@ func NewClient(config *ClientConfig) (*Client, error) {
 	log := loggerFactory.NewLogger("turnc")
 
 	if config.Conn == nil {
-		return nil, fmt.Errorf("conn cannot not be nil")
+		return nil, errNilConn
 	}
 
 	if config.Net == nil {
@@ -163,7 +163,7 @@ func (c *Client) WriteTo(data []byte, to net.Addr) (int, error) {
 // to supply incoming data, instead.
 func (c *Client) Listen() error {
 	if err := c.listenTryLock.Lock(); err != nil {
-		return fmt.Errorf("already listening: %s", err.Error())
+		return fmt.Errorf("%w: %s", errAlreadyListening, err.Error())
 	}
 
 	go func() {
@@ -219,7 +219,6 @@ func (c *Client) SendBindingRequestTo(to net.Addr) (net.Addr, error) {
 		return nil, err
 	}
 
-	//return fmt.Sprintf("pkt_size=%d src_addr=%s refl_addr=%s:%d", size, addr, reflAddr.IP, reflAddr.Port), nil
 	return &net.UDPAddr{
 		IP:   reflAddr.IP,
 		Port: reflAddr.Port,
@@ -229,7 +228,7 @@ func (c *Client) SendBindingRequestTo(to net.Addr) (net.Addr, error) {
 // SendBindingRequest sends a new STUN request to the STUN server
 func (c *Client) SendBindingRequest() (net.Addr, error) {
 	if c.stunServ == nil {
-		return nil, fmt.Errorf("STUN server address is not set for the client")
+		return nil, errSTUNServerAddressNotSet
 	}
 	return c.SendBindingRequestTo(c.stunServ)
 }
@@ -237,13 +236,13 @@ func (c *Client) SendBindingRequest() (net.Addr, error) {
 // Allocate sends a TURN allocation request to the given transport address
 func (c *Client) Allocate() (net.PacketConn, error) {
 	if err := c.allocTryLock.Lock(); err != nil {
-		return nil, fmt.Errorf("only one Allocate() caller is allowed: %s", err.Error())
+		return nil, fmt.Errorf("%w: %s", errOneAllocateOnly, err.Error())
 	}
 	defer c.allocTryLock.Unlock()
 
 	relayedConn := c.relayedUDPConn()
 	if relayedConn != nil {
-		return nil, fmt.Errorf("already allocated at %s", relayedConn.LocalAddr().String())
+		return nil, fmt.Errorf("%w: %s", errAlreadyAllocated, relayedConn.LocalAddr().String())
 	}
 
 	msg, err := stun.Build(
@@ -299,9 +298,9 @@ func (c *Client) Allocate() (net.PacketConn, error) {
 	if res.Type.Class == stun.ClassErrorResponse {
 		var code stun.ErrorCodeAttribute
 		if err = code.GetFrom(res); err == nil {
-			return nil, fmt.Errorf("%s (error %s)", res.Type, code)
+			return nil, fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113
 		}
-		return nil, fmt.Errorf("%s", res.Type)
+		return nil, fmt.Errorf("%s", res.Type) //nolint:goerr113
 	}
 
 	// Getting relayed addresses from response.
@@ -411,7 +410,7 @@ func (c *Client) HandleInbound(data []byte, from net.Addr) (bool, error) {
 		return true, c.handleChannelData(data)
 	case len(c.stunServStr) != 0 && from.String() == c.stunServStr:
 		// received from STUN server but it is not a STUN message
-		return true, fmt.Errorf("non-STUN message from STUN server")
+		return true, errNonSTUNMessage
 	default:
 		// assume, this is an application data
 		c.log.Tracef("non-STUN/TURN packect, unhandled")
@@ -426,11 +425,11 @@ func (c *Client) handleSTUNMessage(data []byte, from net.Addr) error {
 
 	msg := &stun.Message{Raw: raw}
 	if err := msg.Decode(); err != nil {
-		return fmt.Errorf("failed to decode STUN message: %s", err.Error())
+		return fmt.Errorf("%w: %s", errFailedToDecodeSTUN, err.Error())
 	}
 
 	if msg.Type.Class == stun.ClassRequest {
-		return fmt.Errorf("unexpected STUN request message: %s", msg.String())
+		return fmt.Errorf("%w : %s", errUnexpectedSTUNRequestMessage, msg.String())
 	}
 
 	if msg.Type.Class == stun.ClassIndication {
@@ -511,7 +510,7 @@ func (c *Client) handleChannelData(data []byte) error {
 
 	addr, ok := relayedConn.FindAddrByChannelNumber(uint16(chData.Number))
 	if !ok {
-		return fmt.Errorf("binding with channel %d not found", int(chData.Number))
+		return fmt.Errorf("%w: %d", errChannelBindNotFound, int(chData.Number))
 	}
 
 	c.log.Tracef("channel data received from %s (ch=%d)", addr.String(), int(chData.Number))
@@ -533,7 +532,7 @@ func (c *Client) onRtxTimeout(trKey string, nRtx int) {
 		// all retransmisstions failed
 		c.trMap.Delete(trKey)
 		if !tr.WriteResult(client.TransactionResult{
-			Err: fmt.Errorf("all retransmissions for %s failed", trKey),
+			Err: fmt.Errorf("%w %s", errAllRetransmissionsFailed, trKey),
 		}) {
 			c.log.Debug("no listener for transaction")
 		}
@@ -546,7 +545,7 @@ func (c *Client) onRtxTimeout(trKey string, nRtx int) {
 	if err != nil {
 		c.trMap.Delete(trKey)
 		if !tr.WriteResult(client.TransactionResult{
-			Err: fmt.Errorf("failed to retransmit transaction %s", trKey),
+			Err: fmt.Errorf("%w %s", errFailedToRetransmitTransaction, trKey),
 		}) {
 			c.log.Debug("no listener for transaction")
 		}
diff --git a/errors.go b/errors.go
index 80a4199..5f587b4 100644
--- a/errors.go
+++ b/errors.go
@@ -3,10 +3,23 @@ package turn
 import "errors"
 
 var (
-	errRelayAddressInvalid        = errors.New("turn: RelayAddress must be valid IP to use RelayAddressGeneratorStatic")
-	errNoAvailableConns           = errors.New("turn: PacketConnConfigs and ConnConfigs are empty, unable to proceed")
-	errConnUnset                  = errors.New("turn: PacketConnConfig must have a non-nil Conn")
-	errListenerUnset              = errors.New("turn: ListenerConfig must have a non-nil Listener")
-	errListeningAddressInvalid    = errors.New("turn: RelayAddressGenerator has invalid ListeningAddress")
-	errRelayAddressGeneratorUnset = errors.New("turn: RelayAddressGenerator in RelayConfig is unset")
+	errRelayAddressInvalid           = errors.New("turn: RelayAddress must be valid IP to use RelayAddressGeneratorStatic")
+	errNoAvailableConns              = errors.New("turn: PacketConnConfigs and ConnConfigs are empty, unable to proceed")
+	errConnUnset                     = errors.New("turn: PacketConnConfig must have a non-nil Conn")
+	errListenerUnset                 = errors.New("turn: ListenerConfig must have a non-nil Listener")
+	errListeningAddressInvalid       = errors.New("turn: RelayAddressGenerator has invalid ListeningAddress")
+	errRelayAddressGeneratorUnset    = errors.New("turn: RelayAddressGenerator in RelayConfig is unset")
+	errNilConn                       = errors.New("turn: conn cannot not be nil")
+	errTODO                          = errors.New("turn: TODO")
+	errAlreadyListening              = errors.New("turn: already listening")
+	errFailedToClose                 = errors.New("turn: Server failed to close")
+	errFailedToRetransmitTransaction = errors.New("turn: failed to retransmit transaction")
+	errAllRetransmissionsFailed      = errors.New("all retransmissions failed for")
+	errChannelBindNotFound           = errors.New("no binding found for channel")
+	errSTUNServerAddressNotSet       = errors.New("STUN server address is not set for the client")
+	errOneAllocateOnly               = errors.New("only one Allocate() caller is allowed")
+	errAlreadyAllocated              = errors.New("already allocated")
+	errNonSTUNMessage                = errors.New("non-STUN message from STUN server")
+	errFailedToDecodeSTUN            = errors.New("failed to decode STUN message")
+	errUnexpectedSTUNRequestMessage  = errors.New("unexpected STUN request message")
 )
diff --git a/internal/allocation/allocation.go b/internal/allocation/allocation.go
index e1a3d18..d1cb8c6 100644
--- a/internal/allocation/allocation.go
+++ b/internal/allocation/allocation.go
@@ -2,7 +2,6 @@
 package allocation
 
 import (
-	"fmt"
 	"net"
 	"sync"
 	"time"
@@ -95,7 +94,7 @@ func (a *Allocation) AddChannelBind(c *ChannelBind, lifetime time.Duration) erro
 	channelByNumber := a.GetChannelByNumber(c.Number)
 
 	if channelByNumber != a.GetChannelByAddr(c.Peer) {
-		return fmt.Errorf("you cannot use the same channel number with different peer")
+		return errSameChannelDifferentPeer
 	}
 
 	// Add or refresh this channel.
diff --git a/internal/allocation/allocation_manager.go b/internal/allocation/allocation_manager.go
index 1b461b6..9a1271a 100644
--- a/internal/allocation/allocation_manager.go
+++ b/internal/allocation/allocation_manager.go
@@ -37,11 +37,11 @@ type Manager struct {
 func NewManager(config ManagerConfig) (*Manager, error) {
 	switch {
 	case config.AllocatePacketConn == nil:
-		return nil, fmt.Errorf("AllocatePacketConn must be set")
+		return nil, errAllocatePacketConnMustBeSet
 	case config.AllocateConn == nil:
-		return nil, fmt.Errorf("AllocateConn must be set")
+		return nil, errAllocateConnMustBeSet
 	case config.LeveledLogger == nil:
-		return nil, fmt.Errorf("LeveledLogger must be set")
+		return nil, errLeveledLoggerMustBeSet
 	}
 
 	return &Manager{
@@ -76,19 +76,19 @@ func (m *Manager) Close() error {
 func (m *Manager) CreateAllocation(fiveTuple *FiveTuple, turnSocket net.PacketConn, requestedPort int, lifetime time.Duration) (*Allocation, error) {
 	switch {
 	case fiveTuple == nil:
-		return nil, fmt.Errorf("allocations must not be created with nil FivTuple")
+		return nil, errNilFiveTuple
 	case fiveTuple.SrcAddr == nil:
-		return nil, fmt.Errorf("allocations must not be created with nil FiveTuple.SrcAddr")
+		return nil, errNilFiveTupleSrcAddr
 	case fiveTuple.DstAddr == nil:
-		return nil, fmt.Errorf("allocations must not be created with nil FiveTuple.DstAddr")
+		return nil, errNilFiveTupleDstAddr
 	case turnSocket == nil:
-		return nil, fmt.Errorf("allocations must not be created with nil turnSocket")
+		return nil, errNilTurnSocket
 	case lifetime == 0:
-		return nil, fmt.Errorf("allocations must not be created with a lifetime of 0")
+		return nil, errLifetimeZero
 	}
 
 	if a := m.GetAllocation(fiveTuple); a != nil {
-		return nil, fmt.Errorf("allocation attempt created with duplicate FiveTuple %v", fiveTuple)
+		return nil, fmt.Errorf("%w: %v", errDupeFiveTuple, fiveTuple)
 	}
 	a := NewAllocation(turnSocket, fiveTuple, m.log)
 
@@ -175,7 +175,7 @@ func (m *Manager) GetRandomEvenPort() (int, error) {
 
 	udpAddr, ok := addr.(*net.UDPAddr)
 	if !ok {
-		return 0, fmt.Errorf("failed to cast net.Addr to *net.UDPAddr")
+		return 0, errFailedToCastUDPAddr
 	} else if err := conn.Close(); err != nil {
 		return 0, err
 	} else if udpAddr.Port%2 == 1 {
diff --git a/internal/allocation/errors.go b/internal/allocation/errors.go
new file mode 100644
index 0000000..fdd8335
--- /dev/null
+++ b/internal/allocation/errors.go
@@ -0,0 +1,17 @@
+package allocation
+
+import "errors"
+
+var (
+	errAllocatePacketConnMustBeSet = errors.New("AllocatePacketConn must be set")
+	errAllocateConnMustBeSet       = errors.New("AllocateConn must be set")
+	errLeveledLoggerMustBeSet      = errors.New("LeveledLogger must be set")
+	errSameChannelDifferentPeer    = errors.New("you cannot use the same channel number with different peer")
+	errNilFiveTuple                = errors.New("allocations must not be created with nil FivTuple")
+	errNilFiveTupleSrcAddr         = errors.New("allocations must not be created with nil FiveTuple.SrcAddr")
+	errNilFiveTupleDstAddr         = errors.New("allocations must not be created with nil FiveTuple.DstAddr")
+	errNilTurnSocket               = errors.New("allocations must not be created with nil turnSocket")
+	errLifetimeZero                = errors.New("allocations must not be created with a lifetime of 0")
+	errDupeFiveTuple               = errors.New("allocation attempt created with duplicate FiveTuple")
+	errFailedToCastUDPAddr         = errors.New("failed to cast net.Addr to *net.UDPAddr")
+)
diff --git a/internal/client/conn.go b/internal/client/conn.go
index 5b5dcdb..8a2b1ae 100644
--- a/internal/client/conn.go
+++ b/internal/client/conn.go
@@ -2,6 +2,7 @@
 package client
 
 import (
+	"errors"
 	"fmt"
 	"io"
 	"math"
@@ -146,7 +147,7 @@ func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
 				Op:   "read",
 				Net:  c.LocalAddr().Network(),
 				Addr: c.LocalAddr(),
-				Err:  fmt.Errorf("use of closed network connection"),
+				Err:  errClosed,
 			}
 		}
 	}
@@ -157,11 +158,11 @@ func (c *UDPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
 // an Error with Timeout() == true after a fixed time limit;
 // see SetDeadline and SetWriteDeadline.
 // On packet-oriented connections, write timeouts are rare.
-func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) {
+func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) { //nolint: gocognit
 	var err error
 	_, ok := addr.(*net.UDPAddr)
 	if !ok {
-		return 0, fmt.Errorf("addr is not a net.UDPAddr")
+		return 0, errUDPAddrCast
 	}
 
 	// check if we have a permission for the destination IP addr
@@ -194,7 +195,7 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) {
 	}
 
 	for i := 0; i < maxRetryAttempts; i++ {
-		if err = createPermission(); err != errTryAgain {
+		if err = createPermission(); !errors.Is(err, errTryAgain) {
 			break
 		}
 	}
@@ -226,7 +227,6 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) {
 						c.log.Warnf("bind() failed: %s", err2.Error())
 						b.setState(bindingStateFailed)
 						// keep going...
-						// TODO: consider try binding again after a while
 					} else {
 						b.setState(bindingStateReady)
 					}
@@ -268,7 +268,6 @@ func (c *UDPConn) WriteTo(p []byte, addr net.Addr) (int, error) {
 					c.log.Warnf("bind() for refresh failed: %s", err.Error())
 					b.setState(bindingStateFailed)
 					// keep going...
-					// TODO: consider try binding again after a while
 				} else {
 					b.setRefreshedAt(time.Now())
 					b.setState(bindingStateReady)
@@ -289,7 +288,7 @@ func (c *UDPConn) Close() error {
 
 	select {
 	case <-c.closeCh:
-		return fmt.Errorf("already closed")
+		return errAlreadyClosed
 	default:
 		close(c.closeCh)
 	}
@@ -396,11 +395,10 @@ func (c *UDPConn) createPermissions(addrs ...net.Addr) error {
 				c.setNonceFromMsg(res)
 				return errTryAgain
 			}
-			err = fmt.Errorf("%s (error %s)", res.Type, code)
-		} else {
-			err = fmt.Errorf("%s", res.Type)
+			return fmt.Errorf("%s (error %s)", res.Type, code) //nolint:goerr113
 		}
-		return err
+
+		return fmt.Errorf("%s", res.Type) //nolint:goerr113
 	}
 
 	return nil
@@ -452,13 +450,13 @@ func (c *UDPConn) refreshAllocation(lifetime time.Duration, dontWait bool) error
 		stun.Fingerprint,
 	)
 	if err != nil {
-		return fmt.Errorf("failed to build refresh request: %s", err.Error())
+		return fmt.Errorf("%w: %s", errFailedToBuildRefreshRequest, err.Error())
 	}
 
 	c.log.Debugf("send refresh request (dontWait=%v)", dontWait)
 	trRes, err := c.obs.PerformTransaction(msg, c.obs.TURNServerAddr(), dontWait)
 	if err != nil {
-		return fmt.Errorf("failed to refresh refresh: %s", err.Error())
+		return fmt.Errorf("%w: %s", errFailedToRefreshAllocation, err.Error())
 	}
 
 	if dontWait {
@@ -478,13 +476,13 @@ func (c *UDPConn) refreshAllocation(lifetime time.Duration, dontWait bool) error
 			}
 			return err
 		}
-		return fmt.Errorf("%s", res.Type)
+		return fmt.Errorf("%s", res.Type) //nolint:goerr113
 	}
 
 	// Getting lifetime from response
 	var updatedLifetime proto.Lifetime
 	if err := updatedLifetime.GetFrom(res); err != nil {
-		return fmt.Errorf("failed to get lifetime from refresh response: %s", err.Error())
+		return fmt.Errorf("%w: %s", errFailedToGetLifetime, err.Error())
 	}
 
 	c.setLifetime(updatedLifetime.Duration)
@@ -499,7 +497,7 @@ func (c *UDPConn) refreshPermissions() error {
 		return nil
 	}
 	if err := c.createPermissions(addrs...); err != nil {
-		if err == errTryAgain {
+		if errors.Is(err, errTryAgain) {
 			return errTryAgain
 		}
 		c.log.Errorf("fail to refresh permissions: %s", err.Error())
@@ -536,7 +534,7 @@ func (c *UDPConn) bind(b *binding) error {
 	res := trRes.Msg
 
 	if res.Type != stun.NewType(stun.MethodChannelBind, stun.ClassSuccessResponse) {
-		return fmt.Errorf("unexpected response type %s", res.Type)
+		return fmt.Errorf("unexpected response type %s", res.Type) //nolint:goerr113
 	}
 
 	c.log.Debugf("channel binding successful: %s %d", b.addr.String(), b.number)
@@ -564,7 +562,7 @@ func (c *UDPConn) onRefreshTimers(id int) {
 		// when stale nonce returns, sencond retry should succeed
 		for i := 0; i < maxRetryAttempts; i++ {
 			err = c.refreshAllocation(lifetime, false)
-			if err != errTryAgain {
+			if !errors.Is(err, errTryAgain) {
 				break
 			}
 		}
@@ -575,7 +573,7 @@ func (c *UDPConn) onRefreshTimers(id int) {
 		var err error
 		for i := 0; i < maxRetryAttempts; i++ {
 			err = c.refreshPermissions()
-			if err != errTryAgain {
+			if !errors.Is(err, errTryAgain) {
 				break
 			}
 		}
diff --git a/internal/client/errors.go b/internal/client/errors.go
index 435a7e2..2d4bd30 100644
--- a/internal/client/errors.go
+++ b/internal/client/errors.go
@@ -4,7 +4,19 @@ import (
 	"errors"
 )
 
-var errTryAgain = errors.New("try again")
+var (
+	errFakeErr                             = errors.New("fake error")
+	errTryAgain                            = errors.New("try again")
+	errClosed                              = errors.New("use of closed network connection")
+	errUDPAddrCast                         = errors.New("addr is not a net.UDPAddr")
+	errAlreadyClosed                       = errors.New("already closed")
+	errDoubleLock                          = errors.New("try-lock is already locked")
+	errTransactionClosed                   = errors.New("transaction closed")
+	errWaitForResultOnNonResultTransaction = errors.New("WaitForResult called on non-result transaction")
+	errFailedToBuildRefreshRequest         = errors.New("failed to build refresh request")
+	errFailedToRefreshAllocation           = errors.New("failed to refresh allocation")
+	errFailedToGetLifetime                 = errors.New("failed to get lifetime from refresh response")
+)
 
 type timeoutError struct {
 	msg string
diff --git a/internal/client/transaction.go b/internal/client/transaction.go
index 169de93..610a4d4 100644
--- a/internal/client/transaction.go
+++ b/internal/client/transaction.go
@@ -1,7 +1,6 @@
 package client
 
 import (
-	"fmt"
 	"net"
 	"sync"
 	"time"
@@ -101,13 +100,13 @@ func (t *Transaction) WriteResult(res TransactionResult) bool {
 func (t *Transaction) WaitForResult() TransactionResult {
 	if t.resultCh == nil {
 		return TransactionResult{
-			Err: fmt.Errorf("WaitForResult called on non-result transaction"),
+			Err: errWaitForResultOnNonResultTransaction,
 		}
 	}
 
 	result, ok := <-t.resultCh
 	if !ok {
-		result.Err = fmt.Errorf("transaction closed")
+		result.Err = errTransactionClosed
 	}
 	return result
 }
@@ -127,8 +126,6 @@ func (t *Transaction) Retries() int {
 	return t.nRtx
 }
 
-////////////////////////////////////////////////////////////////////////////////
-
 // TransactionMap is a thread-safe transaction map
 type TransactionMap struct {
 	trMap map[string]*Transaction
diff --git a/internal/client/trylock.go b/internal/client/trylock.go
index 4f55534..48e25a0 100644
--- a/internal/client/trylock.go
+++ b/internal/client/trylock.go
@@ -1,7 +1,6 @@
 package client
 
 import (
-	"fmt"
 	"sync/atomic"
 )
 
@@ -14,7 +13,7 @@ type TryLock struct {
 // Otherwise, it returns false immedidately.
 func (c *TryLock) Lock() error {
 	if !atomic.CompareAndSwapInt32(&c.n, 0, 1) {
-		return fmt.Errorf("try-lock is already locked")
+		return errDoubleLock
 	}
 	return nil
 }
diff --git a/internal/ipnet/util.go b/internal/ipnet/util.go
index 1e35711..9df7f56 100644
--- a/internal/ipnet/util.go
+++ b/internal/ipnet/util.go
@@ -2,10 +2,12 @@
 package ipnet
 
 import (
-	"fmt"
+	"errors"
 	"net"
 )
 
+var errFailedToCastAddr = errors.New("failed to cast net.Addr to *net.UDPAddr or *net.TCPAddr")
+
 // AddrIPPort extracts the IP and Port from a net.Addr
 func AddrIPPort(a net.Addr) (net.IP, int, error) {
 	aUDP, ok := a.(*net.UDPAddr)
@@ -18,7 +20,7 @@ func AddrIPPort(a net.Addr) (net.IP, int, error) {
 		return aTCP.IP, aTCP.Port, nil
 	}
 
-	return nil, 0, fmt.Errorf("failed to cast net.Addr to *net.UDPAddr or *net.TCPAddr")
+	return nil, 0, errFailedToCastAddr
 }
 
 // AddrEqual asserts that two net.Addrs are equal
diff --git a/internal/proto/reqfamily.go b/internal/proto/reqfamily.go
index dae4f3a..e83d6bb 100644
--- a/internal/proto/reqfamily.go
+++ b/internal/proto/reqfamily.go
@@ -12,6 +12,8 @@ type RequestedAddressFamily byte
 
 const requestedFamilySize = 4
 
+var errInvalidRequestedFamilyValue = errors.New("invalid value for requested family attribute")
+
 // GetFrom decodes REQUESTED-ADDRESS-FAMILY from message.
 func (f *RequestedAddressFamily) GetFrom(m *stun.Message) error {
 	v, err := m.Get(stun.AttrRequestedAddressFamily)
@@ -25,7 +27,7 @@ func (f *RequestedAddressFamily) GetFrom(m *stun.Message) error {
 	case byte(RequestedFamilyIPv4), byte(RequestedFamilyIPv6):
 		*f = RequestedAddressFamily(v[0])
 	default:
-		return errors.New("invalid value for requested family attribute")
+		return errInvalidRequestedFamilyValue
 	}
 	return nil
 }
diff --git a/internal/server/errors.go b/internal/server/errors.go
new file mode 100644
index 0000000..13f8ee1
--- /dev/null
+++ b/internal/server/errors.go
@@ -0,0 +1,26 @@
+package server
+
+import "errors"
+
+var (
+	errFailedToGenerateNonce                  = errors.New("failed to generate nonce")
+	errFailedToSendError                      = errors.New("failed to send error message")
+	errDuplicatedNonce                        = errors.New("duplicated Nonce generated, discarding request")
+	errNoSuchUser                             = errors.New("no such user exists")
+	errUnexpectedClass                        = errors.New("unexpected class")
+	errUnexpectedMethod                       = errors.New("unexpected method")
+	errFailedToHandle                         = errors.New("failed to handle")
+	errUnhandledSTUNPacket                    = errors.New("unhandled STUN packet")
+	errUnableToHandleChannelData              = errors.New("unable to handle ChannelData")
+	errFailedToCreateSTUNPacket               = errors.New("failed to create stun message from packet")
+	errFailedToCreateChannelData              = errors.New("failed to create channel data from packet")
+	errRelayAlreadyAllocatedForFiveTuple      = errors.New("relay already allocated for 5-TUPLE")
+	errRequestedTransportMustBeUDP            = errors.New("RequestedTransport must be UDP")
+	errNoDontFragmentSupport                  = errors.New("no support for DONT-FRAGMENT")
+	errRequestWithReservationTokenAndEvenPort = errors.New("Request must not contain RESERVATION-TOKEN and EVEN-PORT")
+	errNoAllocationFound                      = errors.New("no allocation found")
+	errNoPermission                           = errors.New("unable to handle send-indication, no permission added")
+	errShortWrite                             = errors.New("packet write smaller than packet")
+	errNoSuchChannelBind                      = errors.New("no such channel bind")
+	errFailedWriteSocket                      = errors.New("failed writing to socket")
+)
diff --git a/internal/server/server.go b/internal/server/server.go
index 8e1c043..27f3758 100644
--- a/internal/server/server.go
+++ b/internal/server/server.go
@@ -1,4 +1,4 @@
-//Package server implements the private API to implement a TURN server
+// Package server implements the private API to implement a TURN server
 package server
 
 import (
@@ -46,12 +46,12 @@ func handleDataPacket(r Request) error {
 	r.Log.Debugf("received DataPacket from %s", r.SrcAddr.String())
 	c := proto.ChannelData{Raw: r.Buff}
 	if err := c.Decode(); err != nil {
-		return fmt.Errorf("failed to create channel data from packet: %v", err)
+		return fmt.Errorf("%w: %v", errFailedToCreateChannelData, err)
 	}
 
 	err := handleChannelData(r, &c)
 	if err != nil {
-		err = fmt.Errorf("unable to handle ChannelData from %v: %v", r.SrcAddr, err)
+		err = fmt.Errorf("%w from %v: %v", errUnableToHandleChannelData, r.SrcAddr, err)
 	}
 
 	return err
@@ -61,17 +61,17 @@ func handleTURNPacket(r Request) error {
 	r.Log.Debug("handleTURNPacket")
 	m := &stun.Message{Raw: append([]byte{}, r.Buff...)}
 	if err := m.Decode(); err != nil {
-		return fmt.Errorf("failed to create stun message from packet: %v", err)
+		return fmt.Errorf("%w: %v", errFailedToCreateSTUNPacket, err)
 	}
 
 	h, err := getMessageHandler(m.Type.Class, m.Type.Method)
 	if err != nil {
-		return fmt.Errorf("unhandled STUN packet %v-%v from %v: %v", m.Type.Method, m.Type.Class, r.SrcAddr, err)
+		return fmt.Errorf("%w %v-%v from %v: %v", errUnhandledSTUNPacket, m.Type.Method, m.Type.Class, r.SrcAddr, err)
 	}
 
 	err = h(r, m)
 	if err != nil {
-		return fmt.Errorf("failed to handle %v-%v from %v: %v", m.Type.Method, m.Type.Class, r.SrcAddr, err)
+		return fmt.Errorf("%w %v-%v from %v: %v", errFailedToHandle, m.Type.Method, m.Type.Class, r.SrcAddr, err)
 	}
 
 	return nil
@@ -84,7 +84,7 @@ func getMessageHandler(class stun.MessageClass, method stun.Method) (func(r Requ
 		case stun.MethodSend:
 			return handleSendIndication, nil
 		default:
-			return nil, fmt.Errorf("unexpected method: %s", method)
+			return nil, fmt.Errorf("%w: %s", errUnexpectedMethod, method)
 		}
 
 	case stun.ClassRequest:
@@ -100,10 +100,10 @@ func getMessageHandler(class stun.MessageClass, method stun.Method) (func(r Requ
 		case stun.MethodBinding:
 			return handleBindingRequest, nil
 		default:
-			return nil, fmt.Errorf("unexpected method: %s", method)
+			return nil, fmt.Errorf("%w: %s", errUnexpectedMethod, method)
 		}
 
 	default:
-		return nil, fmt.Errorf("unexpected class: %s", class)
+		return nil, fmt.Errorf("%w: %s", errUnexpectedClass, class)
 	}
 }
diff --git a/internal/server/turn.go b/internal/server/turn.go
index 560f646..cbac09c 100644
--- a/internal/server/turn.go
+++ b/internal/server/turn.go
@@ -40,7 +40,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
 	//    a 437 (Allocation Mismatch) error.
 	if alloc := r.AllocationManager.GetAllocation(fiveTuple); alloc != nil {
 		msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeAllocMismatch})
-		return buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("relay already allocated for 5-TUPLE"), msg...)
+		return buildAndSendErr(r.Conn, r.SrcAddr, errRelayAlreadyAllocatedForFiveTuple, msg...)
 	}
 
 	// 3. The server checks if the request contains a REQUESTED-TRANSPORT
@@ -54,7 +54,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
 		return buildAndSendErr(r.Conn, r.SrcAddr, err, badRequestMsg...)
 	} else if requestedTransport.Protocol != proto.ProtoUDP {
 		msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnsupportedTransProto})
-		return buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("RequestedTransport must be UDP"), msg...)
+		return buildAndSendErr(r.Conn, r.SrcAddr, errRequestedTransportMustBeUDP, msg...)
 	}
 
 	// 4. The request may contain a DONT-FRAGMENT attribute.  If it does,
@@ -64,7 +64,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
 	//    comprehension-required attribute.
 	if m.Contains(stun.AttrDontFragment) {
 		msg := buildMsg(m.TransactionID, stun.NewType(stun.MethodAllocate, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeUnknownAttribute}, &stun.UnknownAttributes{stun.AttrDontFragment})
-		return buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("no support for DONT-FRAGMENT"), msg...)
+		return buildAndSendErr(r.Conn, r.SrcAddr, errNoDontFragmentSupport, msg...)
 	}
 
 	// 5.  The server checks if the request contains a RESERVATION-TOKEN
@@ -79,7 +79,7 @@ func handleAllocateRequest(r Request, m *stun.Message) error {
 	if err = reservationTokenAttr.GetFrom(m); err == nil {
 		var evenPort proto.EvenPort
 		if err = evenPort.GetFrom(m); err == nil {
-			return buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("Request must not contain RESERVATION-TOKEN and EVEN-PORT"), badRequestMsg...)
+			return buildAndSendErr(r.Conn, r.SrcAddr, errRequestWithReservationTokenAndEvenPort, badRequestMsg...)
 		}
 	}
 
@@ -184,7 +184,7 @@ func handleRefreshRequest(r Request, m *stun.Message) error {
 		a := r.AllocationManager.GetAllocation(fiveTuple)
 
 		if a == nil {
-			return fmt.Errorf("no allocation found for %v:%v", r.SrcAddr, r.Conn.LocalAddr())
+			return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
 		}
 		a.Refresh(lifetimeDuration)
 	} else {
@@ -208,7 +208,7 @@ func handleCreatePermissionRequest(r Request, m *stun.Message) error {
 		Protocol: allocation.UDP,
 	})
 	if a == nil {
-		return fmt.Errorf("no allocation found for %v:%v", r.SrcAddr, r.Conn.LocalAddr())
+		return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
 	}
 
 	messageIntegrity, hasAuth, err := authenticateRequest(r, m, stun.MethodCreatePermission)
@@ -255,7 +255,7 @@ func handleSendIndication(r Request, m *stun.Message) error {
 		Protocol: allocation.UDP,
 	})
 	if a == nil {
-		return fmt.Errorf("no allocation found for %v:%v", r.SrcAddr, r.Conn.LocalAddr())
+		return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
 	}
 
 	dataAttr := proto.Data{}
@@ -270,12 +270,12 @@ func handleSendIndication(r Request, m *stun.Message) error {
 
 	msgDst := &net.UDPAddr{IP: peerAddress.IP, Port: peerAddress.Port}
 	if perm := a.GetPermission(msgDst); perm == nil {
-		return fmt.Errorf("unable to handle send-indication, no permission added: %v", msgDst)
+		return fmt.Errorf("%w: %v", errNoPermission, msgDst)
 	}
 
 	l, err := a.RelaySocket.WriteTo(dataAttr, msgDst)
 	if l != len(dataAttr) {
-		return fmt.Errorf("packet write smaller than packet %d != %d (expected) err: %v", l, len(dataAttr), err)
+		return fmt.Errorf("%w %d != %d (expected) err: %v", errShortWrite, l, len(dataAttr), err)
 	}
 	return err
 }
@@ -289,7 +289,7 @@ func handleChannelBindRequest(r Request, m *stun.Message) error {
 		Protocol: allocation.UDP,
 	})
 	if a == nil {
-		return fmt.Errorf("no allocation found for %v:%v", r.SrcAddr, r.Conn.LocalAddr())
+		return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
 	}
 
 	badRequestMsg := buildMsg(m.TransactionID, stun.NewType(stun.MethodChannelBind, stun.ClassErrorResponse), &stun.ErrorCodeAttribute{Code: stun.CodeBadRequest})
@@ -333,19 +333,19 @@ func handleChannelData(r Request, c *proto.ChannelData) error {
 		Protocol: allocation.UDP,
 	})
 	if a == nil {
-		return fmt.Errorf("no allocation found for %v:%v", r.SrcAddr, r.Conn.LocalAddr())
+		return fmt.Errorf("%w %v:%v", errNoAllocationFound, r.SrcAddr, r.Conn.LocalAddr())
 	}
 
 	channel := a.GetChannelByNumber(c.Number)
 	if channel == nil {
-		return fmt.Errorf("no channel bind found for %x", uint16(c.Number))
+		return fmt.Errorf("%w %x", errNoSuchChannelBind, uint16(c.Number))
 	}
 
 	l, err := a.RelaySocket.WriteTo(c.Data, channel.Peer)
 	if err != nil {
-		return fmt.Errorf("failed writing to socket: %s", err.Error())
+		return fmt.Errorf("%w: %s", errFailedWriteSocket, err.Error())
 	} else if l != len(c.Data) {
-		return fmt.Errorf("packet write smaller than packet %d != %d (expected)", l, len(c.Data))
+		return fmt.Errorf("%w %d != %d (expected)", errShortWrite, l, len(c.Data))
 	}
 
 	return nil
diff --git a/internal/server/util.go b/internal/server/util.go
index 60e93de..c6baffb 100644
--- a/internal/server/util.go
+++ b/internal/server/util.go
@@ -1,9 +1,7 @@
 package server
 
 import (
-	// #nosec
-
-	"crypto/md5"
+	"crypto/md5" //nolint:gosec,gci
 	"fmt"
 	"io"
 	"math/rand"
@@ -25,20 +23,19 @@ func randSeq(n int) string {
 	letters := []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
 	b := make([]rune, n)
 	for i := range b {
-		b[i] = letters[rand.Intn(len(letters))]
+		b[i] = letters[rand.Intn(len(letters))] //nolint:gosec
 	}
 	return string(b)
 }
 
-// TODO, include time info support stale nonces
 func buildNonce() (string, error) {
 	/* #nosec */
 	h := md5.New()
 	if _, err := io.WriteString(h, strconv.FormatInt(time.Now().Unix(), 10)); err != nil {
-		return "", fmt.Errorf("failed generate nonce: %v", err)
+		return "", fmt.Errorf("%w: %v", errFailedToGenerateNonce, err)
 	}
-	if _, err := io.WriteString(h, strconv.FormatInt(rand.Int63(), 10)); err != nil {
-		return "", fmt.Errorf("failed generate nonce: %v", err)
+	if _, err := io.WriteString(h, strconv.FormatInt(rand.Int63(), 10)); err != nil { //nolint:gosec
+		return "", fmt.Errorf("%w: %v", errFailedToGenerateNonce, err)
 	}
 	return fmt.Sprintf("%x", h.Sum(nil)), nil
 }
@@ -55,7 +52,7 @@ func buildAndSend(conn net.PacketConn, dst net.Addr, attrs ...stun.Setter) error
 // Send a STUN packet and return the original error to the caller
 func buildAndSendErr(conn net.PacketConn, dst net.Addr, err error, attrs ...stun.Setter) error {
 	if sendErr := buildAndSend(conn, dst, attrs...); sendErr != nil {
-		err = fmt.Errorf("failed to send error message %v %v", sendErr, err)
+		err = fmt.Errorf("%w %v %v", errFailedToSendError, sendErr, err)
 	}
 	return err
 }
@@ -73,7 +70,7 @@ func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method)
 
 		// Nonce has already been taken
 		if _, keyCollision := r.Nonces.LoadOrStore(nonce, time.Now()); keyCollision {
-			return nil, false, fmt.Errorf("duplicated Nonce generated, discarding request")
+			return nil, false, errDuplicatedNonce
 		}
 
 		return nil, false, buildAndSend(r.Conn, r.SrcAddr, buildMsg(m.TransactionID,
@@ -112,7 +109,7 @@ func authenticateRequest(r Request, m *stun.Message, callingMethod stun.Method)
 
 	ourKey, ok := r.AuthHandler(usernameAttr.String(), realmAttr.String(), r.SrcAddr)
 	if !ok {
-		return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("no user exists for %s", usernameAttr.String()), badRequestMsg...)
+		return nil, false, buildAndSendErr(r.Conn, r.SrcAddr, fmt.Errorf("%w %s", errNoSuchUser, usernameAttr.String()), badRequestMsg...)
 	}
 
 	if err := stun.MessageIntegrity(ourKey).Check(m); err != nil {
diff --git a/lt_cred.go b/lt_cred.go
index fff4110..07520b1 100644
--- a/lt_cred.go
+++ b/lt_cred.go
@@ -1,8 +1,8 @@
 package turn
 
-import (
+import ( //nolint:gci
 	"crypto/hmac"
-	"crypto/sha1"
+	"crypto/sha1" //nolint:gosec,gci
 	"encoding/base64"
 	"net"
 	"strconv"
diff --git a/relay_address_generator_none.go b/relay_address_generator_none.go
index a33a868..6fab061 100644
--- a/relay_address_generator_none.go
+++ b/relay_address_generator_none.go
@@ -1,7 +1,6 @@
 package turn
 
 import (
-	"fmt"
 	"net"
 	"strconv"
 
@@ -42,5 +41,5 @@ func (r *RelayAddressGeneratorNone) AllocatePacketConn(network string, requested
 
 // AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with
 func (r *RelayAddressGeneratorNone) AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) {
-	return nil, nil, fmt.Errorf("TODO")
+	return nil, nil, errTODO
 }
diff --git a/relay_address_generator_static.go b/relay_address_generator_static.go
index 11ba905..ae921e7 100644
--- a/relay_address_generator_static.go
+++ b/relay_address_generator_static.go
@@ -1,7 +1,6 @@
 package turn
 
 import (
-	"fmt"
 	"net"
 	"strconv"
 
@@ -52,5 +51,5 @@ func (r *RelayAddressGeneratorStatic) AllocatePacketConn(network string, request
 
 // AllocateConn generates a new Conn to receive traffic on and the IP/Port to populate the allocation response with
 func (r *RelayAddressGeneratorStatic) AllocateConn(network string, requestedPort int) (net.Conn, net.Addr, error) {
-	return nil, nil, fmt.Errorf("TODO")
+	return nil, nil, errTODO
 }
diff --git a/server.go b/server.go
index a3458d9..c8deba8 100644
--- a/server.go
+++ b/server.go
@@ -127,9 +127,9 @@ func (s *Server) Close() error {
 		return nil
 	}
 
-	err := fmt.Errorf("Server failed to close")
+	err := errFailedToClose
 	for _, e := range errors {
-		err = fmt.Errorf("%s; Close error (%v) ", err.Error(), e)
+		err = fmt.Errorf("%s; Close error (%v) ", err.Error(), e) //nolint:goerr113
 	}
 
 	return err
diff --git a/server_config.go b/server_config.go
index 1b55209..9ff3872 100644
--- a/server_config.go
+++ b/server_config.go
@@ -1,8 +1,7 @@
 package turn
 
 import (
-	// #nosec
-	"crypto/md5"
+	"crypto/md5" //nolint:gosec,gci
 	"fmt"
 	"net"
 	"strings"
diff --git a/stun_conn.go b/stun_conn.go
index b93af0c..e1446bd 100644
--- a/stun_conn.go
+++ b/stun_conn.go
@@ -10,8 +10,10 @@ import (
 	"github.com/pion/turn/v2/internal/proto"
 )
 
-var errInvalidTURNFrame = errors.New("data is not a valid TURN frame, no STUN or ChannelData found")
-var errIncompleteTURNFrame = errors.New("data contains incomplete STUN or TURN frame")
+var (
+	errInvalidTURNFrame    = errors.New("data is not a valid TURN frame, no STUN or ChannelData found")
+	errIncompleteTURNFrame = errors.New("data contains incomplete STUN or TURN frame")
+)
 
 // STUNConn wraps a net.Conn and implements
 // net.PacketConn by being STUN aware and
@@ -64,7 +66,7 @@ func consumeSingleTURNFrame(p []byte) (int, error) {
 func (s *STUNConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
 	// First pass any buffered data from previous reads
 	n, err = consumeSingleTURNFrame(s.buff)
-	if err == errInvalidTURNFrame {
+	if errors.Is(err, errInvalidTURNFrame) {
 		return 0, nil, err
 	} else if err == nil {
 		copy(p, s.buff[:n])


Test patch:
diff --git a/internal/client/conn_test.go b/internal/client/conn_test.go
index fcff102..ef73088 100644
--- a/internal/client/conn_test.go
+++ b/internal/client/conn_test.go
@@ -1,7 +1,6 @@
 package client
 
 import (
-	"fmt"
 	"net"
 	"testing"
 
@@ -54,7 +53,7 @@ func TestUDPConn(t *testing.T) {
 	t.Run("bind()", func(t *testing.T) {
 		obs := &dummyUDPConnObserver{
 			_performTransaction: func(msg *stun.Message, to net.Addr, dontWait bool) (TransactionResult, error) {
-				return TransactionResult{}, fmt.Errorf("fake error")
+				return TransactionResult{}, errFakeErr
 			},
 		}
 
diff --git a/internal/proto/chandata_test.go b/internal/proto/chandata_test.go
index a05013b..bcaa0e4 100644
--- a/internal/proto/chandata_test.go
+++ b/internal/proto/chandata_test.go
@@ -4,6 +4,7 @@ import (
 	"bufio"
 	"bytes"
 	"encoding/hex"
+	"errors"
 	"io"
 	"testing"
 )
@@ -127,7 +128,7 @@ func TestChannelData_Decode(t *testing.T) {
 		m := &ChannelData{
 			Raw: tc.buf,
 		}
-		if err := m.Decode(); err != tc.err {
+		if err := m.Decode(); !errors.Is(err, tc.err) {
 			t.Errorf("unexpected: (%s) %v != %v", tc.name, tc.err, err)
 		}
 	}
@@ -231,9 +232,7 @@ func TestChromeChannelData(t *testing.T) {
 	// All hex streams decoded to raw binary format and stored in data slice.
 	// Decoding packets to messages.
 	for i, packet := range data {
-		var (
-			m = new(ChannelData)
-		)
+		m := new(ChannelData)
 		m.Raw = packet
 		if err := m.Decode(); err != nil {
 			t.Errorf("Packet %d: %v", i, err)
diff --git a/internal/proto/chann_test.go b/internal/proto/chann_test.go
index 7e94b68..e21126c 100644
--- a/internal/proto/chann_test.go
+++ b/internal/proto/chann_test.go
@@ -1,6 +1,7 @@
 package proto
 
 import (
+	"errors"
 	"testing"
 
 	"github.com/pion/stun"
@@ -87,7 +88,7 @@ func TestChannelNumber(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				nHandle := new(ChannelNumber)
-				if err := nHandle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := nHandle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 				m.Add(stun.AttrChannelNumber, []byte{1, 2, 3})
diff --git a/internal/proto/chrome_test.go b/internal/proto/chrome_test.go
index 3aac81e..771adda 100644
--- a/internal/proto/chrome_test.go
+++ b/internal/proto/chrome_test.go
@@ -28,9 +28,7 @@ func TestChromeAllocRequest(t *testing.T) {
 	// All hex streams decoded to raw binary format and stored in data slice.
 	// Decoding packets to messages.
 	for i, packet := range data {
-		var (
-			m = new(stun.Message)
-		)
+		m := new(stun.Message)
 		if _, err := m.Write(packet); err != nil {
 			t.Errorf("Packet %d: %v", i, err)
 		}
diff --git a/internal/proto/data_test.go b/internal/proto/data_test.go
index daf6f26..2d7af82 100644
--- a/internal/proto/data_test.go
+++ b/internal/proto/data_test.go
@@ -2,6 +2,7 @@ package proto
 
 import (
 	"bytes"
+	"errors"
 	"testing"
 
 	"github.com/pion/stun"
@@ -78,7 +79,7 @@ func TestData(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				var handle Data
-				if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := handle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 			})
diff --git a/internal/proto/evenport_test.go b/internal/proto/evenport_test.go
index fd2a024..5fa894d 100644
--- a/internal/proto/evenport_test.go
+++ b/internal/proto/evenport_test.go
@@ -1,6 +1,7 @@
 package proto
 
 import (
+	"errors"
 	"testing"
 
 	"github.com/pion/stun"
@@ -65,7 +66,7 @@ func TestEvenPort(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				var handle EvenPort
-				if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := handle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 				m.Add(stun.AttrEvenPort, []byte{1, 2, 3})
diff --git a/internal/proto/lifetime_test.go b/internal/proto/lifetime_test.go
index baf5283..fcff8b9 100644
--- a/internal/proto/lifetime_test.go
+++ b/internal/proto/lifetime_test.go
@@ -1,8 +1,8 @@
 package proto
 
 import (
+	"errors"
 	"testing"
-
 	"time"
 
 	"github.com/pion/stun"
@@ -89,7 +89,7 @@ func TestLifetime(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				nHandle := new(Lifetime)
-				if err := nHandle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := nHandle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 				m.Add(stun.AttrLifetime, []byte{1, 2, 3})
diff --git a/internal/proto/reqfamily_test.go b/internal/proto/reqfamily_test.go
index b306ef4..731cf77 100644
--- a/internal/proto/reqfamily_test.go
+++ b/internal/proto/reqfamily_test.go
@@ -1,6 +1,7 @@
 package proto
 
 import (
+	"errors"
 	"testing"
 
 	"github.com/pion/stun"
@@ -70,7 +71,7 @@ func TestRequestedAddressFamily(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				var handle RequestedAddressFamily
-				if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := handle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 				m.Add(stun.AttrRequestedAddressFamily, []byte{1, 2, 3})
diff --git a/internal/proto/reqtrans_test.go b/internal/proto/reqtrans_test.go
index 26904ec..6780d01 100644
--- a/internal/proto/reqtrans_test.go
+++ b/internal/proto/reqtrans_test.go
@@ -1,6 +1,7 @@
 package proto
 
 import (
+	"errors"
 	"testing"
 
 	"github.com/pion/stun"
@@ -80,7 +81,7 @@ func TestRequestedTransport(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				var handle RequestedTransport
-				if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := handle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 				m.Add(stun.AttrRequestedTransport, []byte{1, 2, 3})
diff --git a/internal/proto/rsrvtoken_test.go b/internal/proto/rsrvtoken_test.go
index f57ac1c..36b6289 100644
--- a/internal/proto/rsrvtoken_test.go
+++ b/internal/proto/rsrvtoken_test.go
@@ -2,6 +2,7 @@ package proto
 
 import (
 	"bytes"
+	"errors"
 	"testing"
 
 	"github.com/pion/stun"
@@ -64,7 +65,7 @@ func TestReservationToken(t *testing.T) {
 			t.Run("HandleErr", func(t *testing.T) {
 				m := new(stun.Message)
 				var handle ReservationToken
-				if err := handle.GetFrom(m); err != stun.ErrAttributeNotFound {
+				if err := handle.GetFrom(m); !errors.Is(err, stun.ErrAttributeNotFound) {
 					t.Errorf("%v should be not found", err)
 				}
 				m.Add(stun.AttrReservationToken, []byte{1, 2, 3})
diff --git a/lt_cred_test.go b/lt_cred_test.go
index 47237f6..93b231e 100644
--- a/lt_cred_test.go
+++ b/lt_cred_test.go
@@ -15,7 +15,7 @@ func TestLtCredMech(t *testing.T) {
 	username := "1599491771"
 	sharedSecret := "foobar"
 
-	expectedPassword := "Tpz/nKkyvX/vMSLKvL4sbtBt8Vs="
+	expectedPassword := "Tpz/nKkyvX/vMSLKvL4sbtBt8Vs=" //nolint:gosec
 	actualPassword, _ := longTermCredentials(username, sharedSecret)
 	if expectedPassword != actualPassword {
 		t.Errorf("Expected %q, got %q", expectedPassword, actualPassword)