// Path: src/main/java/org/chris/portmapper/model/PortMapping.java
// public class PortMapping {
// 
//     public static final String MAPPING_ENTRY_LEASE_DURATION = "NewLeaseDuration";
//     public static final String MAPPING_ENTRY_ENABLED = "NewEnabled";
//     public static final String MAPPING_ENTRY_REMOTE_HOST = "NewRemoteHost";
//     public static final String MAPPING_ENTRY_INTERNAL_CLIENT = "NewInternalClient";
//     public static final String MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION = "NewPortMappingDescription";
//     public static final String MAPPING_ENTRY_PROTOCOL = "NewProtocol";
//     public static final String MAPPING_ENTRY_INTERNAL_PORT = "NewInternalPort";
//     public static final String MAPPING_ENTRY_EXTERNAL_PORT = "NewExternalPort";
// 
//     private static final long DEFAULT_LEASE_DURATION = 0;
// 
//     private final int externalPort;
//     private final Protocol protocol;
//     private final int internalPort;
//     private final String description;
//     private final String internalClient;
//     private final String remoteHost;
//     private final boolean enabled;
//     private final long leaseDuration;
// 
//     public PortMapping(final Protocol protocol, final String remoteHost, final int externalPort,
//             final String internalClient, final int internalPort, final String description) {
//         this(protocol, remoteHost, externalPort, internalClient, internalPort, description, true,
//                 DEFAULT_LEASE_DURATION);
//     }
// 
//     public PortMapping(final Protocol protocol, final String remoteHost, final int externalPort,
//             final String internalClient, final int internalPort, final String description, final boolean enabled,
//             final long leaseDuration) {
//         this.protocol = protocol;
//         this.remoteHost = remoteHost;
//         this.externalPort = externalPort;
//         this.internalClient = internalClient;
//         this.internalPort = internalPort;
//         this.description = description;
//         this.enabled = enabled;
//         this.leaseDuration = leaseDuration;
//     }
// 
//     private PortMapping(final ActionResponse response) {
//         final Map<String, String> values = new HashMap<>();
// 
//         for (final Object argObj : response.getOutActionArgumentNames()) {
//             final String argName = (String) argObj;
//             values.put(argName, response.getOutActionArgumentValue(argName));
//         }
// 
//         externalPort = Integer.parseInt(values.get(MAPPING_ENTRY_EXTERNAL_PORT));
//         internalPort = Integer.parseInt(values.get(MAPPING_ENTRY_INTERNAL_PORT));
//         final String protocolString = values.get(MAPPING_ENTRY_PROTOCOL);
//         protocol = (protocolString.equalsIgnoreCase("TCP") ? Protocol.TCP : Protocol.UDP);
//         description = values.get(MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION);
//         internalClient = values.get(MAPPING_ENTRY_INTERNAL_CLIENT);
//         remoteHost = values.get(MAPPING_ENTRY_REMOTE_HOST);
//         final String enabledString = values.get(MAPPING_ENTRY_ENABLED);
//         enabled = enabledString != null && enabledString.equals("1");
//         leaseDuration = Long.parseLong(values.get(MAPPING_ENTRY_LEASE_DURATION));
//     }
// 
//     public static PortMapping create(final ActionResponse response) {
//         return new PortMapping(response);
//     }
// 
//     /**
//      * @return the leaseDuration
//      */
//     public long getLeaseDuration() {
//         return leaseDuration;
//     }
// 
//     public int getExternalPort() {
//         return externalPort;
//     }
// 
//     public Protocol getProtocol() {
//         return protocol;
//     }
// 
//     public int getInternalPort() {
//         return internalPort;
//     }
// 
//     public String getDescription() {
//         return description;
//     }
// 
//     public String getInternalClient() {
//         return internalClient;
//     }
// 
//     public String getRemoteHost() {
//         return remoteHost;
//     }
// 
//     public boolean isEnabled() {
//         return enabled;
//     }
// 
//     public String getCompleteDescription() {
//         final StringBuilder b = new StringBuilder();
//         b.append(protocol);
//         b.append(" ");
//         if (remoteHost != null) {
//             b.append(remoteHost);
//         }
//         b.append(":");
//         b.append(externalPort);
//         b.append(" -> ");
//         b.append(internalClient);
//         b.append(":");
//         b.append(internalPort);
//         b.append(" ");
//         b.append(enabled ? "enabled" : "not enabled");
//         b.append(" ");
//         b.append(description);
//         return b.toString();
//     }
// 
//     @Override
//     public String toString() {
//         return description;
//     }
// }
// 
// Path: src/main/java/org/chris/portmapper/router/RouterException.java
// public class RouterException extends Exception {
// 
//     private static final long serialVersionUID = 1L;
// 
//     public RouterException(final String message, final Throwable cause) {
//         super(message, cause);
//     }
// 
//     public RouterException(final String message) {
//         super(message);
//     }
// }


import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import org.chris.portmapper.model.PortMapping;
import org.chris.portmapper.router.RouterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sbbi.upnp.impls.InternetGatewayDevice;
import net.sbbi.upnp.messages.ActionResponse;
import net.sbbi.upnp.messages.UPNPResponseException;
             * In order to speed this up, we will do the same here, but stop, when the first exception is thrown.
             */

            while (morePortMappingsAvailable()) {
                logger.debug("Getting port mapping with entry number {}...", currentMappingNumber);

                try {
                    final ActionResponse response = router.getGenericPortMappingEntry(currentMappingNumber);
                    addResponse(response);
                } catch (final UPNPResponseException e) {
                    handleUPNPResponseException(e);
                }

                currentMappingNumber++;
            }

            checkMaxNumPortMappingsReached();

        } catch (final IOException e) {
            throw new RouterException("Could not get NAT mappings: " + e.getMessage(), e);
        }

        logger.debug("Found {} mappings, {} mappings returned as null.", mappings.size(), nullPortMappings);
        return mappings;
    }

    /**
     * Check, if the max number of entries is reached and print a warning message.
     */
    private void checkMaxNumPortMappingsReached() {
// Path: src/main/java/org/chris/portmapper/model/PortMapping.java
// public class PortMapping {
// 
//     public static final String MAPPING_ENTRY_LEASE_DURATION = "NewLeaseDuration";
//     public static final String MAPPING_ENTRY_ENABLED = "NewEnabled";
//     public static final String MAPPING_ENTRY_REMOTE_HOST = "NewRemoteHost";
//     public static final String MAPPING_ENTRY_INTERNAL_CLIENT = "NewInternalClient";
//     public static final String MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION = "NewPortMappingDescription";
//     public static final String MAPPING_ENTRY_PROTOCOL = "NewProtocol";
//     public static final String MAPPING_ENTRY_INTERNAL_PORT = "NewInternalPort";
//     public static final String MAPPING_ENTRY_EXTERNAL_PORT = "NewExternalPort";
// 
//     private static final long DEFAULT_LEASE_DURATION = 0;
// 
//     private final int externalPort;
//     private final Protocol protocol;
//     private final int internalPort;
//     private final String description;
//     private final String internalClient;
//     private final String remoteHost;
//     private final boolean enabled;
//     private final long leaseDuration;
// 
//     public PortMapping(final Protocol protocol, final String remoteHost, final int externalPort,
//             final String internalClient, final int internalPort, final String description) {
//         this(protocol, remoteHost, externalPort, internalClient, internalPort, description, true,
//                 DEFAULT_LEASE_DURATION);
//     }
// 
//     public PortMapping(final Protocol protocol, final String remoteHost, final int externalPort,
//             final String internalClient, final int internalPort, final String description, final boolean enabled,
//             final long leaseDuration) {
//         this.protocol = protocol;
//         this.remoteHost = remoteHost;
//         this.externalPort = externalPort;
//         this.internalClient = internalClient;
//         this.internalPort = internalPort;
//         this.description = description;
//         this.enabled = enabled;
//         this.leaseDuration = leaseDuration;
//     }
// 
//     private PortMapping(final ActionResponse response) {
//         final Map<String, String> values = new HashMap<>();
// 
//         for (final Object argObj : response.getOutActionArgumentNames()) {
//             final String argName = (String) argObj;
//             values.put(argName, response.getOutActionArgumentValue(argName));
//         }
// 
//         externalPort = Integer.parseInt(values.get(MAPPING_ENTRY_EXTERNAL_PORT));
//         internalPort = Integer.parseInt(values.get(MAPPING_ENTRY_INTERNAL_PORT));
//         final String protocolString = values.get(MAPPING_ENTRY_PROTOCOL);
//         protocol = (protocolString.equalsIgnoreCase("TCP") ? Protocol.TCP : Protocol.UDP);
//         description = values.get(MAPPING_ENTRY_PORT_MAPPING_DESCRIPTION);
//         internalClient = values.get(MAPPING_ENTRY_INTERNAL_CLIENT);
//         remoteHost = values.get(MAPPING_ENTRY_REMOTE_HOST);
//         final String enabledString = values.get(MAPPING_ENTRY_ENABLED);
//         enabled = enabledString != null && enabledString.equals("1");
//         leaseDuration = Long.parseLong(values.get(MAPPING_ENTRY_LEASE_DURATION));
//     }
// 
//     public static PortMapping create(final ActionResponse response) {
//         return new PortMapping(response);
//     }
// 
//     /**
//      * @return the leaseDuration
//      */
//     public long getLeaseDuration() {
//         return leaseDuration;
//     }
// 
//     public int getExternalPort() {
//         return externalPort;
//     }
// 
//     public Protocol getProtocol() {
//         return protocol;
//     }
// 
//     public int getInternalPort() {
//         return internalPort;
//     }
// 
//     public String getDescription() {
//         return description;
//     }
// 
//     public String getInternalClient() {
//         return internalClient;
//     }
// 
//     public String getRemoteHost() {
//         return remoteHost;
//     }
// 
//     public boolean isEnabled() {
//         return enabled;
//     }
// 
//     public String getCompleteDescription() {
//         final StringBuilder b = new StringBuilder();
//         b.append(protocol);
//         b.append(" ");
//         if (remoteHost != null) {
//             b.append(remoteHost);
//         }
//         b.append(":");
//         b.append(externalPort);
//         b.append(" -> ");
//         b.append(internalClient);
//         b.append(":");
//         b.append(internalPort);
//         b.append(" ");
//         b.append(enabled ? "enabled" : "not enabled");
//         b.append(" ");
//         b.append(description);
//         return b.toString();
//     }
// 
//     @Override
//     public String toString() {
//         return description;
//     }
// }
// 
// Path: src/main/java/org/chris/portmapper/router/RouterException.java
// public class RouterException extends Exception {
// 
//     private static final long serialVersionUID = 1L;
// 
//     public RouterException(final String message, final Throwable cause) {
//         super(message, cause);
//     }
// 
//     public RouterException(final String message) {
//         super(message);
//     }
// }


// Path: src/main/java/org/chris/portmapper/router/sbbi/SBBIPortMappingExtractor.java
import java.io.IOException;
import java.util.Collection;
import java.util.LinkedList;
import org.chris.portmapper.model.PortMapping;
import org.chris.portmapper.router.RouterException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import net.sbbi.upnp.impls.InternetGatewayDevice;
import net.sbbi.upnp.messages.ActionResponse;
import net.sbbi.upnp.messages.UPNPResponseException;

             * In order to speed this up, we will do the same here, but stop, when the first exception is thrown.
             */

            while (morePortMappingsAvailable()) {
                logger.debug("Getting port mapping with entry number {}...", currentMappingNumber);

                try {
                    final ActionResponse response = router.getGenericPortMappingEntry(currentMappingNumber);
                    addResponse(response);
                } catch (final UPNPResponseException e) {
                    handleUPNPResponseException(e);
                }

                currentMappingNumber++;
            }

            checkMaxNumPortMappingsReached();

        } catch (final IOException e) {
            throw new RouterException("Could not get NAT mappings: " + e.getMessage(), e);
        }

        logger.debug("Found {} mappings, {} mappings returned as null.", mappings.size(), nullPortMappings);
        return mappings;
    }

    /**
     * Check, if the max number of entries is reached and print a warning message.
     */
    private void checkMaxNumPortMappingsReached() {
        if (currentMappingNumber == maxNumPortMappings) {