diff --git a/src/upnp.c b/src/upnp.c
index a21f9cf..f508ade 100644
--- a/src/upnp.c
+++ b/src/upnp.c
@@ -168,6 +168,11 @@ int upnp_map(protocol_state_t *state, const client_mapping_t *mapping,
case 725: // The NAT implementation only supports permanent lease times on port mapping
duration = 0;
break;
+ case 729: // Attempted port mapping is not allowed due to conflict with other mechanisms
+ // NAT port mapping rules can be created by other mechanisms besides UPnP IGD.
+ // Therefore, it is possible that port mappings done by independent mechanisms MAY overlap or conflict.
+ external_port = random_port();
+ break;
default:
return PROTOCOL_ERR_PROTOCOL_FAILED;
break;
@@ -329,13 +334,26 @@ int upnp_impl_query_control_url(upnp_impl_t *impl, timestamp_t end_timestamp) {
return PROTOCOL_ERR_NETWORK_FAILED;
}
- const char *serviceType = "urn:schemas-upnp-org:service:WANIPConnection:1";
+ // Try to find WANIPConnection:2
+ const char *serviceType = "urn:schemas-upnp-org:service:WANIPConnection:2";
const char *service =
xml_find_matching_child(response.body, "service", "serviceType", serviceType);
- if (!service) {
- PLUM_LOG_WARN("WANIPConnection not found in UPnP-IGD services");
- http_free(&response);
- return PROTOCOL_ERR_PROTOCOL_FAILED;
+ if (service) {
+ impl->wanipconnection_ver = 2;
+ }
+ else {
+ // Try to find WANIPConnection:1
+ serviceType = "urn:schemas-upnp-org:service:WANIPConnection:1";
+ service =
+ xml_find_matching_child(response.body, "service", "serviceType", serviceType);
+ if (service) {
+ impl->wanipconnection_ver = 1;
+ }
+ else {
+ PLUM_LOG_WARN("WANIPConnection not found in UPnP-IGD services");
+ http_free(&response);
+ return PROTOCOL_ERR_PROTOCOL_FAILED;
+ }
}
char control_url[HTTP_MAX_URL_LEN];
@@ -365,7 +383,7 @@ int upnp_impl_query_control_url(upnp_impl_t *impl, timestamp_t end_timestamp) {
strcpy(control_url, tmp);
}
- PLUM_LOG_DEBUG("UPnP-IGP WANIPConnection control URL: %s", control_url);
+ PLUM_LOG_DEBUG("UPnP-IGP WANIPConnection:%d control URL: %s", impl->wanipconnection_ver, control_url);
free(impl->control_url);
impl->control_url = malloc(strlen(control_url) + 1);
strcpy(impl->control_url, control_url);
@@ -449,7 +467,7 @@ int upnp_impl_map(upnp_impl_t *impl, plum_ip_protocol_t protocol, uint16_t exter
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
""
""
+ "xmlns:m=\"urn:schemas-upnp-org:service:WANIPConnection:%d\">"
""
"%hu"
"%s"
@@ -461,6 +479,7 @@ int upnp_impl_map(upnp_impl_t *impl, plum_ip_protocol_t protocol, uint16_t exter
""
""
"\r\n",
+ impl->wanipconnection_ver,
external_port, protocol == PLUM_IP_PROTOCOL_UDP ? "UDP" : "TCP",
internal_port, local_str, description, lifetime);
if (len <= 0 || len >= UPNP_BUFFER_SIZE) {
@@ -472,8 +491,14 @@ int upnp_impl_map(upnp_impl_t *impl, plum_ip_protocol_t protocol, uint16_t exter
memset(&request, 0, sizeof(request));
request.method = HTTP_METHOD_POST;
request.url = impl->control_url;
- request.headers =
- "SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping\r\n";
+ if (impl->wanipconnection_ver == 2) {
+ request.headers =
+ "SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:2#AddPortMapping\r\n";
+ }
+ else {
+ request.headers =
+ "SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping\r\n";
+ }
request.body = buffer;
request.body_size = strlen(request.body);
request.body_type = "text/xml; charset=\"utf-8\"";
@@ -517,14 +542,14 @@ int upnp_impl_unmap(upnp_impl_t *impl, plum_ip_protocol_t protocol, uint16_t ext
""
""
- ""
+ ""
""
"%hu"
"%s"
""
""
"\r\n",
- external_port, protocol == PLUM_IP_PROTOCOL_UDP ? "UDP" : "TCP");
+ impl->wanipconnection_ver, external_port, protocol == PLUM_IP_PROTOCOL_UDP ? "UDP" : "TCP");
if (len <= 0 || len >= UPNP_BUFFER_SIZE) {
PLUM_LOG_ERROR("Failed to format SOAP request body");
return PROTOCOL_ERR_UNKNOWN;
@@ -534,8 +559,14 @@ int upnp_impl_unmap(upnp_impl_t *impl, plum_ip_protocol_t protocol, uint16_t ext
memset(&request, 0, sizeof(request));
request.method = HTTP_METHOD_POST;
request.url = impl->control_url;
- request.headers =
- "SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping\r\n";
+ if (impl->wanipconnection_ver == 2) {
+ request.headers =
+ "SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:2#DeletePortMapping\r\n";
+ }
+ else {
+ request.headers =
+ "SOAPAction: urn:schemas-upnp-org:service:WANIPConnection:1#DeletePortMapping\r\n";
+ }
request.body = buffer;
request.body_size = strlen(request.body);
request.body_type = "text/xml; charset=\"utf-8\"";
diff --git a/src/upnp.h b/src/upnp.h
index 50f4e29..16559ca 100644
--- a/src/upnp.h
+++ b/src/upnp.h
@@ -31,6 +31,7 @@ typedef struct {
socket_t sock;
char external_addr_str[ADDR_MAX_STRING_LEN];
char *location_url;
+ int wanipconnection_ver;
char *control_url;
atomic(upnp_interrupt_t) interrupt;
} upnp_impl_t;