Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions .travis.yml

This file was deleted.

250 changes: 192 additions & 58 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,89 +1,223 @@
go-stun
=======
# go-stun

[![Go Reference (latest release)](https://pkg.go.dev/badge/github.com/ccding/go-stun/stun.svg)](https://pkg.go.dev/github.com/ccding/go-stun/stun)
[![Tests](https://github.com/ccding/go-stun/actions/workflows/go.yml/badge.svg)](https://github.com/ccding/go-stun/actions/workflows/go.yml)
[![License](https://img.shields.io/badge/license-Apache%202.0-red.svg)](LICENSE)

[![License](https://img.shields.io/badge/License-Apache%202.0-red.svg)](https://opensource.org/licenses/Apache-2.0)
[![GoDoc](https://godoc.org/github.com/ccding/go-stun?status.svg)](http://godoc.org/github.com/ccding/go-stun/stun)
[![Go Report Card](https://goreportcard.com/badge/github.com/ccding/go-stun)](https://goreportcard.com/report/github.com/ccding/go-stun)
`go-stun` is a Go library and command-line client for STUN over UDP. It can
discover the public IP address and port assigned to a UDP socket and, when the
server supports the required probes, classify the client's NAT behavior.

go-stun is a STUN (RFC 3489, 5389) client implementation in golang
(a.k.a. UDP hole punching).
STUN is one building block for NAT traversal. This project does not implement a
complete UDP hole-punching, ICE, or TURN solution.

[RFC 3489](https://tools.ietf.org/html/rfc3489):
STUN - Simple Traversal of User Datagram Protocol (UDP)
Through Network Address Translators (NATs)
## Features

[RFC 5389](https://tools.ietf.org/html/rfc5389):
Session Traversal Utilities for NAT (STUN)
- Discover a socket's public (server-reflexive) IP address and port.
- Send [RFC 5389] Binding requests with `SOFTWARE` and `FINGERPRINT`
attributes.
- Perform classic NAT type discovery based on [RFC 3489].
- Test NAT mapping and filtering behavior as described by [RFC 5780].
- Select the STUN server and local IP address or port.
- Reuse an existing `net.PacketConn` from library code.
- Communicate with RFC 3489-only servers through an explicit compatibility
mode.

### Use the Command Line Tool
## Install the command

Simply run these commands (if you have installed golang and set `$GOPATH`)
This README documents the current `master` branch. With Go 1.16 or newer,
install that branch explicitly until the next release is tagged:

```console
go install github.com/ccding/go-stun@master
```
go get github.com/ccding/go-stun

The newest tag, `v0.1.5`, predates RFC 3489 compatibility mode, the `-legacy`
flag, and `ErrBehaviorDiscoveryUnsupported`. Use `@latest` only if you need the
older released interface.

Ensure your Go binary directory (usually `$GOBIN` or `$GOPATH/bin`) is on
`PATH`, then run:

```console
go-stun
```
or clone this repo and run these commands
```
go build

To build from a source checkout instead:

```console
git clone https://github.com/ccding/go-stun.git
cd go-stun
go build .
./go-stun
```
You will get the output like
```

## Command-line usage

Running `go-stun` with no options uses the default server and an automatically
selected local address. Example output:

```text
NAT Type: Full cone NAT
External IP Family: 1
External IP: 166.111.4.100
External Port: 23009
External IP: 203.0.113.10
External Port: 54321
```
You can use `-s` flag to use another STUN server, and use `-v` to work on
verbose mode.

Most public STUN servers, including Google's and Cloudflare's, support basic
Binding requests but not the alternate-address tests needed to classify NAT
behavior. With those servers the client returns `NATUnknown`, a non-nil mapped
address, and a nil error. Full NAT classification requires a server with
RFC 3489 classic NAT-discovery support or RFC 5780 behavior discovery.
```bash
> ./go-stun --help
Usage of ./go-stun:
-b Enable NAT behavior test mode
-i string
The ip on which to bind requests, set to empty will use default
-legacy
Enable compatibility with RFC 3489-only STUN servers
-p int
The port on which to bind requests, set to 0 to pick a random port
-s string
STUN server address (default "stunserver2025.stunprotocol.org:3478")
-v int
Verbose level (0: none, 1: verbose, 2: double verbose, 3: triple verbose)

The values depend on the network and server. Address family `1` denotes IPv4;
`2` denotes IPv6.

Available options:

| Option | Description |
| --- | --- |
| `-s host:port` | Use a specific STUN server. |
| `-i ip` | Bind requests to a local IP address. |
| `-p port` | Bind requests to a local port; `0` selects an available port. |
| `-b` | Run RFC 5780 mapping and filtering behavior tests. |
| `-legacy` | Omit modern optional attributes for RFC 3489-only servers. |
| `-v level` | Set verbosity to `0` (quiet), `1` (protocol trace), or `2`/`3` (also dump packets in hex); values above `3` are rejected. |

Use `go-stun -h` to see the current defaults. For example:

```console
go-stun -s stun.example.com:3478
go-stun -s stun.example.com:3478 -b
go-stun -v 1
```

### Use the Library
## Use the library

Add the package to a Go module:

The library `github.com/ccding/go-stun/stun` is extremely easy to use -- just
one line of code.
```console
go get github.com/ccding/go-stun/stun@master
```

Then create a client and call `Discover`:

```go
import "github.com/ccding/go-stun/stun"
package main

import (
"fmt"
"log"

"github.com/ccding/go-stun/stun"
)

func main() {
nat, host, err := stun.NewClient().Discover()
client := stun.NewClient()

natType, mappedAddr, err := client.Discover()
if err != nil {
log.Fatal(err)
}

fmt.Println("NAT type:", natType)
if mappedAddr != nil {
fmt.Println("Mapped address:", mappedAddr)
}
}
```

Modern Binding requests include SOFTWARE and FINGERPRINT attributes. For an
RFC 3489-only server, enable compatibility mode before discovery. Compatibility
mode is disabled by default, preserving the request format used by earlier
go-stun releases:
If no server is configured, the client uses `stun.DefaultServerAddr`. Other
configuration methods include `SetServerHost`, `SetLocalIP`, `SetLocalPort`,
`SetSoftwareName`, and the verbosity setters. For an existing socket, use
`stun.NewClientWithConnection(conn)` with a `net.PacketConn` created by an
applicable `net.Listen*` function; the caller remains responsible for closing
the connection, and `Keepalive` can refresh its mapping.

Run `go doc github.com/ccding/go-stun/stun` for documentation matching the
version in your module. The linked [package reference] shows the latest tagged
release and will not include `master`-only symbols until a new release is
published.

### NAT behavior discovery

`Discover` first performs a standard Binding request. If that succeeds but the
server does not provide a usable alternate address, it returns
`stun.NATUnknown`, the mapped address, and a `nil` error. The mapped address is
still valid even though the NAT type could not be determined.

Add `"errors"` to the import block, then call `BehaviorTest` (or use the CLI's
`-b` option) for RFC 5780 mapping and filtering tests:

```go
behavior, err := client.BehaviorTest()
if errors.Is(err, stun.ErrBehaviorDiscoveryUnsupported) {
fmt.Println("The server does not support behavior discovery")
} else if err != nil {
fmt.Println("Behavior test failed:", err)
}

if behavior != nil && behavior.MappingType != stun.BehaviorTypeUnknown {
fmt.Println("Mapping behavior:", behavior.MappingType)
}
if behavior != nil && behavior.FilteringType != stun.BehaviorTypeUnknown {
fmt.Println("Filtering behavior:", behavior.FilteringType)
}
```

A non-nil behavior result may contain partial results when a later probe fails.

### RFC 3489 compatibility

Normal requests include `SOFTWARE` and `FINGERPRINT` attributes. Some legacy
RFC 3489 servers reject those attributes; enable compatibility mode for such a
server:

```go
client := stun.NewClient()
client.SetRFC3489Compatibility(true)
nat, host, err := client.Discover()
```

UDP requests retain the RFC 3489 retransmission schedule used by the classic
NAT-discovery algorithm: nine sends starting at 100 ms, doubling to a 1.6 s
cap. This favors legacy behavior over RFC 5389's newer recommended defaults.
The equivalent command-line option is `-legacy`.

## Server requirements and limitations

A successful STUN Binding request only requires the server to return a mapped
address. NAT classification additionally requires an alternate IP address and
port, advertised through RFC 3489's `CHANGED-ADDRESS` or RFC 5780's
`OTHER-ADDRESS` attribute. Many public STUN servers support Binding but do not
support these discovery probes; `NAT type unavailable` is therefore an expected
result with those servers.

UDP requests use the RFC 3489 retransmission schedule: nine sends beginning at
100 ms, doubling up to a 1.6-second interval. A timed-out probe can consequently
take several seconds.

## Security

Report suspected vulnerabilities privately by following the
[security policy](SECURITY.md). Please do not publish vulnerability details in
a GitHub issue.

## Development

Run the checks used by CI before submitting changes:

```console
git ls-files -z -- '*.go' | xargs -0 gofmt -l
go mod tidy
go mod verify
go vet -mod=readonly ./...
staticcheck -checks=all ./...
go test -mod=readonly -race -shuffle=on -covermode=atomic -coverprofile=coverage.out ./...
govulncheck -test ./...
```

The formatting command should produce no output, and `go mod tidy` should not
change `go.mod` or `go.sum`. CI also requires at least 84% statement coverage.
See the [Go CI workflow] for the pinned Go and tool versions and the exact
checks.

## License

`go-stun` is available under the [Apache License 2.0](LICENSE).

More details please go to `main.go` and [GoDoc](http://godoc.org/github.com/ccding/go-stun/stun)
[package reference]: https://pkg.go.dev/github.com/ccding/go-stun/stun
[Go CI workflow]: .github/workflows/go.yml
[RFC 3489]: https://www.rfc-editor.org/rfc/rfc3489.html
[RFC 5389]: https://www.rfc-editor.org/rfc/rfc5389.html
[RFC 5780]: https://www.rfc-editor.org/rfc/rfc5780.html
82 changes: 68 additions & 14 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,75 @@
# Security Policy

## Supported Versions
## Supported versions

Use this section to tell people about which versions of your project are
currently being supported with security updates.
Security fixes land on the current default branch and are published in a new
tagged release. Existing tags are not patched in place, so users should upgrade
to a release containing the fix or, until one is available, to a reviewed
commit on the default branch.

| Version | Supported |
| ------- | ------------------ |
| 5.1.x | :white_check_mark: |
| 5.0.x | :x: |
| 4.0.x | :white_check_mark: |
| < 4.0 | :x: |
| Version | Security updates |
| --- | --- |
| Current default branch | Yes |
| Latest tagged release | Fixes shipped as a new release |
| Older tagged releases | No |

## Reporting a Vulnerability
## Reporting a vulnerability

Use this section to tell people how to report a vulnerability.
Please report suspected vulnerabilities through GitHub's private
[Report a vulnerability] form. Do not disclose vulnerability details in a
public issue, pull request, or discussion before a coordinated fix is
available.

Tell them where to go, how often they can expect to get an update on a
reported vulnerability, what to expect if the vulnerability is accepted or
declined, etc.
Include as much of the following information as possible:

- The affected version, tag, or commit.
- The operating system, Go version, and relevant network configuration.
- A description of the issue, its security impact, and a realistic attack
scenario.
- Minimal reproduction steps, test code, or a sanitized packet capture.
- Any known mitigations or suggested fixes.

Remove credentials and redact unrelated personal or network data from the
report. If a report needs large or sensitive attachments, start with a short
private report so the maintainers can arrange an appropriate transfer method.

## What to expect

The maintainers aim to acknowledge a report within seven days. After an
initial assessment, they will confirm whether the issue is accepted, request
additional information, or explain why it is not considered a vulnerability.
Response and remediation times depend on severity and maintainer availability.

For accepted vulnerabilities, the maintainers will work with the reporter on
a fix and coordinated disclosure. When appropriate, the resolution may include
a new release, a GitHub Security Advisory, and credit for the reporter. Please
allow time for users to upgrade before publishing technical details.

## Scope

Security-relevant reports include, but are not limited to:

- Crashes or excessive resource consumption caused by untrusted STUN packets.
- Acceptance of spoofed, malformed, or incorrectly correlated responses.
- Validation bypasses that produce unsafe behavior in the library or CLI.
- Vulnerabilities in dependencies or repository automation that directly
affect users of `go-stun`.

The following are generally not security vulnerabilities by themselves:

- Disclosure of the public mapped address, which is the intended purpose of
STUN.
- An unavailable or misconfigured third-party STUN server.
- An unknown or inaccurate NAT classification caused by a server that does not
support the required RFC 3489 or RFC 5780 discovery probes.
- Issues that affect only unsupported releases and are already fixed in a
newer supported version.

## Safe testing

Test only systems and networks you own or have explicit permission to assess.
Avoid disrupting public STUN services, accessing other users' data, or
performing tests that could degrade availability. Stop testing and report the
issue if you encounter sensitive data or cause unexpected impact.

[Report a vulnerability]: https://github.com/ccding/go-stun/security/advisories/new