pidgin/pidgin

Use gUPnP-igd to implement add/remove port mappings

19 months ago, Elliott Sales de Andrade
13fdc1beaf35
Parents e6b5a931b741
Children 9f6a2c90800e
Use gUPnP-igd to implement add/remove port mappings

Unfortunately, there appears to be no way to [cancel a mapping request](https://gitlab.gnome.org/GNOME/gupnp-igd/-/issues/7), so I've removed that function, and removed the return values used for cancelling. If cancelling comes back, it'll probably be in the form of a `GCancellable` anyway.

Testing Done:
Compile only.

We removed implicit automatic port mapping when we replaced `purple_proxy_connect` with direct `gio` code. Protocols should probably be explicit about when they request a port mapping, but they haven't been made to do that yet.

Reviewed at https://reviews.imfreedom.org/r/1789/
--- a/ChangeLog.API Sun Sep 18 04:06:33 2022 -0500
+++ b/ChangeLog.API Sun Sep 18 22:30:19 2022 -0500
@@ -264,7 +264,8 @@
* purple_str_size_to_units now takes a goffset as the size parameter
* PTFunc renamed to PurpleThemeFunc
* purple_txt_resolve now takes a PurpleAccount as the first parameter
- * UPnPMappingAddRemove renamed to PurpleUPnPMappingAddRemove
+ * purple_upnp_remove_port_mapping no longer returns anything
+ * purple_upnp_set_port_mapping no longer returns anything
* purple_util_fetch_url_request now takes a PurpleAccount as
the first parameter
* purple_util_fetch_url_request now takes a length as the eighth
@@ -707,6 +708,8 @@
* purple_txt_resolve_account
* PurpleType, use GType instead.
* purple_unescape_filename
+ * PurpleUPnPMappingAddRemove
+ * purple_upnp_cancel_port_mapping
* purple_url_decode
* purple_user_dir
* purple_utf8_salvage. Use g_utf8_make_valid instead.
--- a/libpurple/meson.build Sun Sep 18 04:06:33 2022 -0500
+++ b/libpurple/meson.build Sun Sep 18 22:30:19 2022 -0500
@@ -310,8 +310,8 @@
install : true,
version : PURPLE_LIB_VERSION,
dependencies : # static_link_libs
- [dnsapi, ws2_32, glib, gio, gplugin_dep, gupnp, libsoup,
- libxml, gdk_pixbuf, gstreamer,
+ [dnsapi, ws2_32, glib, gio, gplugin_dep, gupnp,
+ gupnp_igd, libsoup, libxml, gdk_pixbuf, gstreamer,
gstreamer_app, json, sqlite3, math])
install_headers(purple_coreheaders,
--- a/libpurple/upnp.c Sun Sep 18 04:06:33 2022 -0500
+++ b/libpurple/upnp.c Sun Sep 18 22:30:19 2022 -0500
@@ -24,8 +24,7 @@
#include <libgupnp/gupnp-control-point.h>
#include <libgupnp/gupnp-service-info.h>
#include <libgupnp/gupnp-service-proxy.h>
-
-#include <libsoup/soup.h>
+#include <libgupnp-igd/gupnp-simple-igd.h>
#include "upnp.h"
@@ -44,45 +43,9 @@
/***************************************************************
** General Defines *
****************************************************************/
-/* limit UPnP-triggered http downloads to 128k */
-#define MAX_UPNP_DOWNLOAD (128 * 1024)
-
-/******************************************************************
-** Action Defines *
-*******************************************************************/
-#define SOAP_ACTION \
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" \
- "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " \
- "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" \
- "<s:Body>\r\n" \
- "<u:%s xmlns:u=\"%s\">\r\n" \
- "%s" \
- "</u:%s>\r\n" \
- "</s:Body>\r\n" \
- "</s:Envelope>"
-
-#define PORT_MAPPING_LEASE_TIME "0"
+#define PORT_MAPPING_LEASE_TIME 0
#define PORT_MAPPING_DESCRIPTION "PURPLE_UPNP_PORT_FORWARD"
-#define ADD_PORT_MAPPING_PARAMS \
- "<NewRemoteHost></NewRemoteHost>\r\n" \
- "<NewExternalPort>%i</NewExternalPort>\r\n" \
- "<NewProtocol>%s</NewProtocol>\r\n" \
- "<NewInternalPort>%i</NewInternalPort>\r\n" \
- "<NewInternalClient>%s</NewInternalClient>\r\n" \
- "<NewEnabled>1</NewEnabled>\r\n" \
- "<NewPortMappingDescription>" \
- PORT_MAPPING_DESCRIPTION \
- "</NewPortMappingDescription>\r\n" \
- "<NewLeaseDuration>" \
- PORT_MAPPING_LEASE_TIME \
- "</NewLeaseDuration>\r\n"
-
-#define DELETE_PORT_MAPPING_PARAMS \
- "<NewRemoteHost></NewRemoteHost>\r\n" \
- "<NewExternalPort>%i</NewExternalPort>\r\n" \
- "<NewProtocol>%s</NewProtocol>\r\n"
-
typedef enum {
PURPLE_UPNP_STATUS_UNDISCOVERED = -1,
PURPLE_UPNP_STATUS_UNABLE_TO_DISCOVER,
@@ -99,8 +62,7 @@
gint64 lookup_time;
} PurpleUPnPControlInfo;
-struct _PurpleUPnPMappingAddRemove
-{
+typedef struct {
unsigned short portmap;
gchar protocol[4];
gboolean add;
@@ -108,15 +70,14 @@
gpointer cb_data;
gboolean success;
guint tima; /* g_timeout_add handle */
- SoupMessage *msg;
-};
+} PurpleUPnPMappingAddRemove;
static PurpleUPnPControlInfo control_info = {
PURPLE_UPNP_STATUS_UNDISCOVERED,
NULL, "\0", "\0", "\0", 0};
static GUPnPContextManager *manager = NULL;
-static SoupSession *session = NULL;
+static GUPnPSimpleIgd *simple_igd = NULL;
static GSList *discovery_callbacks = NULL;
static void lookup_internal_ip(void);
@@ -287,33 +248,6 @@
control_info.status = PURPLE_UPNP_STATUS_DISCOVERING;
}
-static SoupMessage *
-purple_upnp_generate_action_message_and_send(const gchar *actionName,
- const gchar *actionParams,
- SoupSessionCallback cb,
- gpointer cb_data)
-{
- SoupMessage *msg;
- gchar *action;
- gchar* soapMessage;
-
- /* set the soap message */
- soapMessage = g_strdup_printf(SOAP_ACTION, actionName,
- control_info.service_type, actionParams, actionName);
-
- msg = soup_message_new("POST", control_info.control_url);
- // purple_http_request_set_max_len(msg, MAX_UPNP_DOWNLOAD);
- action = g_strdup_printf("\"%s#%s\"", control_info.service_type, actionName);
- soup_message_headers_replace(soup_message_get_request_headers(msg),
- "SOAPAction", action);
- g_free(action);
- soup_message_set_request(msg, "text/xml; charset=utf-8", SOUP_MEMORY_TAKE,
- soapMessage, strlen(soapMessage));
- soup_session_queue_message(session, msg, cb, cb_data);
-
- return msg;
-}
-
const gchar *
purple_upnp_get_public_ip()
{
@@ -431,26 +365,57 @@
}
static void
-done_port_mapping_cb(G_GNUC_UNUSED SoupSession *session, SoupMessage *msg,
- gpointer user_data)
+upnp_mapped_external_port_cb(GUPnPSimpleIgd *igd, const gchar *protocol,
+ G_GNUC_UNUSED const gchar *external_ip,
+ G_GNUC_UNUSED const gchar *replaces_external_ip,
+ guint16 external_port,
+ G_GNUC_UNUSED const gchar *local_ip,
+ guint16 local_port,
+ G_GNUC_UNUSED const gchar *description,
+ gpointer data)
{
- PurpleUPnPMappingAddRemove *ar = user_data;
-
- gboolean success = TRUE;
+ PurpleUPnPMappingAddRemove *ar = data;
- /* determine if port mapping was a success */
- if (!SOUP_STATUS_IS_SUCCESSFUL(soup_message_get_status(msg))) {
- purple_debug_error("upnp",
- "purple_upnp_set_port_mapping(): Failed HTTP_OK: %s",
- soup_message_get_reason_phrase(msg));
- success = FALSE;
- } else {
- purple_debug_info("upnp",
- "Successfully completed port mapping operation");
+ if(!purple_strequal(ar->protocol, protocol)) {
+ return;
+ }
+ if(external_port != ar->portmap) {
+ return;
}
- ar->success = success;
+ purple_debug_info("upnp", "Successfully completed port mapping operation");
+
+ ar->success = TRUE;
ar->tima = g_timeout_add(0, fire_ar_cb_async_and_free, ar);
+
+ g_signal_handlers_disconnect_by_data(igd, ar);
+}
+
+static void
+upnp_error_mapping_port_cb(GUPnPSimpleIgd *igd, GError *error,
+ const gchar *protocol,
+ guint16 external_port,
+ G_GNUC_UNUSED const gchar *local_ip,
+ G_GNUC_UNUSED guint16 local_port,
+ G_GNUC_UNUSED const gchar *description,
+ gpointer data)
+{
+ PurpleUPnPMappingAddRemove *ar = data;
+
+ if(!purple_strequal(ar->protocol, protocol)) {
+ return;
+ }
+ if(external_port != ar->portmap) {
+ return;
+ }
+
+ purple_debug_error("upnp", "purple_upnp_set_port_mapping(): Failed: %s",
+ error->message);
+
+ ar->success = FALSE;
+ ar->tima = g_timeout_add(0, fire_ar_cb_async_and_free, ar);
+
+ g_signal_handlers_disconnect_by_data(igd, ar);
}
static void
@@ -458,41 +423,43 @@
{
PurpleUPnPMappingAddRemove *ar = data;
- if (has_control_mapping) {
- gchar action_name[25];
- gchar *action_params;
- if(ar->add) {
- const gchar *internal_ip;
- /* get the internal IP */
- if(!(internal_ip = purple_upnp_get_internal_ip())) {
- purple_debug_error("upnp",
- "purple_upnp_set_port_mapping(): couldn't get local ip\n");
- ar->success = FALSE;
- ar->tima = g_timeout_add(0, fire_ar_cb_async_and_free, ar);
- return;
- }
- strncpy(action_name, "AddPortMapping",
- sizeof(action_name));
- action_params = g_strdup_printf(
- ADD_PORT_MAPPING_PARAMS,
- ar->portmap, ar->protocol, ar->portmap,
- internal_ip);
- } else {
- strncpy(action_name, "DeletePortMapping", sizeof(action_name));
- action_params = g_strdup_printf(
- DELETE_PORT_MAPPING_PARAMS,
- ar->portmap, ar->protocol);
- }
-
- ar->msg = purple_upnp_generate_action_message_and_send(
- action_name, action_params, done_port_mapping_cb, ar);
-
- g_free(action_params);
+ if (!has_control_mapping) {
+ ar->success = FALSE;
+ ar->tima = g_timeout_add(0, fire_ar_cb_async_and_free, ar);
return;
}
- ar->success = FALSE;
- ar->tima = g_timeout_add(0, fire_ar_cb_async_and_free, ar);
+ if(ar->add) {
+ const gchar *internal_ip;
+ /* get the internal IP */
+ if(!(internal_ip = purple_upnp_get_internal_ip())) {
+ purple_debug_error("upnp",
+ "purple_upnp_set_port_mapping(): couldn't get local ip\n");
+ ar->success = FALSE;
+ ar->tima = g_timeout_add(0, fire_ar_cb_async_and_free, ar);
+ return;
+ }
+
+ if(simple_igd == NULL) {
+ simple_igd = gupnp_simple_igd_new();
+ }
+
+ gupnp_simple_igd_add_port(simple_igd, ar->protocol, ar->portmap,
+ internal_ip, ar->portmap,
+ PORT_MAPPING_LEASE_TIME,
+ PORT_MAPPING_DESCRIPTION);
+ } else {
+ if(simple_igd == NULL) {
+ simple_igd = gupnp_simple_igd_new();
+ }
+
+ gupnp_simple_igd_remove_port(simple_igd, ar->protocol, ar->portmap);
+ }
+
+ g_signal_connect(simple_igd, "mapped-external-port",
+ G_CALLBACK(upnp_mapped_external_port_cb), ar);
+ g_signal_connect(simple_igd, "error-mapping-port",
+ G_CALLBACK(upnp_error_mapping_port_cb), ar);
}
static gboolean
@@ -505,38 +472,9 @@
return FALSE;
}
-void purple_upnp_cancel_port_mapping(PurpleUPnPMappingAddRemove *ar)
-{
- GSList *l;
-
- /* Remove ar from discovery_callbacks if present; it was inserted after a cb.
- * The same cb may be in the list multiple times, so be careful to remove
- * the one associated with ar. */
- l = discovery_callbacks;
- while (l)
- {
- GSList *next = l->next;
-
- if (next && (next->data == ar)) {
- discovery_callbacks = g_slist_delete_link(discovery_callbacks, next);
- next = l->next;
- discovery_callbacks = g_slist_delete_link(discovery_callbacks, l);
- }
-
- l = next;
- }
-
- if (ar->tima > 0)
- g_source_remove(ar->tima);
-
- soup_session_cancel_message(session, ar->msg, SOUP_STATUS_CANCELLED);
-
- g_free(ar);
-}
-
-PurpleUPnPMappingAddRemove *
-purple_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
- PurpleUPnPCallback cb, gpointer cb_data)
+void
+purple_upnp_set_port_mapping(unsigned short portmap, const gchar *protocol,
+ PurpleUPnPCallback cb, gpointer cb_data)
{
PurpleUPnPMappingAddRemove *ar;
@@ -574,13 +512,11 @@
do_port_mapping_cb(TRUE, ar);
break;
}
-
- return ar;
}
-PurpleUPnPMappingAddRemove *
-purple_upnp_remove_port_mapping(unsigned short portmap, const char* protocol,
- PurpleUPnPCallback cb, gpointer cb_data)
+void
+purple_upnp_remove_port_mapping(unsigned short portmap, const char *protocol,
+ PurpleUPnPCallback cb, gpointer cb_data)
{
PurpleUPnPMappingAddRemove *ar;
@@ -618,8 +554,6 @@
do_port_mapping_cb(TRUE, ar);
break;
}
-
- return ar;
}
static void
@@ -637,8 +571,6 @@
void
purple_upnp_init()
{
- session = soup_session_new();
-
g_signal_connect(g_network_monitor_get_default(),
"network-changed",
G_CALLBACK(purple_upnp_network_config_changed_cb),
@@ -648,9 +580,7 @@
void
purple_upnp_uninit(void)
{
- soup_session_abort(session);
- g_clear_object(&session);
-
+ g_clear_object(&simple_igd);
g_clear_pointer(&control_info.control_url, g_free);
g_clear_pointer(&control_info.service_type, g_free);
g_clear_object(&manager);
--- a/libpurple/upnp.h Sun Sep 18 04:06:33 2022 -0500
+++ b/libpurple/upnp.h Sun Sep 18 22:30:19 2022 -0500
@@ -27,16 +27,12 @@
#ifndef PURPLE_UPNP_H
#define PURPLE_UPNP_H
-typedef struct _PurpleUPnPMappingAddRemove PurpleUPnPMappingAddRemove;
-
G_BEGIN_DECLS
/**************************************************************************/
/* UPnP API */
/**************************************************************************/
-/* typedef struct _PurpleUPnPRequestData PurpleUPnPRequestData; */
-
typedef void (*PurpleUPnPCallback) (gboolean success, gpointer data);
@@ -80,15 +76,6 @@
const gchar* purple_upnp_get_public_ip(void);
/**
- * purple_upnp_cancel_port_mapping:
- * @mapping_data: The data returned when you initiated the UPnP mapping request.
- *
- * Cancel a pending port mapping request initiated with either
- * purple_upnp_set_port_mapping() or purple_upnp_remove_port_mapping().
- */
-void purple_upnp_cancel_port_mapping(PurpleUPnPMappingAddRemove *mapping_data);
-
-/**
* purple_upnp_set_port_mapping:
* @portmap: The port to map to this client
* @protocol: The protocol to map, either "TCP" or "UDP"
@@ -99,12 +86,8 @@
* Maps Ports in a UPnP enabled IGD that sits on the local network to
* this purple client. Essentially, this function takes care of the port
* forwarding so things like file transfers can work behind NAT firewalls
- *
- * Returns: (transfer full): Data which can be passed to purple_upnp_cancel_port_mapping() to
- * cancel
*/
-PurpleUPnPMappingAddRemove *purple_upnp_set_port_mapping(unsigned short portmap, const gchar* protocol,
- PurpleUPnPCallback cb, gpointer cb_data);
+void purple_upnp_set_port_mapping(unsigned short portmap, const gchar *protocol, PurpleUPnPCallback cb, gpointer cb_data);
/**
* purple_upnp_remove_port_mapping:
@@ -118,12 +101,8 @@
* to this purple client. Essentially, this function takes care of deleting the
* port forwarding after they have completed a connection so another client on
* the local network can take advantage of the port forwarding
- *
- * Returns: (transfer full): Data which can be passed to purple_upnp_cancel_port_mapping() to
- * cancel
*/
-PurpleUPnPMappingAddRemove *purple_upnp_remove_port_mapping(unsigned short portmap,
- const gchar* protocol, PurpleUPnPCallback cb, gpointer cb_data);
+void purple_upnp_remove_port_mapping(unsigned short portmap, const gchar *protocol, PurpleUPnPCallback cb, gpointer cb_data);
G_END_DECLS
--- a/meson.build Sun Sep 18 04:06:33 2022 -0500
+++ b/meson.build Sun Sep 18 22:30:19 2022 -0500
@@ -294,6 +294,7 @@
#######################################################################
gupnp = dependency('gupnp-1.2', version : '>= 1.2.0')
+gupnp_igd = dependency('gupnp-igd-1.0', version : '>= 1.0.0')
#######################################################################
# Check for libsoup (required)