qulogic/pidgin

Implement mdns_publish using DNS Service API.
draft win-mdns
2021-01-04, Elliott Sales de Andrade
792aadc67525
Parents 69eee16785bf
Children 9b049f9577a1
Implement mdns_publish using DNS Service API.
--- a/libpurple/protocols/bonjour/mdns_dnsapi.c Fri Jan 01 03:26:15 2021 -0500
+++ b/libpurple/protocols/bonjour/mdns_dnsapi.c Mon Jan 04 00:31:06 2021 -0500
@@ -32,13 +32,40 @@
/* data used by win32 bonjour implementation */
typedef struct {
- gpointer unused;
+ DNS_SERVICE_INSTANCE instance;
+ DNS_SERVICE_CANCEL register_cancel;
} Win32SessionImplData;
typedef struct {
gpointer unused;
} Win32BuddyImplData;
+static gboolean
+_mdns_service_register_main_context(gpointer data)
+{
+ DWORD status = GPOINTER_TO_UINT(data);
+
+ /* TODO: deal with collision */
+ if (status != ERROR_SUCCESS) {
+ purple_debug_error("bonjour", "service advertisement - error (%ld)",
+ status);
+ } else {
+ purple_debug_info("bonjour", "service advertisement - success.");
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+/* This callback occurs in a different thread; so all it does is pass the
+ * result to the main context thread, or Purple will break. */
+static void
+_mdns_service_register_callback(DWORD status, PVOID pQueryContext,
+ PDNS_SERVICE_INSTANCE pInstance)
+{
+ g_timeout_add_seconds(0, G_SOURCE_FUNC(_mdns_service_register_main_context),
+ GUINT_TO_POINTER(status));
+}
+
/****************************
* mdns_interface functions *
****************************/
@@ -57,10 +84,138 @@
dnsapi_mdns_publish(BonjourDnsSd *data, PublishType type, GSList *records)
{
Win32SessionImplData *idata = data->mdns_impl_data;
- gboolean ret = FALSE;
+ gchar *tmp = NULL;
+ PWSTR service_name = NULL;
+ PWSTR hostname = NULL;
+ DWORD dwPropertiesCount = 0;
+ DWORD i;
+ PCWSTR *keys = NULL;
+ PCWSTR *values = NULL;
+ DNS_SERVICE_REGISTER_REQUEST request;
+ DNS_STATUS status;
+ GError *error = NULL;
+ gboolean ret = TRUE;
g_return_val_if_fail(idata != NULL, FALSE);
+ dwPropertiesCount = g_slist_length(records);
+ keys = g_new0(PCWSTR, dwPropertiesCount);
+ values = g_new0(PCWSTR, dwPropertiesCount);
+ for (i = 0; i < dwPropertiesCount; i++, records = records->next) {
+ PurpleKeyValuePair *kvp = records->data;
+ keys[i] = g_utf8_to_utf16(kvp->key, -1, NULL, NULL, &error);
+ if (error != NULL) {
+ purple_debug_error(
+ "bonjour",
+ "Unable to convert record key '%s' to UTF-16: %s",
+ (const gchar *)kvp->key, error->message);
+ g_error_free(error);
+ ret = FALSE;
+ break;
+ }
+ values[i] = g_utf8_to_utf16(kvp->value, -1, NULL, NULL, &error);
+ if (error != NULL) {
+ purple_debug_error(
+ "bonjour",
+ "Unable to convert record value '%s' to UTF-16: %s",
+ (const gchar *)kvp->value, error->message);
+ g_error_free(error);
+ ret = FALSE;
+ break;
+ }
+ }
+ if (!ret) {
+ for (i = 0; i < dwPropertiesCount; i++) {
+ g_free((gpointer)keys[i]);
+ g_free((gpointer)values[i]);
+ }
+ g_free(keys);
+ g_free(values);
+
+ return FALSE;
+ }
+
+ /* This seems to be required, though it doesn't appear to matter what
+ * its value is. Use whatever GLib determined to be the host name. */
+ hostname = g_utf8_to_utf16(g_get_host_name(), -1, NULL, NULL, &error);
+ if (error != NULL) {
+ purple_debug_error("bonjour",
+ "Unable to convert hostname '%s' to UTF-16: %s",
+ g_get_host_name(), error->message);
+ g_error_free(error);
+ for (i = 0; i < dwPropertiesCount; i++) {
+ g_free((gpointer)keys[i]);
+ g_free((gpointer)values[i]);
+ }
+ g_free(keys);
+ g_free(values);
+
+ return FALSE;
+ }
+
+ /* The DNS Service API requires this to be specified as one entry,
+ * including the .local suffix. */
+ tmp = g_strjoin(".", bonjour_get_jid(data->account),
+ LINK_LOCAL_RECORD_NAME "local", NULL);
+ service_name = g_utf8_to_utf16(tmp, -1, NULL, NULL, &error);
+ if (error != NULL) {
+ purple_debug_error("bonjour",
+ "Unable to convert service name '%s' to UTF-16: %s",
+ tmp, error->message);
+ g_error_free(error);
+ for (i = 0; i < dwPropertiesCount; i++) {
+ g_free((gpointer)keys[i]);
+ g_free((gpointer)values[i]);
+ }
+ g_free(keys);
+ g_free(values);
+ g_free(hostname);
+ g_free(tmp);
+
+ return FALSE;
+ }
+ g_free(tmp);
+
+ request.Version = DNS_QUERY_REQUEST_VERSION1;
+ request.InterfaceIndex = 0;
+ request.pServiceInstance = DnsServiceConstructInstance(
+ service_name, hostname, NULL, NULL, data->port_p2pj, 0, 0,
+ dwPropertiesCount, keys, values);
+ request.pRegisterCompletionCallback = _mdns_service_register_callback;
+ request.pQueryContext = idata;
+ request.hCredentials = NULL;
+ request.unicastEnabled = FALSE;
+
+ switch (type) {
+ case PUBLISH_START:
+ purple_debug_info("bonjour", "Registering presence on port %d",
+ data->port_p2pj);
+ status = DnsServiceRegister(&request, &idata->register_cancel);
+ break;
+
+ case PUBLISH_UPDATE:
+ purple_debug_info("bonjour", "Updating presence");
+ status = DnsServiceRegister(&request, &idata->register_cancel);
+ break;
+ }
+
+ if (status != DNS_REQUEST_PENDING) {
+ purple_debug_error("bonjour",
+ "Failed to publish presence service (%ld)", status);
+ ret = FALSE;
+ }
+
+ /* Free the memory used by temp data */
+ DnsServiceFreeInstance(request.pServiceInstance);
+ for (i = 0; i < dwPropertiesCount; i++) {
+ g_free((gpointer)keys[i]);
+ g_free((gpointer)values[i]);
+ }
+ g_free(keys);
+ g_free(values);
+ g_free(hostname);
+ g_free(service_name);
+
return ret;
}