@@ -0,0 +1,165 @@
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_PS4 || UNITY_PSM
using System;
using System.Net;
using System.Net.Sockets;
using UdpKit;

class DotNetSocket : UdpPlatformSocket {
string error;
Socket socket;
DotNetPlatform platform;
EndPoint recvEndPoint;
UdpEndPoint endpoint;

public override UdpPlatform Platform {
get { return platform; }
}

public DotNetSocket(DotNetPlatform platform) {
this.platform = platform;

try {
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Blocking = false;

SetConnReset(socket);
}
catch (SocketException exn) {
HandleSocketException(exn);
}

recvEndPoint = new IPEndPoint(IPAddress.Any, 0);
}

public override string Error {
get { return error; }
}

public override bool IsBound {
get { return socket != null && socket.IsBound; }
}

public override UdpEndPoint EndPoint {
get {
VerifyIsBound();
return endpoint;
}
}

public override bool Broadcast {
get {
VerifyIsBound();

try {
error = null;
return socket.EnableBroadcast;
}
catch (SocketException exn) {
HandleSocketException(exn);
return false;
}
}
set {
VerifyIsBound();

try {
socket.EnableBroadcast = value;
}
catch (SocketException exn) {
error = null;
HandleSocketException(exn);
}
}
}

public override void Close() {
VerifyIsBound();

try {
error = null;
socket.Close();
}
catch (SocketException exn) {
HandleSocketException(exn);
}
}

public override void Bind(UdpEndPoint ep) {
try {
error = null;
socket.Bind(DotNetPlatform.ConvertEndPoint(ep));

endpoint = DotNetPlatform.ConvertEndPoint(socket.LocalEndPoint);

UdpLog.Info("Socket bound to {0}", endpoint);
}
catch (SocketException exn) {
HandleSocketException(exn);
}
}

public override bool RecvPoll() {
return RecvPoll(0);
}

public override bool RecvPoll(int timeoutInMs) {
try {
return socket.Poll(timeoutInMs * 1000, SelectMode.SelectRead);
}
catch (SocketException exn) {
HandleSocketException(exn);
return false;
}
}

public override int RecvFrom(byte[] buffer, int bufferSize, ref UdpEndPoint endpoint) {
try {
int bytesReceived = socket.ReceiveFrom(buffer, 0, bufferSize, SocketFlags.None, ref recvEndPoint);

if (bytesReceived > 0) {
endpoint = DotNetPlatform.ConvertEndPoint(recvEndPoint);
return bytesReceived;
}
else {
return -1;
}
}
catch (SocketException exn) {
HandleSocketException(exn);
return -1;
}
}


public override int SendTo(byte[] buffer, int bytesToSend, UdpEndPoint endpoint) {
try {
return socket.SendTo(buffer, 0, bytesToSend, SocketFlags.None, DotNetPlatform.ConvertEndPoint(endpoint));
}
catch (SocketException exn) {
HandleSocketException(exn);
return -1;
}
}

void HandleSocketException(SocketException exn) {
error = exn.ErrorCode + ": " + exn.SocketErrorCode.ToString();
}

void VerifyIsBound() {
if (IsBound == false) {
throw new InvalidOperationException();
}
}

static void SetConnReset(Socket s) {
try {
const uint IOC_IN = 0x80000000;
const uint IOC_VENDOR = 0x18000000;
uint SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
s.IOControl((int)SIO_UDP_CONNRESET, new byte[] { Convert.ToByte(false) }, null);
}
catch { }
}

}
#endif
@@ -0,0 +1,77 @@
#if (UNITY_ANDROID || UNITY_IPHONE) && !UNITY_EDITOR
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
using System.Security;
using System;
using UdpKit;

public static class NativePInvoke {
#if UNITY_ANDROID
public const string DLL_NAME = "udpkit_android";
#elif UNITY_IPHONE
public const string DLL_NAME = "__Internal";
#else
public const string DLL_NAME = null;
#endif

public const int UDPKIT_SOCKET_OK = 0;
public const int UDPKIT_SOCKET_ERROR = -1;
public const int UDPKIT_SOCKET_NOTVALID = -2;
public const int UDPKIT_SOCKET_NODATA = -3;

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateSocket();

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 Bind(IntPtr socket, UdpEndPoint.Native addr);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 BroadcastEnable(IntPtr socket);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 BroadcastDisable(IntPtr socket);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 SendTo(IntPtr socket, byte[] buffer, int size, UdpEndPoint.Native addr);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 RecvFrom(IntPtr socket, [In,Out] byte[] buffer, int size, [Out] out UdpEndPoint.Native addr);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 RecvPoll(IntPtr socket, int timeout);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 GetEndPoint(IntPtr socket, [Out] out UdpEndPoint.Native addr);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern Int32 Close(IntPtr socket);

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
static extern IntPtr PlatformName();
public static string PlatformName_Wrapper() { return Marshal.PtrToStringAnsi(PlatformName()); }

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
static extern IntPtr Error();
public static string Error_Wrapper() { return Marshal.PtrToStringAnsi(Error()); }

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern UInt32 GetPrecisionTime();

[DllImport(DLL_NAME)]
[SuppressUnmanagedCodeSecurity]
public static extern UInt32 GetBroadcastAddress();
}
#endif
@@ -0,0 +1,84 @@
#if (UNITY_ANDROID || UNITY_IPHONE) && !UNITY_EDITOR
using UdpKit;
using UnityEngine;
using System.Collections;

public class NativePlatform : UdpKit.UdpPlatform {
static readonly object timeLock = new object();

public override UdpKit.UdpPlatformSocket CreateSocket() {
return new NativeSocket(this);
}

public override UdpKit.UdpIPv4Address GetBroadcastAddress() {
#if UNITY_IPHONE
var addr = NativePInvoke.GetBroadcastAddress();
return new UdpKit.UdpIPv4Address((byte) (addr >> 0), (byte) (addr >> 8), (byte) (addr >> 16), (byte) (addr >> 24));
#elif UNITY_ANDROID
return Android.GetBroadcastAddress();
#else
return new UdpKit.UdpIPv4Address(255, 255, 255, 255);
#endif
}

public override UdpIPv4Address[] ResolveHostAddresses(string host) {
return new UdpIPv4Address[0];
}

public override System.Collections.Generic.List<UdpKit.UdpPlatformInterface> GetNetworkInterfaces() {
return new System.Collections.Generic.List<UdpKit.UdpPlatformInterface>();
}

public override uint GetPrecisionTime() {
lock (timeLock) {
return NativePInvoke.GetPrecisionTime();
}
}

public override bool SupportsBroadcast {
get { return true; }
}

public override bool SupportsMasterServer {
get { return true; }
}

#if UNITY_ANDROID
static class Android {
static AndroidJavaObject androidMulticastLock;

public static UdpIPv4Address GetBroadcastAddress () {
AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject wifi = activity.Call<AndroidJavaObject>("getSystemService", "wifi");
AndroidJavaObject dhcp = wifi.Call<AndroidJavaObject>("getDhcpInfo");

int dhcp_ip = dhcp.Get<int>("ipAddress");
int dhcp_mask = dhcp.Get<int>("netmask");
int broadcast = (dhcp_ip & dhcp_mask) | ~dhcp_mask;
byte[] quads = new byte[4];

for (int k = 0; k < 4; k++) {
quads[k] = (byte) ((broadcast >> k * 8) & 0xFF);
}

return new UdpIPv4Address(quads[0], quads[1], quads[2], quads[3]);
}

public static void AcquireMulticastLock () {
AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject wifi = activity.Call<AndroidJavaObject>("getSystemService", "wifi");
androidMulticastLock = wifi.Call<AndroidJavaObject>("createMulticastLock", "udpkit");
androidMulticastLock.Call("acquire");
}

public static void ReleaseMulticastLock () {
if (androidMulticastLock != null) {
androidMulticastLock.Call("release");
androidMulticastLock.Dispose();
androidMulticastLock = null;
}
}
}
#endif
}
#endif
@@ -0,0 +1,127 @@
#if (UNITY_ANDROID || UNITY_IPHONE) && !UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UdpKit;

public class NativeSocket : UdpKit.UdpPlatformSocket {
NativePlatform platform;

IntPtr socket;
bool broadcast;

public override bool Broadcast {
get {
return broadcast;
}
set {
if (value) {
NativePInvoke.BroadcastEnable(socket);
}
else {
NativePInvoke.BroadcastDisable(socket);
}

broadcast = value;
}
}

public override UdpEndPoint EndPoint {
get {
UdpEndPoint.Native ep = default(UdpEndPoint.Native);

if (NativePInvoke.GetEndPoint(socket, out ep) == NativePInvoke.UDPKIT_SOCKET_OK) {
return ep.AsManaged;
}

return UdpEndPoint.Any;
}
}

public override string Error {
get { return NativePInvoke.Error_Wrapper(); }
}

public override bool IsBound {
get { return EndPoint != UdpEndPoint.Any; }
}

public override UdpKit.UdpPlatform Platform {
get { return platform; }
}

public NativeSocket(NativePlatform p) {
platform = p;
broadcast = false;
socket = NativePInvoke.CreateSocket();
}

public override void Bind(UdpKit.UdpEndPoint ep) {
CheckResult(NativePInvoke.Bind(socket, ep.AsNative));
}

public override void Close() {
CheckResult(NativePInvoke.Close(socket));
socket = IntPtr.Zero;
}

public override int RecvFrom(byte[] buffer, int bufferSize, ref UdpKit.UdpEndPoint endpoint) {
var sender = default(UdpEndPoint.Native);
var bytesReceived = NativePInvoke.RecvFrom(socket, buffer, bufferSize, out sender);

if (bytesReceived > 0) {
endpoint = sender.AsManaged;
}

if (bytesReceived < 0) {
UdpLog.Error(Error);
bytesReceived = -1;
}

return bytesReceived;
}

public override bool RecvPoll(int timeout) {
return CheckResult(NativePInvoke.RecvPoll(socket, timeout));
}

public override bool RecvPoll() {
return RecvPoll(0);
}

public override int SendTo(byte[] buffer, int bytesToSend, UdpKit.UdpEndPoint endpoint) {
var bytesSent = NativePInvoke.SendTo(socket, buffer, bytesToSend, endpoint.AsNative);

if (bytesSent >= 0) {
return bytesSent;
}

UdpLog.Error(Error);
return -1;
}

bool CheckResult(int result) {
if (result == NativePInvoke.UDPKIT_SOCKET_OK) {
return true;
}

if (result == NativePInvoke.UDPKIT_SOCKET_ERROR) {
UdpLog.Error(Error);
UdpLog.Error(System.Environment.StackTrace);
return false;
}

if (result == NativePInvoke.UDPKIT_SOCKET_NOTVALID) {
UdpLog.Error("Invalid socket pointer: {0}", socket);
return false;
}

if (result == NativePInvoke.UDPKIT_SOCKET_NODATA) {
return false;
}

throw new Exception(string.Format("Unknown return code: {0}", result));
}
}
#endif
@@ -0,0 +1,70 @@
#if UNITY_EDITOR || UNITY_STANDALONE || UNITY_WEBPLAYER || UNITY_PS4 || UNITY_PSM || UNITY_WP8
#define USE_DOTNET
#endif

using UnityEngine;
using System.Collections;
using UdpKit;

#if USE_DOTNET
using System.Diagnostics;
#endif

public class NullPlatform : UdpPlatform {
#if USE_DOTNET
class PrecisionTimer {
static readonly long start = Stopwatch.GetTimestamp();
static readonly double freq = 1.0 / (double)Stopwatch.Frequency;

internal static uint GetCurrentTime() {
long diff = Stopwatch.GetTimestamp() - start;
double seconds = (double)diff * freq;
return (uint)(seconds * 1000.0);
}
}
#elif (UNITY_ANDROID || UNITY_IPHONE) && !UNITY_EDITOR
readonly object timeLock = new object();
#endif

public NullPlatform() {
GetPrecisionTime();
}

public override UdpPlatformSocket CreateSocket() {
return new NullSocket(this);
}

public override UdpIPv4Address GetBroadcastAddress() {
return UdpIPv4Address.Broadcast;
}

public override System.Collections.Generic.List<UdpPlatformInterface> GetNetworkInterfaces() {
return new System.Collections.Generic.List<UdpPlatformInterface>();
}

public override uint GetPrecisionTime() {
#if USE_DOTNET
return PrecisionTimer.GetCurrentTime();
#elif (UNITY_ANDROID || UNITY_IPHONE) && !UNITY_EDITOR
lock (timeLock) {
return NativePInvoke.GetPrecisionTime();
}
#endif
}

public override UdpIPv4Address[] ResolveHostAddresses(string host) {
return new UdpIPv4Address[0];
}

public override bool SupportsBroadcast {
get { return false; }
}

public override bool SupportsMasterServer {
get { return false; }
}

public override bool IsNull {
get { return true; }
}
}
@@ -0,0 +1,55 @@
using UnityEngine;
using System.Collections;
using UdpKit;

public class NullSocket : UdpPlatformSocket {
NullPlatform platform;

public NullSocket(NullPlatform p) {
platform = p;
}

public override void Bind(UdpEndPoint ep) {

}

public override bool Broadcast {
get;
set;
}

public override void Close() {
}

public override UdpEndPoint EndPoint {
get { return UdpEndPoint.Any; }
}

public override string Error {
get { return ""; }
}

public override bool IsBound {
get { return true; }
}

public override UdpPlatform Platform {
get { return platform; }
}

public override int RecvFrom(byte[] buffer, int bufferSize, ref UdpEndPoint remoteEndpoint) {
return 0;
}

public override bool RecvPoll(int timeout) {
return false;
}

public override bool RecvPoll() {
return false;
}

public override int SendTo(byte[] buffer, int bytesToSend, UdpEndPoint endpoint) {
return bytesToSend;
}
}
@@ -0,0 +1,44 @@
#if UNITY_WP8 && !UNITY_EDITOR
using UnityEngine;
using System.Collections;

public class Wp8Interface : UdpKit.UdpPlatformInterface {
readonly UdpKit.UdpIPv4Address[] unicast;
readonly UdpKit.UdpIPv4Address[] gateway;
readonly UdpKit.UdpIPv4Address[] multicast;

public Wp8Interface(UdpKit.UdpIPv4Address address) {
unicast = new UdpKit.UdpIPv4Address[1] { address };

address.Byte0 = 1;
gateway = new UdpKit.UdpIPv4Address[1] { address };

address.Byte0 = 255;
multicast = new UdpKit.UdpIPv4Address[2] { UdpKit.UdpIPv4Address.Broadcast, address };
}

public override UdpKit.UdpIPv4Address[] GatewayAddresses {
get { return gateway; }
}

public override UdpKit.UdpLinkType LinkType {
get { return UdpKit.UdpLinkType.Unknown; }
}

public override UdpKit.UdpIPv4Address[] MulticastAddresses {
get { return multicast; }
}

public override string Name {
get { return "UNKNOWN"; }
}

public override byte[] PhysicalAddress {
get { return new byte[0]; }
}

public override UdpKit.UdpIPv4Address[] UnicastAddresses {
get { return unicast; }
}
}
#endif
@@ -0,0 +1,65 @@
#if UNITY_WP8 && !UNITY_EDITOR
using System.Diagnostics;

public class Wp8Platform : UdpKit.UdpPlatform {
class PrecisionTimer {
static readonly long start = Stopwatch.GetTimestamp();
static readonly double freq = 1.0 / (double)Stopwatch.Frequency;

internal static uint GetCurrentTime() {
long diff = Stopwatch.GetTimestamp() - start;
double seconds = (double)diff * freq;
return (uint)(seconds * 1000.0);
}
}

public Wp8Platform() {
GetPrecisionTime();
}

public override UdpKit.UdpPlatformSocket CreateSocket() {
return new Wp8Socket(this);
}

public override UdpKit.UdpIPv4Address GetBroadcastAddress() {
return UdpKit.UdpIPv4Address.Broadcast;
}

public override System.Collections.Generic.List<UdpKit.UdpPlatformInterface> GetNetworkInterfaces() {
var result = new System.Collections.Generic.List<UdpKit.UdpPlatformInterface>();

foreach (var addr in UdpKit.Wp8Platform.GetIpAddresses()) {
try {
var ipv4 = UdpKit.UdpIPv4Address.Parse(addr);

if (ipv4.IsPrivate) {
result.Add(new Wp8Interface(ipv4));
}
}
catch { }
}

return result;
}

public override uint GetPrecisionTime() {
return PrecisionTimer.GetCurrentTime();
}

public override UdpKit.UdpIPv4Address[] ResolveHostAddresses(string host) {
return new UdpKit.UdpIPv4Address[0];
}

public override bool SupportsBroadcast {
get { return false; }
}

public override bool SupportsMasterServer {
get { return true; }
}

public override bool IsNull {
get { return false; }
}
}
#endif
@@ -0,0 +1,87 @@
#if UNITY_WP8 && !UNITY_EDITOR
using System.Net;

public class Wp8Socket : UdpKit.UdpPlatformSocket {
Wp8Platform platform;

UdpKit.Wp8Socket socket;
UdpKit.UdpEndPoint endpoint;

public Wp8Socket(Wp8Platform p) {
socket = new UdpKit.Wp8Socket(BoltLog.Warn, 8192);
platform = p;
}

public override void Bind(UdpKit.UdpEndPoint ep) {
socket.Bind(ep.Port.ToString());
}

public override bool Broadcast {
get { return false; }
set { }
}

public override void Close() {
socket.Close();
}

public override UdpKit.UdpEndPoint EndPoint {
get { return UdpKit.UdpEndPoint.Parse("0.0.0.0:" + socket.LocalPort); }
}

public override string Error {
get { return ""; }
}

public override bool IsBound {
get { return socket.IsBound; }
}

public override UdpKit.UdpPlatform Platform {
get { return platform; }
}

public override int RecvFrom(byte[] buffer, int bufferSize, ref UdpKit.UdpEndPoint remoteEndpoint) {
var host = "";
var port = "";
var bytes = socket.RecvFrom(buffer, bufferSize, ref host, ref port);

if (bytes > 0 && host != null && port != null) {
remoteEndpoint = UdpKit.UdpEndPoint.Parse(host + ":" + port);
return bytes;
}

return 0;
}

public override bool RecvPoll(int timeout) {
return socket.RecvPoll(timeout);
}

public override bool RecvPoll() {
return RecvPoll(0);
}

public override int SendTo(byte[] buffer, int bytesToSend, UdpKit.UdpEndPoint endpoint) {
return socket.SendTo(buffer, bytesToSend, endpoint.Address.ToString(), endpoint.Port.ToString());
}

#pragma warning disable 618
static UdpKit.UdpEndPoint ConvertEndPoint(EndPoint endpoint) {
return ConvertEndPoint((IPEndPoint)endpoint);
}

static UdpKit.UdpEndPoint ConvertEndPoint(IPEndPoint endpoint) {
return new UdpKit.UdpEndPoint(new UdpKit.UdpIPv4Address(endpoint.Address.Address), (ushort)endpoint.Port);
}

static UdpKit.UdpIPv4Address ConvertAddress(IPAddress address) {
return new UdpKit.UdpIPv4Address(address.Address);
}

static IPEndPoint ConvertEndPoint(UdpKit.UdpEndPoint endpoint) {
return new IPEndPoint(new IPAddress(new byte[] { endpoint.Address.Byte3, endpoint.Address.Byte2, endpoint.Address.Byte1, endpoint.Address.Byte0 }), endpoint.Port);
}
#pragma warning restore 618
}
#endif
@@ -0,0 +1,345 @@
#if BOLT_UPNP_SUPPORT && UNITY_STANDALONE
using Mono.Nat;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using UdpKit;
using UnityEngine;

namespace BoltInternal {
public class StandaloneNatCommunicator : BoltInternal.NatCommunicator {

class NatPortMappingChanged {
public NatDeviceState Device;
public NatPortMapping Mapping;
}

class NatPortMapping : Bolt.IPortMapping {
public volatile int External;
public volatile int Internal;
public volatile Bolt.NatPortMappingStatus Status;

ushort Bolt.IPortMapping.External {
get { return (ushort)External; }
}

ushort Bolt.IPortMapping.Internal {
get { return (ushort)Internal; }
}

Bolt.NatPortMappingStatus Bolt.IPortMapping.Status {
get { return Status; }
}

public NatPortMapping Clone() {
return (NatPortMapping)MemberwiseClone();
}

public override int GetHashCode() {
return External ^ Internal;
}

public override bool Equals(object obj) {
var that = obj as NatPortMapping;
if (that != null) {
return this.Internal == that.Internal && this.External == that.External;
}
else {
return false;
}
}

public override string ToString() {
return string.Format("[PortMapping External={0} Internal={1} Status={2}]", External, Internal, Status);
}


}

class NatDeviceState : Bolt.INatDevice {
public volatile INatDevice Nat;
public volatile IPAddress ExternalAddress;
public volatile Dictionary<int, NatPortMapping> PortMappings = new Dictionary<int, NatPortMapping>();

string Bolt.INatDevice.DeviceType {
get { lock (syncLock) { return Nat.DeviceType; } }
}

UdpIPv4Address Bolt.INatDevice.PublicAddress {
get { lock (syncLock) { return ExternalAddress == null ? UdpIPv4Address.Any : UdpIPv4Address.Parse(ExternalAddress.ToString()); } }
}

UdpIPv4Address Bolt.INatDevice.LocalAddress {
get { lock (syncLock) { return UdpIPv4Address.Parse(Nat.LocalAddress.ToString()); } }
}

IEnumerable<Bolt.IPortMapping> Bolt.INatDevice.Ports {
get { lock (syncLock) { return PortMappings.Values.Cast<Bolt.IPortMapping>().ToArray(); } }
}

public override string ToString() {
return string.Format("[NatDevice Local={0} Public={1}]", ((Bolt.INatDevice)this).LocalAddress, ((Bolt.INatDevice)this).PublicAddress);
}
}

class BoltLogTextWriter : TextWriter {
public override System.Text.Encoding Encoding {
get { return System.Text.Encoding.UTF8; }
}

public override void WriteLine(string value) {
BoltLog.Info("UPnP: " + value);
}

public override void WriteLine(string format, params object[] args) {
BoltLog.Info("UPnP: " + format, args);
}
}

static readonly object syncLock = new object();
static List<NatDeviceState> deviceList = new List<NatDeviceState>();
static Queue<NatPortMappingChanged> portChanges = new Queue<NatPortMappingChanged>();
static Dictionary<int, NatPortMapping> portList = new Dictionary<int, NatPortMapping>();

static StandaloneNatCommunicator() {
NatUtility.DeviceLost += NatUtility_DeviceLost;
NatUtility.DeviceFound += NatUtility_DeviceFound;
}

public override bool IsEnabled {
get { return NatUtility.IsEnabled; }
}

public override bool NextPortStatusChange(out Bolt.INatDevice device, out Bolt.IPortMapping mapping) {
lock (syncLock) {
if (portChanges.Count > 0) {
var change = portChanges.Dequeue();

device = change.Device;
mapping = change.Mapping;
return true;
}
else {
device = null;
mapping = null;
return false;
}
}
}

public override void OpenPort(int port) {
UpdatePort(Bolt.NatPortMappingStatus.Open, port);
}

public override void ClosePort(int port) {
UpdatePort(Bolt.NatPortMappingStatus.Closed, port);
}

void UpdatePort(Bolt.NatPortMappingStatus status, int port) {
if (port < 1 || port > ushort.MaxValue) {
throw new System.ArgumentOutOfRangeException("port");
}

lock (syncLock) {
NatPortMapping mapping = new NatPortMapping { Status = status, Internal = port, External = port };

if (portList.ContainsKey(port)) {
portList.Remove(port);
}

portList.Add(port, mapping);
}
}

public override IEnumerable<Bolt.INatDevice> NatDevices {
get { lock (syncLock) { return deviceList.Cast<Bolt.INatDevice>().ToArray(); } }
}

public override void Update() {
lock (syncLock) {
foreach (var mp in portList.Values) {
foreach (var dev in deviceList) {
NatPortMapping devMp;

if (dev.PortMappings.TryGetValue(mp.External, out devMp)) {
if (devMp.Status != mp.Status) {
switch (mp.Status) {
case Bolt.NatPortMappingStatus.Open: NatUtility_OpenPort(dev, devMp.External); break;
case Bolt.NatPortMappingStatus.Closed: NatUtility_ClosePort(dev, devMp.External); break;
}
}
}
else {
devMp = mp.Clone();
devMp.Status = Bolt.NatPortMappingStatus.Unknown;
dev.PortMappings.Add(devMp.External, devMp);
}
}
}
}
}

public override void Disable(bool async) {
lock (syncLock) {
portList = new Dictionary<int, NatPortMapping>();
deviceList = new List<NatDeviceState>();

NatUtility.ShutdownThread(async);

BoltLog.Info("UPnP Disabled");
}
}

public override void Enable() {
lock (syncLock) {
//NatUtility.Logger = new BoltLogTextWriter();
NatUtility.StartDiscovery();
BoltLog.Info("UPnP Enabled");
}
}

static void NatUtility_DeviceFound(object sender, DeviceEventArgs e) {
lock (syncLock) {
foreach (var device in deviceList) {
if (device.Equals(e.Device)) {
return;
}
}

NatDeviceState deviceState;
deviceState = new NatDeviceState { Nat = e.Device };
deviceState.PortMappings = new Dictionary<int, NatPortMapping>();
deviceList.Add(deviceState);

BoltLog.Info("Found {0}", deviceState);

NatUtility_FindPublicAddress(deviceState);
}
}

static void NatUtility_DeviceLost(object sender, DeviceEventArgs e) {
lock (syncLock) {
for (int i = 0; i < deviceList.Count; ++i) {
if (deviceList[i].Equals(e.Device)) {
deviceList.RemoveAt(i);
i -= 1;
}
}

BoltLog.Info("Lost NAT device at {0}", e.Device.LocalAddress);
}
}

static void NatUtility_FindPublicAddress(NatDeviceState device) {
lock (syncLock) {
device.Nat.BeginGetExternalIP(ar => {
lock (syncLock) {
try {
device.ExternalAddress = device.Nat.EndGetExternalIP(ar);
BoltLog.Info("Found external address of {0}", device);
}
catch (Exception exn) {
BoltLog.Exception(exn);
}
}
}, null);
}
}

static void NatUtility_OpenPort_Finish(NatDeviceState device, int port) {
try {
var natMapping = device.PortMappings.Values.FirstOrDefault(p => p.Internal == port && p.External == port);
if (natMapping != null) {
// set this port as open
natMapping.Status = Bolt.NatPortMappingStatus.Open;

// tell user about this
portChanges.Enqueue(new NatPortMappingChanged { Device = device, Mapping = natMapping.Clone() });

// meep
BoltLog.Info("Changed {0} on {1}", natMapping, device);
}
else {
BoltLog.Warn("Received incorrect port mapping result from {0}", device);
}
}
catch (Exception exn) {
BoltLog.Exception(exn);
}
}

static void NatUtility_OpenPort(NatDeviceState device, int port) {
lock (syncLock) {
Mapping mapping = new Mapping(Protocol.Udp, port, port);
device.Nat.BeginCreatePortMap(mapping, ar => {
lock (syncLock) {
try {
device.Nat.EndCreatePortMap(ar);

// finish this
NatUtility_OpenPort_Finish(device, port);
}
catch (MappingException exn) {
if (exn.ErrorCode == 718) {
NatUtility_OpenPort_Finish(device, port);
}
else {
BoltLog.Exception(exn);
}
}
catch (Exception exn) {
BoltLog.Exception(exn);
}
}
}, null);
}
}

static void ClosePortMapping(NatDeviceState device, int port) {
var natMapping = device.PortMappings.Values.FirstOrDefault(p => p.Internal == port && p.External == port);
if (natMapping != null) {
// set this port as open
natMapping.Status = Bolt.NatPortMappingStatus.Closed;

// tell user about this
portChanges.Enqueue(new NatPortMappingChanged { Device = device, Mapping = natMapping.Clone() });

// meep
BoltLog.Info("Changed {0} on {1}", natMapping, device);
}
else {
BoltLog.Warn("Received incorrect port mapping result from {0}", device);
}
}

static void NatUtility_ClosePort(NatDeviceState device, int port) {
lock (syncLock) {
Mapping mapping = new Mapping(Protocol.Udp, port, port);
device.Nat.BeginDeletePortMap(mapping, ar => {
lock (syncLock) {
try {
device.Nat.EndDeletePortMap(ar);
ClosePortMapping(device, port);
}
catch (MappingException exn) {
if (exn.ErrorCode == 714) {
ClosePortMapping(device, port);
}
else {
BoltLog.Exception(exn);
}
}
catch (Exception exn) {
BoltLog.Exception(exn);
}
}
}, null);
}
}

}
}
#endif