|
|
@ -32,6 +32,8 @@ using System.Net;
|
|
|
|
using System.Xml;
|
|
|
|
using System.Xml;
|
|
|
|
using System.Text;
|
|
|
|
using System.Text;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
|
|
using MediaBrowser.Common.Net;
|
|
|
|
using MediaBrowser.Controller.Dlna;
|
|
|
|
using MediaBrowser.Controller.Dlna;
|
|
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
using MediaBrowser.Model.Logging;
|
|
|
|
|
|
|
|
|
|
|
@ -45,18 +47,14 @@ namespace Mono.Nat.Upnp
|
|
|
|
private string controlUrl;
|
|
|
|
private string controlUrl;
|
|
|
|
private string serviceType;
|
|
|
|
private string serviceType;
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
|
|
private readonly IHttpClient _httpClient;
|
|
|
|
|
|
|
|
|
|
|
|
public override IPAddress LocalAddress
|
|
|
|
public override IPAddress LocalAddress
|
|
|
|
{
|
|
|
|
{
|
|
|
|
get { return localAddress; }
|
|
|
|
get { return localAddress; }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType, ILogger logger, IHttpClient httpClient)
|
|
|
|
/// The callback to invoke when we are finished setting up the device
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
private NatDeviceCallback callback;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType, ILogger logger)
|
|
|
|
|
|
|
|
{
|
|
|
|
{
|
|
|
|
this.LastSeen = DateTime.Now;
|
|
|
|
this.LastSeen = DateTime.Now;
|
|
|
|
this.localAddress = localAddress;
|
|
|
|
this.localAddress = localAddress;
|
|
|
@ -65,6 +63,7 @@ namespace Mono.Nat.Upnp
|
|
|
|
string locationDetails = deviceInfo.Location.ToString();
|
|
|
|
string locationDetails = deviceInfo.Location.ToString();
|
|
|
|
this.serviceType = serviceType;
|
|
|
|
this.serviceType = serviceType;
|
|
|
|
_logger = logger;
|
|
|
|
_logger = logger;
|
|
|
|
|
|
|
|
_httpClient = httpClient;
|
|
|
|
|
|
|
|
|
|
|
|
// Make sure we have no excess whitespace
|
|
|
|
// Make sure we have no excess whitespace
|
|
|
|
locationDetails = locationDetails.Trim();
|
|
|
|
locationDetails = locationDetails.Trim();
|
|
|
@ -91,9 +90,10 @@ namespace Mono.Nat.Upnp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal UpnpNatDevice (IPAddress localAddress, string deviceDetails, string serviceType, ILogger logger)
|
|
|
|
internal UpnpNatDevice(IPAddress localAddress, string deviceDetails, string serviceType, ILogger logger, IHttpClient httpClient)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_logger = logger;
|
|
|
|
_logger = logger;
|
|
|
|
|
|
|
|
_httpClient = httpClient;
|
|
|
|
this.LastSeen = DateTime.Now;
|
|
|
|
this.LastSeen = DateTime.Now;
|
|
|
|
this.localAddress = localAddress;
|
|
|
|
this.localAddress = localAddress;
|
|
|
|
|
|
|
|
|
|
|
@ -190,6 +190,12 @@ namespace Mono.Nat.Upnp
|
|
|
|
return BeginMessageInternal(message, callback, asyncState, EndCreatePortMapInternal);
|
|
|
|
return BeginMessageInternal(message, callback, asyncState, EndCreatePortMapInternal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override Task CreatePortMap(Mapping mapping)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
|
|
|
|
|
|
|
|
return _httpClient.SendAsync(message.Encode(), message.Method);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Removes a port mapping from this computer
|
|
|
|
/// Removes a port mapping from this computer
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
@ -207,7 +213,7 @@ namespace Mono.Nat.Upnp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public override IAsyncResult BeginGetSpecificMapping (Protocol protocol, int port, AsyncCallback callback, object asyncState)
|
|
|
|
public override IAsyncResult BeginGetSpecificMapping(Protocol protocol, int port, AsyncCallback callback, object asyncState)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
GetSpecificPortMappingEntryMessage message = new GetSpecificPortMappingEntryMessage(protocol, port, this);
|
|
|
|
GetSpecificPortMappingEntryMessage message = new GetSpecificPortMappingEntryMessage(protocol, port, this);
|
|
|
|
return this.BeginMessageInternal(message, callback, asyncState, new AsyncCallback(this.EndGetSpecificMappingInternal));
|
|
|
|
return this.BeginMessageInternal(message, callback, asyncState, new AsyncCallback(this.EndGetSpecificMappingInternal));
|
|
|
@ -342,7 +348,7 @@ namespace Mono.Nat.Upnp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (mappingResult.Mappings.Count == 0)
|
|
|
|
if (mappingResult.Mappings.Count == 0)
|
|
|
|
return new Mapping (Protocol.Tcp, -1, -1);
|
|
|
|
return new Mapping(Protocol.Tcp, -1, -1);
|
|
|
|
|
|
|
|
|
|
|
|
return mappingResult.Mappings[0];
|
|
|
|
return mappingResult.Mappings[0];
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -376,7 +382,8 @@ namespace Mono.Nat.Upnp
|
|
|
|
if (body.Length > 0)
|
|
|
|
if (body.Length > 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
request.ContentLength = body.Length;
|
|
|
|
request.ContentLength = body.Length;
|
|
|
|
request.BeginGetRequestStream(delegate(IAsyncResult result) {
|
|
|
|
request.BeginGetRequestStream(delegate (IAsyncResult result)
|
|
|
|
|
|
|
|
{
|
|
|
|
try
|
|
|
|
try
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Stream s = request.EndGetRequestStream(result);
|
|
|
|
Stream s = request.EndGetRequestStream(result);
|
|
|
@ -480,7 +487,7 @@ namespace Mono.Nat.Upnp
|
|
|
|
GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
|
|
|
|
GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
|
|
|
|
if (message != null)
|
|
|
|
if (message != null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Mapping mapping = new Mapping (message.Protocol, message.InternalPort, message.ExternalPort, message.LeaseDuration);
|
|
|
|
Mapping mapping = new Mapping(message.Protocol, message.InternalPort, message.ExternalPort, message.LeaseDuration);
|
|
|
|
mapping.Description = message.PortMappingDescription;
|
|
|
|
mapping.Description = message.PortMappingDescription;
|
|
|
|
mappingResult.Mappings.Add(mapping);
|
|
|
|
mappingResult.Mappings.Add(mapping);
|
|
|
|
GetGenericPortMappingEntry next = new GetGenericPortMappingEntry(mappingResult.Mappings.Count, this);
|
|
|
|
GetGenericPortMappingEntry next = new GetGenericPortMappingEntry(mappingResult.Mappings.Count, this);
|
|
|
@ -514,7 +521,8 @@ namespace Mono.Nat.Upnp
|
|
|
|
|
|
|
|
|
|
|
|
GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
|
|
|
|
GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
|
|
|
|
GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
|
|
|
|
GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
|
|
|
|
if (message != null) {
|
|
|
|
if (message != null)
|
|
|
|
|
|
|
|
{
|
|
|
|
Mapping mapping = new Mapping(mappingResult.SpecificMapping.Protocol, message.InternalPort, mappingResult.SpecificMapping.PublicPort, message.LeaseDuration);
|
|
|
|
Mapping mapping = new Mapping(mappingResult.SpecificMapping.Protocol, message.InternalPort, mappingResult.SpecificMapping.PublicPort, message.LeaseDuration);
|
|
|
|
mapping.Description = mappingResult.SpecificMapping.Description;
|
|
|
|
mapping.Description = mappingResult.SpecificMapping.Description;
|
|
|
|
mappingResult.Mappings.Add(mapping);
|
|
|
|
mappingResult.Mappings.Add(mapping);
|
|
|
@ -523,37 +531,26 @@ namespace Mono.Nat.Upnp
|
|
|
|
CompleteMessage(result);
|
|
|
|
CompleteMessage(result);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal void GetServicesList(NatDeviceCallback callback)
|
|
|
|
internal async Task<bool> GetServicesList()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Save the callback so i can use it again later when i've finished parsing the services available
|
|
|
|
|
|
|
|
this.callback = callback;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Create a HTTPWebRequest to download the list of services the device offers
|
|
|
|
// Create a HTTPWebRequest to download the list of services the device offers
|
|
|
|
byte[] body;
|
|
|
|
var requestOptions = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger).Encode();
|
|
|
|
WebRequest request = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger).Encode(out body);
|
|
|
|
|
|
|
|
if (body.Length > 0)
|
|
|
|
|
|
|
|
NatUtility.Log("Error: Services Message contained a body");
|
|
|
|
|
|
|
|
request.BeginGetResponse(this.ServicesReceived, request);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void ServicesReceived(IAsyncResult result)
|
|
|
|
requestOptions.BufferContent = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
using (var response = await _httpClient.Get(requestOptions).ConfigureAwait(false))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
HttpWebResponse response = null;
|
|
|
|
return ServicesReceived(response);
|
|
|
|
try
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private bool ServicesReceived(Stream s)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
int abortCount = 0;
|
|
|
|
int abortCount = 0;
|
|
|
|
int bytesRead = 0;
|
|
|
|
int bytesRead = 0;
|
|
|
|
byte[] buffer = new byte[10240];
|
|
|
|
byte[] buffer = new byte[10240];
|
|
|
|
StringBuilder servicesXml = new StringBuilder();
|
|
|
|
StringBuilder servicesXml = new StringBuilder();
|
|
|
|
XmlDocument xmldoc = new XmlDocument();
|
|
|
|
XmlDocument xmldoc = new XmlDocument();
|
|
|
|
HttpWebRequest request = result.AsyncState as HttpWebRequest;
|
|
|
|
|
|
|
|
response = request.EndGetResponse(result) as HttpWebResponse;
|
|
|
|
|
|
|
|
Stream s = response.GetResponseStream();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (response.StatusCode != HttpStatusCode.OK) {
|
|
|
|
|
|
|
|
NatUtility.Log("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
|
|
|
|
|
|
|
|
return; // FIXME: This the best thing to do??
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
while (true)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -562,7 +559,6 @@ namespace Mono.Nat.Upnp
|
|
|
|
try
|
|
|
|
try
|
|
|
|
{
|
|
|
|
{
|
|
|
|
xmldoc.LoadXml(servicesXml.ToString());
|
|
|
|
xmldoc.LoadXml(servicesXml.ToString());
|
|
|
|
response.Close();
|
|
|
|
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (XmlException)
|
|
|
|
catch (XmlException)
|
|
|
@ -573,8 +569,7 @@ namespace Mono.Nat.Upnp
|
|
|
|
// parsed by the xmldoc. Without this, the code will never pick up my router.
|
|
|
|
// parsed by the xmldoc. Without this, the code will never pick up my router.
|
|
|
|
if (abortCount++ > 50)
|
|
|
|
if (abortCount++ > 50)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
response.Close();
|
|
|
|
return false;
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
|
|
|
|
NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
|
|
|
|
System.Threading.Thread.Sleep(10);
|
|
|
|
System.Threading.Thread.Sleep(10);
|
|
|
@ -618,32 +613,21 @@ namespace Mono.Nat.Upnp
|
|
|
|
NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
|
|
|
|
NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
|
|
|
|
NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
|
|
|
|
this.callback(this);
|
|
|
|
return true;
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
|
|
|
|
//If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
|
|
|
|
//So we don't invoke the callback, so this device is never added to our lists
|
|
|
|
//So we don't invoke the callback, so this device is never added to our lists
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
catch (WebException ex)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// Just drop the connection, FIXME: Should i retry?
|
|
|
|
|
|
|
|
NatUtility.Log("{0}: Device denied the connection attempt: {1}", HostEndPoint, ex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
finally
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (response != null)
|
|
|
|
|
|
|
|
response.Close();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Overridden.
|
|
|
|
/// Overridden.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <returns></returns>
|
|
|
|
/// <returns></returns>
|
|
|
|
public override string ToString( )
|
|
|
|
public override string ToString()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
//GetExternalIP is blocking and can throw exceptions, can't use it here.
|
|
|
|
//GetExternalIP is blocking and can throw exceptions, can't use it here.
|
|
|
|
return String.Format(
|
|
|
|
return String.Format(
|
|
|
|