SharpICE 0.0.1
dotnet add package SharpICE --version 0.0.1
NuGet\Install-Package SharpICE -Version 0.0.1
<PackageReference Include="SharpICE" Version="0.0.1" />
<PackageVersion Include="SharpICE" Version="0.0.1" />
<PackageReference Include="SharpICE" />
paket add SharpICE --version 0.0.1
#r "nuget: SharpICE, 0.0.1"
#:package SharpICE@0.0.1
#addin nuget:?package=SharpICE&version=0.0.1
#tool nuget:?package=SharpICE&version=0.0.1
SharpICE
STUN, TURN and ICE client and server written in C#, with no native dependencies.
There are three libraries:
- SharpSTUN - STUN client and server
- SharpTURN - TURN client and server, built on SharpSTUN
- SharpICE - ICE agent, built on SharpSTUN and SharpTURN
Implements the following RFCs:
- DNS SRV records RFC2782
- Framing RTP and RTCP Packets over Connection-Oriented Transport RFC4571
- Test Vectors for Session Traversal Utilities for NAT (STUN) RFC5769
- Traversal Using Relays around NAT (TURN) Extensions for TCP Allocations RFC6062
- TCP Candidates with Interactive Connectivity Establishment (ICE) RFC6544
- Multicast DNS RFC6762
- URI Scheme for the Session Traversal Utilities for NAT (STUN) Protocol RFC7064
- Traversal Using Relays around NAT (TURN) Uniform Resource Identifiers RFC7065
- Session Traversal Utilities for NAT (STUN) Usage for Consent Freshness RFC7675
- Multiplexing Scheme Updates for Secure Real-time Transport Protocol (SRTP) Extension for Datagram Transport Layer Security (DTLS) RFC7983
- Interactive Connectivity Establishment (ICE): A Protocol for Network Address Translator (NAT) Traversal RFC8445
- Session Traversal Utilities for NAT (STUN) RFC8489
- Traversal Using Relays around NAT (TURN): Relay Extensions to Session Traversal Utilities for NAT (STUN) RFC8656
- Trickle ICE: Incremental Provisioning of Candidates for the Interactive Connectivity Establishment (ICE) Protocol RFC8838
- Session Description Protocol (SDP) Offer/Answer Procedures for Interactive Connectivity Establishment (ICE) RFC8839
DTLS and TLS
STUN and TURN can run over UDP, TCP, TLS and DTLS. DTLS and TLS are based upon SharpDTLS by default, which supports DTLS 1.2, DTLS 1.3, TLS 1.2 and TLS 1.3. To use another implementation, set DtlsStack or TlsStack in the client or server options:
DtlsStacks.SharpDtls(default) - DTLS 1.3 and DTLS 1.2DtlsStacks.BouncyCastle- DTLS 1.2 onlyTlsStacks.SharpDtls(default) - TLS 1.3 and TLS 1.2TlsStacks.BouncyCastle- TLS 1.2 onlyTlsStacks.System- .NET'sSslStream
With SharpDTLS and BouncyCastle, the server certificate must have an exportable private key.
STUN
Client
Create the STUN client. Username and password are only needed for a server with long-term credentials:
using var client = new StunClient(new StunClientOptions { Username = "user", Password = "pass" });
Connect to the server using a STUN URI. A host without a port is looked up using DNS SRV records and each server is tried in turn:
await client.ConnectAsync("stun:stun.example.org");
Get the address the server sees you at:
IPEndPoint mapped = await client.GetMappedAddressAsync();
Send any other request. When the server asks for credentials, the client sends the request again with them:
StunMessage response = await client.RequestAsync(new StunMessage(StunMethod.Binding, StunMessageClass.Request));
To use TLS, use the "stuns" URI. The server's certificate is checked against the host name:
await client.ConnectAsync("stuns:stun.example.org");
To use DTLS instead of TLS, set UseDtls:
using var client = new StunClient(new StunClientOptions { UseDtls = true });
await client.ConnectAsync("stuns:stun.example.org");
For a single Binding request, you don't need a client at all:
IPEndPoint server = await StunClient.ResolveAsync("stun.l.google.com:19302");
IPEndPoint address = await StunClient.GetMappedAddressAsync(server, TimeSpan.FromSeconds(3));
Server
Create the STUN server. Set Realm and Users to require long-term credentials. Certificate is only needed for TLS and DTLS:
var server = new StunServer(new StunServerOptions
{
Realm = "example.org",
Users = new Dictionary<string, string> { ["user"] = "pass" },
Certificate = certificate,
});
Start the listeners:
server.ListenUdp(new IPEndPoint(IPAddress.Any, 3478));
server.ListenTcp(new IPEndPoint(IPAddress.Any, 3478));
server.ListenTls(new IPEndPoint(IPAddress.Any, 5349));
server.ListenDtls(new IPEndPoint(IPAddress.Any, 5349));
The server answers Binding requests. It supports MD5 and SHA-256 credentials, USERHASH, stateless nonces and ALTERNATE-SERVER redirects. DTLS clients must pass a cookie exchange first, so a spoofed address can't create a connection. Classic STUN (RFC 3489) is not answered.
TURN
Client
Create the TURN client and connect to the server. The transport can be UDP, TCP, TLS or DTLS:
using var turn = new TurnClient(new StunClientOptions { Username = "user", Password = "pass" });
await turn.ConnectAsync("turn:turn.example.org?transport=udp");
Allocate the relayed address. The allocation, permissions and channels are refreshed automatically:
await turn.AllocateAsync();
IPEndPoint relayed = turn.RelayedEndPoint;
Receive data from the peers:
turn.DataReceived += (sender, e) =>
{
IPEndPoint peer = e.Peer;
// the data is in e.Buffer, e.Offset, e.Count
};
Send data to a peer. A permission for the peer is created first, then the data is sent in a Send indication:
await turn.SendAsync(peer, data);
Bind a channel to send the data to the peer as ChannelData, which has less overhead:
await turn.BindChannelAsync(peer);
Delete the allocation:
await turn.DeallocateAsync();
TCP Allocations
A TCP allocation relays TCP connections to the peers. Connect to the server over TCP or TLS and allocate:
using var turn = new TurnClient(new StunClientOptions { Username = "user", Password = "pass" });
await turn.ConnectAsync("turn:turn.example.org?transport=tcp");
await turn.AllocateAsync(new TurnAllocateOptions { Tcp = true });
Connect to a peer. The connection is a plain Stream of the peer's bytes:
using var connection = await turn.ConnectPeerAsync(peer);
await connection.Stream.WriteAsync(data, 0, data.Length);
Accept the peers that connect to the relayed address. Only peers with a permission can connect:
await turn.CreatePermissionAsync(peer.Address);
turn.ConnectionAttempt += async (sender, e) =>
{
using var incoming = await turn.AcceptPeerAsync(e);
// incoming.Stream
};
Server
Create the TURN server. RelayAddress is the public address of the relays:
var server = new TurnServer(new TurnServerOptions
{
Stun = new StunServerOptions
{
Realm = "example.org",
Users = new Dictionary<string, string> { ["user"] = "pass" },
AuthenticateBinding = false,
},
RelayAddress = IPAddress.Parse("203.0.113.7"),
});
Start the listeners:
server.ListenUdp(new IPEndPoint(IPAddress.Any, 3478));
server.ListenTcp(new IPEndPoint(IPAddress.Any, 3478));
The server supports UDP and TCP allocations, dual-stack allocations, EVEN-PORT, permissions and channels. Its defaults are safe: credentials are required, the relays only reach public unicast addresses (use AllowPeer to allow others), the server's own host is never reachable through a relay, and allocations and connections are limited per user and per address.
ICE
The ICE agent has one component, as WebRTC uses it: RTP and RTCP are multiplexed and all media is bundled.
Agent
Create the agent with the STUN and TURN servers to gather the candidates from. The offerer is usually controlling, the answerer controlled:
var options = new IceAgentOptions { Role = IceRole.Controlled };
options.StunServers.Add("stun:stun.l.google.com:19302");
options.TurnServers.Add(new IceTurnServer { Uri = "turn:turn.example.org", Username = "user", Password = "pass" });
var agent = new IceAgent(options);
Gather the local candidates and send them to the peer, together with LocalUfrag and LocalPassword, in the session description or trickled one by one:
var candidates = await agent.GatherCandidatesAsync();
Set the peer's credentials and candidates, then start the checks. Signal the end of the peer's candidates once it has no more:
agent.SetRemoteCredentials(remoteUfrag, remotePassword);
agent.AddRemoteCandidate("candidate:1 1 udp 2130706431 192.168.1.10 50000 typ host");
agent.Start();
agent.AddRemoteCandidate("a=end-of-candidates");
Receive the data that is not STUN, such as DTLS, RTP and RTCP:
agent.DataReceived += (sender, e) =>
{
// the data is in e.Buffer, e.Offset, e.Count
};
Once agent.State is Connected, send data to the peer:
agent.Send(datagram, 0, datagram.Length);
To restart ICE, call Restart and send a new offer with the new LocalUfrag, LocalPassword and candidates. When the peer restarts, pass its new credentials; SetRemoteCredentials returns true:
agent.Restart();
bool restarted = agent.SetRemoteCredentials(newRemoteUfrag, newRemotePassword);
Options
IncludeTcp- gathers ICE-TCP candidates (passive and active), including relayed ones from TURN servers over TCP or TLS. UDP is preferred.RelayOnly- uses relayed candidates only.Lite- makes the agent an ICE lite agent: host candidates only and no checks of its own.PublicAddresses- the public addresses of a 1:1 NAT, such as on a cloud host.MdnsCandidates- hides the local addresses behind random ".local" names, as browsers do. The peer's ".local" candidates are always resolved using multicast DNS.
The agent also supports peer reflexive candidates, triggered checks, regular nomination, role conflict resolution and consent freshness.
Samples
The samples run against each other on one machine with their default settings:
StunServerSample [port]- STUN server on UDP and TCP, port 3478 by defaultStunClientSample [uri] [username] [password]- shows the address the STUN server sees you at,stun:127.0.0.1:3478by defaultTurnServerSample [port] [username] [password] [realm] [relayAddress]- TURN server with UDP and TCP relays, port 3479 by default, useruserand passwordpassTurnClientSample [uri] [username] [password] [peer]- allocates a relay and sends data through it to an echo peer and back,turn:127.0.0.1:3479by default
dotnet run --project src/TurnServerSample
dotnet run --project src/TurnClientSample
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 is compatible. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.0.1 | 49 | 11/11/2026 |