using System; using System.IO; using System.Xml; using System.Net; using System.Net.Sockets; namespace Lidgren.Network { /// /// UPnP support class /// public class NetUPnP { private string m_serviceUrl; private NetPeer m_peer; /// /// NetUPnP constructor /// public NetUPnP(NetPeer peer) { m_peer = peer; } internal void Discover(NetPeer peer) { string str = "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "MAN:\"ssdp:discover\"\r\n" + "MX:3\r\n\r\n"; byte[] arr = System.Text.Encoding.ASCII.GetBytes(str); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true); peer.RawSend(arr, 0, arr.Length, new IPEndPoint(IPAddress.Broadcast, 1900)); peer.Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, false); // allow some extra time for router to respond System.Threading.Thread.Sleep(50); } internal void ExtractServiceUrl(string resp) { #if !DEBUG try { #endif XmlDocument desc = new XmlDocument(); desc.Load(WebRequest.Create(resp).GetResponse().GetResponseStream()); XmlNamespaceManager nsMgr = new XmlNamespaceManager(desc.NameTable); nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); XmlNode typen = desc.SelectSingleNode("//tns:device/tns:deviceType/text()", nsMgr); if (!typen.Value.Contains("InternetGatewayDevice")) return; XmlNode node = desc.SelectSingleNode("//tns:service[tns:serviceType=\"urn:schemas-upnp-org:service:WANIPConnection:1\"]/tns:controlURL/text()", nsMgr); if (node == null) return; m_serviceUrl = CombineUrls(resp, node.Value); m_peer.LogDebug("UPnP service ready"); System.Threading.Thread.Sleep(50); #if !DEBUG } catch { return; } #endif } private static string CombineUrls(string gatewayURL, string subURL) { // Is Control URL an absolute URL? if ((subURL.Contains("http:")) || (subURL.Contains("."))) return subURL; gatewayURL = gatewayURL.Replace("http://", ""); // strip any protocol int n = gatewayURL.IndexOf("/"); if (n != -1) gatewayURL = gatewayURL.Substring(0, n); // Use first portion of URL return "http://" + gatewayURL + subURL; } /// /// Add a forwarding rule to the router using UPnP /// public bool ForwardPort(int port, string description) { if (m_serviceUrl == null) return false; IPAddress mask; var client = NetUtility.GetMyAddress(out mask); try { XmlDocument xdoc = SOAPRequest(m_serviceUrl, "" + "" + port.ToString() + "" + "" + ProtocolType.Udp.ToString().ToUpper() + "" + "" + port.ToString() + "" + "" + client.ToString() + "" + "1" + "" + description + "" + "0" + "", "AddPortMapping"); m_peer.LogDebug("Sent UPnP port forward request"); System.Threading.Thread.Sleep(50); } catch (Exception ex) { m_peer.LogWarning("UPnP port forward failed: " + ex.Message); return false; } return true; } /// /// Delete a forwarding rule from the router using UPnP /// public bool DeleteForwardingRule(int port) { if (m_serviceUrl == null) return false; try { XmlDocument xdoc = SOAPRequest(m_serviceUrl, "" + "" + "" + "" + port + "" + "" + ProtocolType.Udp.ToString().ToUpper() + "" + "", "DeletePortMapping"); return true; } catch (Exception ex) { m_peer.LogWarning("UPnP delete forwarding rule failed: " + ex.Message); return false; } } /// /// Retrieve the extern ip using UPnP /// public IPAddress GetExternalIP() { if (m_serviceUrl == null) return null; try { XmlDocument xdoc = SOAPRequest(m_serviceUrl, "" + "", "GetExternalIPAddress"); XmlNamespaceManager nsMgr = new XmlNamespaceManager(xdoc.NameTable); nsMgr.AddNamespace("tns", "urn:schemas-upnp-org:device-1-0"); string IP = xdoc.SelectSingleNode("//NewExternalIPAddress/text()", nsMgr).Value; return IPAddress.Parse(IP); } catch (Exception ex) { m_peer.LogWarning("Failed to get external IP: " + ex.Message); return null; } } private XmlDocument SOAPRequest(string url, string soap, string function) { string req = "" + "" + "" + soap + "" + ""; WebRequest r = HttpWebRequest.Create(url); r.Method = "POST"; byte[] b = System.Text.Encoding.UTF8.GetBytes(req); r.Headers.Add("SOAPACTION", "\"urn:schemas-upnp-org:service:WANIPConnection:1#" + function + "\""); r.ContentType = "text/xml; charset=\"utf-8\""; r.ContentLength = b.Length; r.GetRequestStream().Write(b, 0, b.Length); XmlDocument resp = new XmlDocument(); WebResponse wres = r.GetResponse(); Stream ress = wres.GetResponseStream(); resp.Load(ress); return resp; } } }