///////////////////////////////////////////////////////////////////////////////////////////////////////////////
        public void AddPortMapping(PortMapping pm, String description)
        {
            mappingsListView.BeginUpdate();
            {
                ListViewItem lvi = new ListViewItem(description);
                lvi.SubItems.Add(pm.LocalPort.ToString());
                lvi.SubItems.Add(pm.DesiredExternalPort.ToString());
                lvi.SubItems.Add("");
                lvi.SubItems.Add(pm.ExternalPort.ToString());
                lvi.Tag = pm;

                mappings.Add(lvi);
                mappingsListView.VirtualListSize = mappings.Count;
            }
            mappingsListView.EndUpdate();

            PortMapper.SharedInstance.AddPortMapping(pm);
        }
 internal virtual void OnDidChangeMappingStatus(PortMapping pm)
 {
     // It is vitally important that we Invoke this on a background thread!
     // The mapping status is generally changed within the context of a lock on the mappings list.
     // This lock is also used within public API methods,
     // so if we don't use a background thread, there's a potential for deadlock.
     Thread bgThread = new Thread(new ParameterizedThreadStart(OnDidChangeMappingStatusThread));
     bgThread.IsBackground = true;
     bgThread.Start(pm);
 }
        private void PortMapper_DidChangeMappingStatus(PortMapper sender, PortMapping pm)
        {
            DebugLog.WriteLine("Form1: PortMapper_DidChangeMappingStatus");

            foreach (ListViewItem lvi in mappings)
            {
                PortMapping lvi_pm = (PortMapping)lvi.Tag;
                if (lvi_pm == pm)
                {
                    mappingsListView.RedrawItems(lvi.Index, lvi.Index, false);
                }
            }
        }
        private void okButton_Click(object sender, EventArgs e)
        {
            UInt16 localPort;
            UInt16.TryParse(localPortTextBox.Text, out localPort);

            UInt16 publicPort;
            UInt16.TryParse(publicPortTextBox.Text, out publicPort);

            PortMappingTransportProtocol protocol;
            if (tcpCheckBox.Checked)
            {
                if (udpCheckBox.Checked)
                    protocol = PortMappingTransportProtocol.Both;
                else
                    protocol = PortMappingTransportProtocol.TCP;
            }
            else
            {
                protocol = PortMappingTransportProtocol.UDP;
            }

            PortMapping pm = new PortMapping(localPort, publicPort, protocol);

            String description;
            if (descriptionTextBox.ForeColor == Color.Gray)
                description = "";
            else
                description = descriptionTextBox.Text;

            WindowManager.GetMainForm().AddPortMapping(pm, description);

            ClearForm();
            Close();
        }
        /// <summary>
        /// Asynchronously removes the given port mapping.
        /// </summary>
        /// <param name="pm">
        ///		The port mapping to remove.
        /// </param>
        public void RemovePortMapping(PortMapping pm)
        {
            if(pm == null) return;

            // All public API methods are wrapped in a single thread lock.
            // This frees users to invoke the public API from multiple threads, but provides us a bit of sanity.
            lock (singleThreadLock)
            {
                lock (portMappings)
                {
                    portMappings.Remove(pm);
                }
                lock (portMappingsToRemove)
                {
                    if (pm.MappingStatus != PortMappingStatus.Unmapped)
                    {
                        portMappingsToRemove.Add(pm);
                    }
                }
                if (isRunning) UpdatePortMappings();
            }
        }
        private bool RemovePortMapping(PortMapping portMapping)
        {
            // Make sure the mapping still belongs to us
            IPAddress ourIP = PortMapper.SharedInstance.LocalIPAddress;
            String ourDescription = GetPortMappingDescription();

            bool udpMappingStolen = false;
            bool tcpMappingStolen = false;

            if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0)
            {
                ExistingUPnPPortMapping existingPM;
                if (existingUPnPPortMappingsUdpDict.TryGetValue(portMapping.ExternalPort, out existingPM))
                {
                    if (!existingPM.LocalAddress.Equals(ourIP) || !existingPM.Description.Equals(ourDescription))
                    {
                        // The mapping was stolen by another machine or process
                        // Do not remove it, but for our purposes we can consider it removed

                        DebugLog.WriteLine("UPnP: RemovePortMapping: UDP mapping stolen");
                        udpMappingStolen = true;
                    }
                }
            }
            if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0)
            {
                ExistingUPnPPortMapping existingPM;
                if (existingUPnPPortMappingsTcpDict.TryGetValue(portMapping.ExternalPort, out existingPM))
                {
                    if (!existingPM.LocalAddress.Equals(ourIP) || !existingPM.Description.Equals(ourDescription))
                    {
                        // The mapping was stolen by another machine or process
                        // Do not remove it, but for our purposes we can consider it removed

                        DebugLog.WriteLine("UPnP: RemovePortMapping: TCM mapping stolen");
                        tcpMappingStolen = true;
                    }
                }
            }

            int result = MiniUPnP.UPNPCOMMAND_SUCCESS;

            bool udpResult = true;
            bool tcpResult = true;

            String extPortStr = portMapping.ExternalPort.ToString();

            if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0 && !udpMappingStolen)
            {
                try
                {
                    result = MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "UDP");
                }
                catch(AccessViolationException)
                {
                    // I have no idea why the above method sometimes throws an AccessException.
                    // The odd part about it is that it works perfect, except for the stupid exception.
                    // So the exception can safely be ignored, it just bugs me because it feels like a hack.
                    DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping");
                }

                DebugLog.WriteLine("UPnP: RemovePortMapping: UDP: result = {0}", result);
                udpResult = (result == MiniUPnP.UPNPCOMMAND_SUCCESS);
            }
            if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0 && !tcpMappingStolen)
            {
                try
                {
                    result = MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "TCP");
                }
                catch(AccessViolationException)
                {
                    // I have no idea why the above method sometimes throws an AccessException.
                    // The odd part about it is that it works perfect, except for the stupid exception.
                    // So the exception can safely be ignored, it just bugs me because it feels like a hack.
                    DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping");
                }

                DebugLog.WriteLine("UPnP: RemovePortMapping: TCP: result = {0}", result);
                tcpResult = (result == MiniUPnP.UPNPCOMMAND_SUCCESS);
            }

            portMapping.SetMappingStatus(PortMappingStatus.Unmapped);

            return (udpResult && tcpResult);
        }
        private bool AddPortMapping(PortMapping portMapping)
        {
            portMapping.SetMappingStatus(PortMappingStatus.Trying);

            String intPortStr = portMapping.LocalPort.ToString();
            String intClient = PortMapper.SharedInstance.LocalIPAddress.ToString();
            String description = GetPortMappingDescription();

            bool done = false;
            int attemptCount = 0;
            do
            {
                int udpErrCode = 0;
                int tcpErrCode = 0;

                bool udpResult = true;
                bool tcpResult = true;

                UInt16 extPort;
                if(portMapping.DesiredExternalPort < (65535 - 40))
                    extPort = (UInt16)(portMapping.DesiredExternalPort + attemptCount);
                else
                    extPort = (UInt16)(portMapping.DesiredExternalPort - attemptCount);

                String extPortStr = extPort.ToString();

                if ((portMapping.TransportProtocol & PortMappingTransportProtocol.UDP) > 0)
                {
                    ExistingUPnPPortMapping existingPM;
                    if (existingUPnPPortMappingsUdpDict.TryGetValue(extPort, out existingPM))
                    {
                        udpErrCode = 718;
                        DebugLog.WriteLine("UPnP: AddPortMapping: UDP: mapping already exists");
                    }
                    else
                    {
                        udpErrCode = MiniUPnP.UPNP_AddPortMapping(urls.controlURL, igddata.ServiceType,
                                                                  extPortStr, intPortStr, intClient, description, "UDP");
                        DebugLog.WriteLine("UPnP: AddPortMapping: UDP: result = {0}", udpErrCode);
                    }

                    udpResult = (udpErrCode == MiniUPnP.UPNPCOMMAND_SUCCESS);
                }
                if ((portMapping.TransportProtocol & PortMappingTransportProtocol.TCP) > 0)
                {
                    ExistingUPnPPortMapping existingPM;
                    if (existingUPnPPortMappingsTcpDict.TryGetValue(extPort, out existingPM))
                    {
                        tcpErrCode = 718;
                        DebugLog.WriteLine("UPnP: AddPortMapping: TCP: mapping already exists");
                    }
                    else
                    {
                        tcpErrCode = MiniUPnP.UPNP_AddPortMapping(urls.controlURL, igddata.ServiceType,
                                                                  extPortStr, intPortStr, intClient, description, "TCP");
                        DebugLog.WriteLine("UPnP: AddPortMapping: TCP: result = {0}", tcpErrCode);
                    }

                    tcpResult = (tcpErrCode == MiniUPnP.UPNPCOMMAND_SUCCESS);
                }

                if (udpResult && !tcpResult)
                {
                    DebugLog.WriteLine("Deleting UDP mapping");
                    try
                    {
                        MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "UDP");
                    }
                    catch(AccessViolationException)
                    {
                        // I have no idea why the above method sometimes throws an AccessException.
                        // The odd part about it is that it works perfect, except for the stupid exception.
                        // So the exception can safely be ignored, it just bugs me because it feels like a hack.
                        DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping");
                    }
                }
                if (tcpResult && !udpResult)
                {
                    DebugLog.WriteLine("Deleting TCP mapping");
                    try
                    {
                        MiniUPnP.UPNP_DeletePortMapping(urls.controlURL, igddata.ServiceType, extPortStr, "TCP");
                    }
                    catch(AccessViolationException)
                    {
                        // I have no idea why the above method sometimes throws an AccessException.
                        // The odd part about it is that it works perfect, except for the stupid exception.
                        // So the exception can safely be ignored, it just bugs me because it feels like a hack.
                        DebugLog.WriteLine("Ignoring exception from method MiniUPnP.UPNP_DeletePortMapping");
                    }
                }

                if (udpResult && tcpResult)
                {
                    // All attempted port mappings were successful
                    portMapping.SetExternalPort(extPort);
                    portMapping.SetMappingStatus(PortMappingStatus.Mapped);
                    return true;
                }

                attemptCount++;
                if(attemptCount >= 10)
                {
                    // We've tried 10 different mappings and still no success
                    done = true;
                }
                else if (!udpResult && udpErrCode != 718)
                {
                    // We received non-conflict error
                    done = true;
                }
                else if (!tcpResult && tcpErrCode != 718)
                {
                    // We received non-conflict error
                    done = true;
                }

            } while (!done);

            portMapping.SetMappingStatus(PortMappingStatus.Unmapped);
            return false;
        }
 private bool RemovePortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp)
 {
     return ApplyPortMapping(portMapping, true, ref natpmp);
 }
 private bool RefreshPortMapping(PortMapping portMapping, ref NATPMP.natpmp_t natpmp)
 {
     return ApplyPortMapping(portMapping, false, ref natpmp);
 }
        private bool ApplyPortMapping(PortMapping portMapping, bool remove, ref NATPMP.natpmp_t natpmp)
        {
            NATPMP.natpmpresp_t response = new NATPMP.natpmpresp_t();
            int r;
            Win32.TimeValue timeout = new Win32.TimeValue();
            Win32.FileDescriptorSet fds = new Win32.FileDescriptorSet(1);

            if (!remove)
            {
                portMapping.SetMappingStatus(PortMappingStatus.Trying);
            }
            PortMappingTransportProtocol protocol = portMapping.TransportProtocol;

            for (int i = 1; i <= 2; i++)
            {
                PortMappingTransportProtocol currentProtocol;
                if(i == 1)
                    currentProtocol = PortMappingTransportProtocol.UDP;
                else
                    currentProtocol = PortMappingTransportProtocol.TCP;

                if (protocol == currentProtocol || protocol == PortMappingTransportProtocol.Both)
                {
                    r = NATPMP.sendnewportmappingrequest(ref natpmp,
                        (i == 1) ? NATPMP.PROTOCOL_UDP : NATPMP.PROTOCOL_TCP,
                        portMapping.LocalPort, portMapping.DesiredExternalPort, (uint)(remove ? 0 : 3600));

                    do
                    {
                        fds.Count = 1;
                        fds.Array[0] = (IntPtr)natpmp.s;
                        NATPMP.getnatpmprequesttimeout(ref natpmp, ref timeout);

                        Win32.select(0, ref fds, IntPtr.Zero, IntPtr.Zero, ref timeout);

                        r = NATPMP.readnatpmpresponseorretry(ref natpmp, ref response);
                    }
                    while(r == NATPMP.ERR_TRYAGAIN);

                    if (r < 0)
                    {
                        portMapping.SetMappingStatus(PortMappingStatus.Unmapped);
                        return false;
                    }
                }
            }

            if (remove)
            {
                portMapping.SetMappingStatus(PortMappingStatus.Unmapped);
            }
            else
            {
                updateInterval = Math.Min(updateInterval, response.pnu_newportmapping.lifetime / 2);
                if (updateInterval < 60)
                {
                    DebugLog.WriteLine("NAT-PMP: ApplyPortMapping: Caution - new port mapping had a lifetime < 120 ({0})",
                        response.pnu_newportmapping.lifetime);

                    updateInterval = 60;
                }
                portMapping.SetExternalPort(response.pnu_newportmapping.mappedpublicport);
                portMapping.SetMappingStatus(PortMappingStatus.Mapped);
            }

            return true;
        }