/*
 * Copyright (C) 2019-2024 iopsys Software Solutions AB
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation
 *
 *		Author: Amin Ben Ramdhane <amin.benramdhane@pivasoftware.com>
 *		Author: Mohd Husaam Mehdi <husaam.mehdi@iopsys.eu>
 */

#include "dns_sd.h"
#include <json-c/json.h>

struct dns_sd_servinfo {
	char app_proto[16];
	char trans_proto[4];
	char domain[257];
};

/*************************************************************
* ENTRY METHOD
**************************************************************/
static int browseDNSSDAdvertiseTextRecordInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data *curr_data = NULL;
	LIST_HEAD(dup_list);
	char *inst = NULL;

	struct uci_section *parent_data = ((struct dm_data *)prev_data)->config_section;
	char *p_sec_name = section_name(parent_data);

	synchronize_config_sections_with_dmmap_sections("dnssd", "text_record", "dmmap_dnssd", &dup_list, parent_node->browse_type);
	list_for_each_entry(curr_data, &dup_list, list) {
		char *p_name = NULL;
		dmuci_get_value_by_section_string(curr_data->config_section, "parent", &p_name);

		if (DM_STRCMP(p_name, p_sec_name) != 0)
			continue;

		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "txt_instance", "txt_alias");
		if (DM_LINK_INST_OBJ(dmctx, parent_node, curr_data, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int get_dns_sd_advertise_filepath(struct uci_section *s, char *file_path, size_t size)
{
	char *file = NULL, *enab = NULL;

	if (s == NULL || file_path == NULL)
		return -1;

	dmuci_get_value_by_section_string(s, "enable", &enab);
	dmuci_get_value_by_section_string(s, "file", &file);

	if (DM_STRLEN(file) == 0)
		return -1;

	bool enable = dmuci_string_to_boolean(enab);
	snprintf(file_path, size, "%s%s", enable ? "/etc/umdns/" : "/etc/umdns/tmp/", file);

	if (!file_exists(file_path))
		return -1;

	return 0;
}

static void delete_dns_sd_text_record(struct uci_section *s, const char *record)
{
	char *parent = NULL, *inst_name = NULL;
	char file_path[256] = {0};

	if (s == NULL || record == NULL)
		return;

	dmuci_get_value_by_section_string(s, "parent", &parent);
	struct uci_section *par_sec = get_origin_section_from_config("dnssd", "advertise", parent);
	if (par_sec == NULL)
		return;

	if (get_dns_sd_advertise_filepath(par_sec, file_path, sizeof(file_path)) != 0)
		return;

	dmuci_get_value_by_section_string(par_sec, "instance_name", &inst_name);
	if (DM_STRLEN(inst_name) == 0)
		return;

	json_object *jobj = json_object_from_file(file_path);
	if (!jobj)
		return;

	json_object *inst_val = json_object_object_get(jobj, inst_name);
	if (inst_val == NULL) {
		json_object_put(jobj);
		return;
	}

	json_object *option = json_object_object_get(inst_val, "txt");
	if (option == NULL) {
		json_object_put(jobj);
		return;
	}

	for (int i = 0; i < json_object_array_length(option); i++) {
		json_object *jval = json_object_array_get_idx(option, i);
		if (DM_STRCMP(json_object_get_string(jval), record) == 0) {
			json_object_array_del_idx(option, i, 1);
			break;
		}
	}

	json_object_to_file(file_path, jobj);
	json_object_put(jobj);
}

static void add_dns_sd_text_record(struct uci_section *s, const char *record)
{
	char *parent = NULL, *inst_name = NULL;
	char file_path[256] = {0};

	if (s == NULL || record == NULL)
		return;

	dmuci_get_value_by_section_string(s, "parent", &parent);
	struct uci_section *par_sec = get_origin_section_from_config("dnssd", "advertise", parent);
	if (par_sec == NULL)
		return;

	if (get_dns_sd_advertise_filepath(par_sec, file_path, sizeof(file_path)) != 0)
		return;

	dmuci_get_value_by_section_string(par_sec, "instance_name", &inst_name);
	if (DM_STRLEN(inst_name) == 0)
		return;

	json_object *jobj = json_object_from_file(file_path);
	if (!jobj)
		return;

	json_object *inst_val = json_object_object_get(jobj, inst_name);
	if (inst_val == NULL) {
		json_object_put(jobj);
		return;
	}

	json_object *option = json_object_object_get(inst_val, "txt");
	if (option == NULL) {
		json_object *jval = json_object_new_array();
		json_object_array_add(jval, json_object_new_string(record));
		json_object_object_add(inst_val, "txt", jval);
	} else {
		json_object_array_add(option, json_object_new_string(record));
	}

	json_object_to_file(file_path, jobj);
	json_object_put(jobj);
}

static int get_dns_sd_advertise_text_record_number_of_entries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDNSSDAdvertiseTextRecordInst);
	dmasprintf(value, "%d", cnt);

	return 0;
}

static int get_dns_sd_advertise_text_record_key(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "key", value);
	return 0;
}

static int set_dns_sd_advertise_text_record_key(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *old_key = NULL, *key_val = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 255, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "key", &old_key);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "key_value", &key_val);

			if (DM_STRLEN(old_key) != 0 && DM_STRLEN(key_val) != 0) {
				char record[256] = {0};
				snprintf(record, sizeof(record), "%s=%s", old_key, key_val);
				delete_dns_sd_text_record(((struct dm_data *)data)->config_section, record);
			}

			if (DM_STRLEN(value) != 0 && DM_STRLEN(key_val) != 0) {
				char record[256] = {0};
				snprintf(record, sizeof(record), "%s=%s", value, key_val);
				add_dns_sd_text_record(((struct dm_data *)data)->config_section, record);
			}

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "key", value);

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

static int get_dns_sd_advertise_text_record_value(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "key_value", value);
	return 0;
}

static int set_dns_sd_advertise_text_record_value(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *old_val = NULL, *key = NULL;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 255, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "key", &key);
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "key_value", &old_val);

			if (DM_STRLEN(key) != 0 && DM_STRLEN(old_val) != 0) {
				char record[256] = {0};
				snprintf(record, sizeof(record), "%s=%s", key, old_val);
				delete_dns_sd_text_record(((struct dm_data *)data)->config_section, record);
			}

			if (DM_STRLEN(value) != 0 && DM_STRLEN(key) != 0) {
				char record[256] = {0};
				snprintf(record, sizeof(record), "%s=%s", key, value);
				add_dns_sd_text_record(((struct dm_data *)data)->config_section, record);
			}

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "key_value", value);

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

static int browseDNSSDServiceInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data curr_data = {0};
	struct srv_info current_srv_info_obj = {0};

	json_object *ubus_result_browse = NULL;
	int id = 0;
	char *inst = NULL;

	// call ubus
	dmubus_call("umdns", "browse", UBUS_ARGS{{"array", "1", Boolean}}, 1, &ubus_result_browse);

	// return from here if result empty
	DM_ASSERT(ubus_result_browse, bbfdm_set_fault_message(dmctx, "ubus call umdns browse did not work"));

	json_object_object_foreach(ubus_result_browse, key, val) {
		char *proto_position = NULL;
		char *str = NULL;

		// example of key: "_http._tcp"
		current_srv_info_obj.application_protocol = dmstrdup(key);

		// for readability
		str = current_srv_info_obj.application_protocol;

		// find the point where transport protocol starts
		if (DM_STRSTR(str, "._tcp")) {
			proto_position = DM_STRSTR(str, "._tcp");
			current_srv_info_obj.transport_protocol_flag = TRANSPORT_TCP;
		} else if (DM_STRSTR(str, "._udp")) {
			proto_position = DM_STRSTR(str, "._udp");
			current_srv_info_obj.transport_protocol_flag = TRANSPORT_UDP;
		} else {
			continue;
		}

		// mark the end of application protocol
		*proto_position = '\0';

		// find and remove leading underscore
		proto_position = DM_STRSTR(str, "_");
		if (proto_position)
			current_srv_info_obj.application_protocol = ++proto_position;

		json_object_object_foreach(val, inner_key, inner_val) {
			// update our object
			current_srv_info_obj.instance_name = dmstrdup(inner_key);

			curr_data.json_object = inner_val;
			curr_data.additional_data = (void *)(&current_srv_info_obj);

			inst = handle_instance_without_section(dmctx, parent_node, ++id);

			if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
				return 0;
		}
	}

	return 0;
}

static int browseDNSSDServiceTextRecordInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	struct dm_data curr_data = {0};

	char *inst = NULL;
	char *key_value_str = NULL;
	char *dup_key_value_str = NULL;
	char *pch = NULL, *spch = NULL;

	key_value_str = dmjson_get_value_array_all(((struct dm_data *)prev_data)->json_object, ",", 1, "txt");

	if (key_value_str) {
		int id = 0;
		dup_key_value_str = dmstrdup(key_value_str);
		for (pch = strtok_r(dup_key_value_str, ",", &spch); pch != NULL; pch = strtok_r(NULL, ",", &spch)) {
			inst = handle_instance_without_section(dmctx, parent_node, ++id);

			curr_data.additional_data = (void *)pch;
			if (DM_LINK_INST_OBJ(dmctx, parent_node, (void *)&curr_data, inst) == DM_STOP)
				break;
		}
	}

	return 0;
}

/*************************************************************
* GET & SET PARAM
**************************************************************/
static int get_dns_sd_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	*value = dmuci_get_option_value_fallback_def("umdns", "@umdns[0]", "enable", "1");
	return 0;
}

static int set_dns_sd_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_set_value("umdns", "@umdns[0]", "enable", value);
			break;
	}

	return 0;
}

static int get_dns_sd_service_number_of_entries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDNSSDServiceInst);
	dmasprintf(value, "%d", cnt);

	return 0;
}

static int get_dns_sd_service_instance_name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *instance_name;

	instance_name = dmstrdup(((struct srv_info *)((struct dm_data *)data)->additional_data)->instance_name);
	if (DM_STRLEN(instance_name) > 63) {
		instance_name[63] = '\0';
	}

	*value = instance_name;
	return 0;
}

static int get_dns_sd_service_application_protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *proto;

	proto = dmstrdup(((struct srv_info *)((struct dm_data *)data)->additional_data)->application_protocol);
	if (DM_STRLEN(proto) > 15) {
		proto[15] = '\0';
	}

	*value = proto;
	return 0;
}

static int get_dns_sd_service_transport_protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	if (((struct srv_info *)((struct dm_data *)data)->additional_data)->transport_protocol_flag == TRANSPORT_TCP)
		*value = "TCP";
	else
		*value = "UDP";

	return 0;
}

static char *dns_domain_ensure_dot(const char *domain)
{
	size_t len;
	char *buf = NULL;

	len = DM_STRLEN(domain);
	if (len == 0)
		return dmstrdup("");

	if (domain[len - 1] == '.')
		buf = dmstrdup(domain);
	else
		dmasprintf(&buf, "%s.", domain);

	if (DM_STRLEN(buf) > 256)
		buf[256] = '\0';

	return buf;
}

static int get_dns_sd_service_domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	const char *domain = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "domain");

	*value = dns_domain_ensure_dot(domain);

	return 0;
}

static int get_dns_sd_service_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *port;

	port = dmstrdup(dmjson_get_value(((struct dm_data *)data)->json_object, 1, "port"));

	*value = port;
	return 0;
}

static int get_dns_sd_service_target(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	const char *target = dmjson_get_value(((struct dm_data *)data)->json_object, 1, "host");

	*value = dns_domain_ensure_dot(target);

	return 0;
}

static int get_dns_sd_service_status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	// inactive services are not present in output of ubus
	*value = "LeaseActive";
	return 0;
}

static int get_dns_sd_service_last_update(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *last_updated;

	last_updated = dmstrdup(dmjson_get_value(((struct dm_data *)data)->json_object, 1, "last_update"));

	*value = last_updated;
	return 0;
}

static int get_dns_sd_service_host(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *ipv4_address = NULL;
	json_object *ipv4_array = NULL;
	unsigned idx = 0;
	char hosts_list[HOSTS_LIST_SIZE] = {0};

	dmjson_foreach_value_in_array(((struct dm_data *)data)->json_object, ipv4_array, ipv4_address, idx, 1, "ipv4") {
		bbfdm_get_references(ctx, MATCH_FIRST, "Device.Hosts.Host.", "IPAddress", ipv4_address, hosts_list, sizeof(hosts_list));
	}

	*value = dmstrdup(hosts_list);
	return 0;
}

static int get_dns_sd_service_time_to_live(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *ttl;

	ttl = dmstrdup(dmjson_get_value(((struct dm_data *)data)->json_object, 1, "ttl"));

	*value = ttl;
	return 0;
}

static int get_dns_sd_service_priority(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *priority;

	priority = dmstrdup(dmjson_get_value(((struct dm_data *)data)->json_object, 1, "priority"));

	*value = priority;
	return 0;
}

static int get_dns_sd_service_weight(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *weight;

	weight = dmstrdup(dmjson_get_value(((struct dm_data *)data)->json_object, 1, "weight"));

	*value = weight;
	return 0;
}

static int get_dns_sd_service_text_record_number_of_entries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDNSSDServiceTextRecordInst);
	dmasprintf(value, "%d", cnt);

	return 0;
}

static int get_dns_sd_service_text_record_key(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *key_value_str = dmstrdup((char *)((struct dm_data *)data)->additional_data);

	char *key_end_position = DM_STRSTR(key_value_str, "=");

	// mark end of key
	if (key_end_position)
		*key_end_position = '\0';

	*value = key_value_str;
	return 0;
}

static int get_dns_sd_service_text_record_value(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *key_value_str = dmstrdup((char *)((struct dm_data *)data)->additional_data);

	char *value_start_position = DM_STRCHR(key_value_str, '=');

	*value = value_start_position ? value_start_position + 1 : "";
	return 0;
}

static int addObjDNSSDAdvertiseTextRecord(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	struct uci_section *parent_sec = ((struct dm_data *)data)->config_section;
	struct uci_section *s = NULL, *dmmap_s = NULL;
	char text_sec[256] = {0};

	snprintf(text_sec, sizeof(text_sec), "%s_text_%s", section_name(parent_sec), *instance);

	dmuci_add_section("dnssd", "text_record", &s);
	dmuci_rename_section_by_section(s, text_sec);
	dmuci_set_value_by_section(s, "parent", section_name(parent_sec));

	dmuci_add_section_bbfdm("dmmap_dnssd", "text_record", &dmmap_s);
	dmuci_set_value_by_section(dmmap_s, "section_name", text_sec);
	dmuci_set_value_by_section(dmmap_s, "txt_instance", *instance);

	return 0;
}

static int delObjDNSSDAdvertiseTextRecord(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	char *param_key = NULL, *key_value = NULL;
	char record[512] = {0};

	struct dm_data *p = (struct dm_data *)data;
	dmuci_get_value_by_section_string(p->config_section, "key", &param_key);
	dmuci_get_value_by_section_string(p->config_section, "key_value", &key_value);

	if (DM_STRLEN(param_key) != 0 && DM_STRLEN(key_value) != 0) {
		snprintf(record, sizeof(record), "%s=%s", param_key, key_value);
		delete_dns_sd_text_record(p->config_section, record);
	}

	dmuci_delete_by_section(p->dmmap_section, NULL, NULL);
	dmuci_delete_by_section(p->config_section, NULL, NULL);

	return 0;
}

static int addObjDNSSDAdvertise(char *refparam, struct dmctx *ctx, void *data, char **instance)
{
	char file_name[240] = {0};
	char file_path[256] = {0};
	char obj_name[235] = {0};

	snprintf(obj_name, sizeof(obj_name), "iowrt_%s", *instance);
	snprintf(file_name, sizeof(file_name), "%s.json", obj_name);
	snprintf(file_path, sizeof(file_path), "/etc/umdns/%s", file_name);

	json_object *jobj = json_object_new_object();
	json_object *inner_obj = json_object_new_object();
	json_object_object_add(jobj, obj_name, inner_obj);
	if (json_object_to_file(file_path, jobj) != 0)
		return FAULT_9002;

	json_object_put(inner_obj);
	json_object_put(jobj);

	// add new section
	struct uci_section *s = NULL, *dmmap_s = NULL;
	dmuci_add_section("dnssd", "advertise", &s);
	dmuci_rename_section_by_section(s, obj_name);
	dmuci_set_value_by_section(s, "enable", "1");
	dmuci_set_value_by_section(s, "file", file_name);
	dmuci_set_value_by_section(s, "instance_name", obj_name);

	dmuci_add_section_bbfdm("dmmap_dnssd", "advertise", &dmmap_s);
	dmuci_set_value_by_section(dmmap_s, "section_name", obj_name);
	dmuci_set_value_by_section(dmmap_s, "adv_instance", *instance);

	return 0;
}

static int delObjDNSSDAdvertise(char *refparam, struct dmctx *ctx, void *data, char *instance, unsigned char del_action)
{
	char file_path[256] = {0};

	if (get_dns_sd_advertise_filepath(((struct dm_data *)data)->config_section, file_path, sizeof(file_path)) != 0)
		return 0;

	// Delete the json file corresponding to advertise
	if (remove(file_path) != 0)
		return FAULT_9002;

	// Delete Text records of this
	char *sec_name = section_name(((struct dm_data *)data)->config_section);
	struct uci_section *s = NULL, *stmp = NULL;
	uci_foreach_sections_safe("dnssd", "text_record", stmp, s) {
		char *parent = NULL;

		dmuci_get_value_by_section_string(s, "parent", &parent);
		if (DM_STRCMP(parent, sec_name) != 0)
			continue;

		struct uci_section *txt_map = get_origin_section_from_dmmap("dmmap_dnssd", "text_record", section_name(s));
		dmuci_delete_by_section(txt_map, NULL, NULL);
		dmuci_delete_by_section(s, NULL, NULL);
	}

	// now delete the advertise section
	dmuci_delete_by_section(((struct dm_data *)data)->dmmap_section, NULL, NULL);
	dmuci_delete_by_section(((struct dm_data *)data)->config_section, NULL, NULL);

	// reload umdns
	dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);

	return 0;
}

static char *get_instance_name(char *file_path)
{
	if (file_path == NULL)
		return NULL;

	json_object *jobj = json_object_from_file(file_path);
	if (jobj == NULL)
		return NULL;

	struct lh_entry *entry;
	entry = json_object_get_object(jobj)->head;

	if (!entry) {
		json_object_put(jobj);
		return NULL;
	}

	char *key = strdup((char*)entry->k);
	json_object_put(jobj);

	return key;
}

void add_dns_sd_text_records(char *file_path, char *instance_name, const char *par_sec)
{
	if (file_path == NULL || instance_name == NULL || par_sec == NULL)
		return;

	json_object *jobj = json_object_from_file(file_path);
	if (jobj == NULL)
		return;

	json_object *holder = json_object_object_get(jobj, instance_name);
	if (holder == NULL) {
		json_object_put(jobj);
		return;
	}

	json_object *text = json_object_object_get(holder, "txt");
	if (text == NULL) {
		json_object_put(jobj);
		return;
	}

	int i;
	int length = json_object_array_length(text);

	for (i = 0; i < length; i++) {
		json_object *elem = json_object_array_get_idx(text, i);
		char record[512] = {0};

		snprintf(record, sizeof(record), "%s", json_object_get_string(elem));
		if (DM_STRLEN(record) == 0)
			continue;

		char *tmp = strchr(record, '=');
		if (tmp) {
			*tmp = '\0';
			tmp = tmp + 1;
		}

		struct uci_section *txt_sec = NULL;
		char txt_sname[125] = {0};

		snprintf(txt_sname, sizeof(txt_sname), "%s_text_%d", par_sec, i);

		dmuci_add_section("dnssd", "text_record", &txt_sec);
		dmuci_rename_section_by_section(txt_sec, txt_sname);
		dmuci_set_value_by_section(txt_sec, "parent", par_sec);
		dmuci_set_value_by_section(txt_sec, "key", record);
		dmuci_set_value_by_section(txt_sec, "key_value", tmp);
	}

	json_object_put(jobj);
}

static void synchronize_dns_sd_advertisements(struct list_head *dup_list, int browse_type)
{
	DIR *dir = opendir("/etc/umdns/");
	if (dir == NULL) {
		goto next;
	}

	struct dirent *entry;
	while ((entry = readdir(dir)) != NULL) {
		if (entry->d_type != DT_REG)
			continue;

		// Process only JSON files
		char *ext = strrchr(entry->d_name, '.');
		if (ext == NULL)
			continue;

		if (strcmp(ext, ".json") != 0)
			continue;

		char sect_name[256] = {0};
		snprintf(sect_name, sizeof(sect_name), "%s", entry->d_name);
		ext = strrchr(sect_name, '.');
		if (ext) {
			*ext = '\0';
		}

		replace_special_char(sect_name, '_');

		bool instance_found = false;
		struct uci_section *s = NULL;
		uci_foreach_sections("dnssd", "advertise", s) {
			if (strcmp(section_name(s), sect_name) == 0) {
				instance_found = true;
				break;
			}
		}

		if (!instance_found) {
			// create entry for this
			struct uci_section *conf_s = NULL;
			char file_path[256 + 16] = {0};
			snprintf(file_path, sizeof(file_path), "/etc/umdns/%s", entry->d_name);
			char *instance_name = get_instance_name(file_path);
			if (DM_STRLEN(instance_name) == 0) {
				continue;
			}

			dmuci_add_section("dnssd", "advertise", &conf_s);
			dmuci_rename_section_by_section(conf_s, sect_name);
			dmuci_set_value_by_section(conf_s, "file", entry->d_name);
			dmuci_set_value_by_section(conf_s, "enable", "1");
			dmuci_set_value_by_section(conf_s, "instance_name", instance_name);

			// Add text records
			add_dns_sd_text_records(file_path, instance_name, sect_name);

			FREE(instance_name);
		}
	}

	closedir(dir);
next:
	synchronize_config_sections_with_dmmap_sections("dnssd", "advertise", "dmmap_dnssd", dup_list, browse_type);
}

static int browseDNSSDAdvertiseInst(struct dmctx *dmctx, DMNODE *parent_node, void *prev_data, char *prev_instance)
{
	char *inst = NULL;
	struct dm_data *curr_data = NULL;
	LIST_HEAD(dup_list);

	synchronize_dns_sd_advertisements(&dup_list, parent_node->browse_type);
	list_for_each_entry(curr_data, &dup_list, list) {
		inst = handle_instance(dmctx, parent_node, curr_data->dmmap_section, "adv_instance", "adv_alias");
		if (DM_LINK_INST_OBJ(dmctx, parent_node, curr_data, inst) == DM_STOP)
			break;
	}

	free_dmmap_config_dup_list(&dup_list);
	return 0;
}

static int get_dns_sd_advertise_number_of_entries(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	int cnt = get_number_of_entries(ctx, data, instance, browseDNSSDAdvertiseInst);
	dmasprintf(value, "%d", cnt);

	return 0;
}

static int get_dns_sd_advertise_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	return bbf_get_alias(ctx, ((struct dm_data *)data)->dmmap_section, "adv_alias", instance, value);
}

static int set_dns_sd_advertise_alias(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	return bbf_set_alias(ctx, ((struct dm_data *)data)->dmmap_section, "adv_alias", instance, value);
}

static int get_dns_sd_advertise_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "enable", value);
	return 0;
}

static int set_dns_sd_advertise_enable(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *fname = NULL, *enable = NULL;
	char dest_path[256] = {0};
	char curr_path[256] = {0};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_boolean(ctx, value))
				return FAULT_9007;
			break;
		case VALUESET:
			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "file", &fname);
			if (strcmp(fname, "") == 0)
				return 0;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "enable", &enable);
			bool cur_enab = dmuci_string_to_boolean(enable);
			bool new_enab = dmuci_string_to_boolean(value);

			snprintf(curr_path, sizeof(curr_path), "%s%s", cur_enab ? "/etc/umdns/" : "/etc/umdns/tmp/", fname);
			snprintf(dest_path, sizeof(dest_path), "%s%s", new_enab ? "/etc/umdns/" : "/etc/umdns/tmp/", fname);

			if (!file_exists(curr_path))
				return FAULT_9002;

			if (rename(curr_path, dest_path) != 0)
				return FAULT_9002;

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "enable", value);

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

static int get_dns_sd_advertise_status(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *enabled = NULL;
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "enable", &enabled);

	bool enable = dmuci_string_to_boolean(enabled);
	*value = enable ? dmstrdup("Enabled") : dmstrdup("Disabled");

	return 0;
}

static int get_dns_sd_advertise_interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct uci_list *net_list = NULL;

	dmuci_get_option_value_list("umdns", "@umdns[0]", "network", &net_list);

	if (net_list != NULL) {
		struct uci_element *e = NULL;
		char iface[256] = {0};

		uci_foreach_element(net_list, e) {
			bbfdm_get_references(ctx, MATCH_FIRST, "Device.IP.Interface.", "Name", e->name, iface, sizeof(iface));
		}

		*value = dmstrdup(iface);
		return 0;
	}

	*value = dmstrdup("");
	return 0;
}

static int set_dns_sd_advertise_interface(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string_list(ctx, value, -1, -1, 1024, -1, -1, NULL, NULL))
				return FAULT_9007;

			if (*value != '\0')
				return FAULT_9007;

			break;
		case VALUESET:
			break;
	}
	return 0;
}

static int get_dns_sd_advertise_instance_name(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "instance_name", value);
	return 0;
}

static int set_dns_sd_advertise_instance_name(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *cur_name = NULL;
	char file_path[256] = {0};

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 63, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			if (get_dns_sd_advertise_filepath(((struct dm_data *)data)->config_section, file_path, sizeof(file_path)) != 0)
				return FAULT_9002;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "instance_name", &cur_name);
			if (DM_STRLEN(cur_name) == 0)
				return FAULT_9002;

			json_object *jobj = json_object_from_file(file_path);
			if (jobj == NULL)
				return FAULT_9002;

			json_object *inst_val = json_object_object_get(jobj, cur_name);
			if (inst_val == NULL) {
				json_object_put(jobj);
				return FAULT_9002;
			}

			json_object *new_obj = json_object_new_object();
			json_object_object_add(new_obj, value, inst_val);

			if (json_object_to_file(file_path, new_obj) != 0) {
				json_object_put(jobj);
				json_object_put(new_obj);
				return FAULT_9002;
			}

			json_object_put(jobj);
			json_object_put(new_obj);

			dmuci_set_value_by_section(((struct dm_data *)data)->config_section, "instance_name", value);

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

static int set_dns_sd_service_info(struct uci_section *s, const char *new_service)
{
	char file_path[256] = {0};
	char *cur_name = NULL;

	if (s == NULL || new_service == NULL)
		return -1;

	if (get_dns_sd_advertise_filepath(s, file_path, sizeof(file_path)) != 0)
		return -1;

	dmuci_get_value_by_section_string(s, "instance_name", &cur_name);
	if (DM_STRLEN(cur_name) == 0)
		return -1;

	json_object *jobj = json_object_from_file(file_path);
	if (jobj == NULL)
		return -1;

	json_object *inst_val = json_object_object_get(jobj, cur_name);
	if (inst_val == NULL) {
		json_object_put(jobj);
		return -1;
	}

	json_object *option = json_object_object_get(inst_val, "service");
	if (option == NULL) {
		json_object_object_add(inst_val, "service", json_object_new_string(new_service));
	} else {
		json_object_set_string(option, new_service);
	}

	if (json_object_to_file(file_path, jobj) != 0) {
		json_object_put(jobj);
		return -1;
	}

	json_object_put(jobj);
	return 0;
}

static void get_dns_sd_service_info(struct uci_section *s, struct dns_sd_servinfo *serv_info)
{
	char *cur_name = NULL;
	char file_path[256] = {0};
	json_object *jobj = NULL;

	if (s == NULL || serv_info == NULL)
		return;

	if (get_dns_sd_advertise_filepath(s, file_path, sizeof(file_path)) != 0)
		return;

	dmuci_get_value_by_section_string(s, "instance_name", &cur_name);
	if (DM_STRLEN(cur_name) == 0)
		return;

	jobj = json_object_from_file(file_path);
	if (jobj == NULL)
		return;

	json_object *inst_val = json_object_object_get(jobj, cur_name);
	if (inst_val == NULL) {
		json_object_put(jobj);
		return;
	}

	json_object *service = json_object_object_get(inst_val, "service");
	if (service == NULL) {
		json_object_put(jobj);
		return;
	}

	char *service_str = dmstrdup(json_object_get_string(service));
	json_object_put(jobj);

	if (DM_STRLEN(service_str) == 0)
		return;

	char *token = strtok(service_str, ".");
	if (token && token[0] == '_')
		DM_STRNCPY(serv_info->app_proto, token + 1, sizeof(serv_info->app_proto));

	token = strtok(NULL, ".");
	if (token && token[0] == '_')
		DM_STRNCPY(serv_info->trans_proto, token + 1, sizeof(serv_info->trans_proto));

	token = strtok(NULL, ".");
	DM_STRNCPY(serv_info->domain, token ? token : "local", sizeof(serv_info->domain));
}

static int get_dns_sd_advertise_application_protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dns_sd_servinfo serv_info;

	memset(&serv_info, 0, sizeof(struct dns_sd_servinfo));
	get_dns_sd_service_info(((struct dm_data *)data)->config_section, &serv_info);

	*value = dmstrdup(serv_info.app_proto);
	return 0;
}

static int set_dns_sd_advertise_application_protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct dns_sd_servinfo serv_info;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 15, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			memset(&serv_info, 0, sizeof(struct dns_sd_servinfo));
			get_dns_sd_service_info(((struct dm_data *)data)->config_section, &serv_info);

			char new_service[512] = {0};
			snprintf(new_service, sizeof(new_service), "_%s._%s.%s",
			         value,
			         serv_info.trans_proto,
			         DM_STRLEN(serv_info.domain) ? serv_info.domain : "local");

			if (set_dns_sd_service_info(((struct dm_data *)data)->config_section, new_service) != 0)
				return FAULT_9002;

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

static int get_dns_sd_advertise_transport_protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dns_sd_servinfo serv_info;

	memset(&serv_info, 0, sizeof(struct dns_sd_servinfo));
	get_dns_sd_service_info(((struct dm_data *)data)->config_section, &serv_info);

	if (DM_STRCMP(serv_info.trans_proto, "tcp") == 0)
		*value = dmstrdup("TCP");
	else if (DM_STRCMP(serv_info.trans_proto, "udp") == 0)
		*value = dmstrdup("UDP");

	return 0;
}

static int set_dns_sd_advertise_transport_protocol(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char *adv_trans_proto[] = {"TCP", "UDP", NULL};
	struct dns_sd_servinfo serv_info;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, -1, adv_trans_proto, NULL))
				return FAULT_9007;
			break;
		case VALUESET:
			memset(&serv_info, 0, sizeof(struct dns_sd_servinfo));
			get_dns_sd_service_info(((struct dm_data *)data)->config_section, &serv_info);

			char new_service[512] = {0};
			snprintf(new_service, sizeof(new_service), "_%s._%s.%s",
			         serv_info.app_proto,
			         strcmp(value, "TCP") == 0 ? "tcp" : "udp",
			         DM_STRLEN(serv_info.domain) ? serv_info.domain : "local");

			if (set_dns_sd_service_info(((struct dm_data *)data)->config_section, new_service) != 0)
				return FAULT_9002;

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

static int get_dns_sd_advertise_domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	struct dns_sd_servinfo serv_info;

	memset(&serv_info, 0, sizeof(struct dns_sd_servinfo));
	get_dns_sd_service_info(((struct dm_data *)data)->config_section, &serv_info);

	*value = dns_domain_ensure_dot(DM_STRLEN(serv_info.domain) ? serv_info.domain : "local");
	return 0;
}

static int set_dns_sd_advertise_domain(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	struct dns_sd_servinfo serv_info;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_string(ctx, value, -1, 256, NULL, NULL))
				return FAULT_9007;
			break;
		case VALUESET: {
			char domain[257] = {0};
			size_t dlen;

			strncpy(domain, DM_STRLEN(value) ? value : "local", sizeof(domain) - 1);
			dlen = strlen(domain);
			if (dlen > 0 && domain[dlen - 1] == '.')
				domain[dlen - 1] = '\0';

			memset(&serv_info, 0, sizeof(struct dns_sd_servinfo));
			get_dns_sd_service_info(((struct dm_data *)data)->config_section, &serv_info);

			char new_service[512] = {0};
			snprintf(new_service, sizeof(new_service), "_%s._%s.%s",
			         serv_info.app_proto,
			         serv_info.trans_proto,
			         domain);

			if (set_dns_sd_service_info(((struct dm_data *)data)->config_section, new_service) != 0)
				return FAULT_9002;

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
		}
	}

	return 0;
}

static int get_dns_sd_advertise_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char **value)
{
	char *cur_name = NULL;
	char file_path[256] = {0};

	if (get_dns_sd_advertise_filepath(((struct dm_data *)data)->config_section, file_path, sizeof(file_path)) != 0)
		return 0;

	dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "instance_name", &cur_name);
	if (DM_STRLEN(cur_name) == 0)
		return 0;

	json_object *jobj = json_object_from_file(file_path);
	if (jobj == NULL)
		return 0;

	json_object *inst_val = json_object_object_get(jobj, cur_name);
	if (inst_val == NULL) {
		json_object_put(jobj);
		return 0;
	}

	json_object *port = json_object_object_get(inst_val, "port");
	if (port == NULL) {
		json_object_put(jobj);
		return 0;
	}

	char *port_str = dmstrdup(json_object_get_string(port));
	json_object_put(jobj);

	*value = port_str;
	return 0;
}

static int set_dns_sd_advertise_port(char *refparam, struct dmctx *ctx, void *data, char *instance, char *value, int action)
{
	char file_path[256] = {0};
	char *cur_name = NULL;
	char *endptr = NULL;
	int num = 0;

	switch (action) {
		case VALUECHECK:
			if (bbfdm_validate_unsignedInt(ctx, value, RANGE_ARGS{{"0","65535"}}, 1))
				return FAULT_9007;
			break;
		case VALUESET:
			num = (int)strtol(value, &endptr, 10);
			if (*endptr != '\0')
				return FAULT_9007;

			if (get_dns_sd_advertise_filepath(((struct dm_data *)data)->config_section, file_path, sizeof(file_path)) != 0)
				return FAULT_9002;

			dmuci_get_value_by_section_string(((struct dm_data *)data)->config_section, "instance_name", &cur_name);
			if (DM_STRLEN(cur_name) == 0)
				return FAULT_9002;

			json_object *jobj = json_object_from_file(file_path);
			if (jobj == NULL)
				return FAULT_9002;

			json_object *inst_val = json_object_object_get(jobj, cur_name);
			if (inst_val == NULL) {
				json_object_put(jobj);
				return FAULT_9002;
			}

			json_object *option = json_object_object_get(inst_val, "port");
			if (option == NULL) {
				json_object_object_add(inst_val, "port", json_object_new_int(num));
			} else {
				json_object_set_int(option, num);
			}

			if (json_object_to_file(file_path, jobj) != 0) {
				json_object_put(jobj);
				return FAULT_9002;
			}

			json_object_put(jobj);

			// reload umdns
			dmubus_call_set("umdns", "reload", UBUS_ARGS{0}, 0);
			break;
	}

	return 0;
}

/**********************************************************************************************************************************
*                                            OBJ & LEAF DEFINITION
***********************************************************************************************************************************/
/* *** Device.DNS.SD. *** */
DMOBJ tDNSPluginObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"SD", &DMREAD, NULL, NULL, NULL, NULL, NULL, NULL, tDNSSDObj, tDNSSDParams, NULL, BBFDM_BOTH, NULL},
{0}
};

/* *** Device.DNS.SD. *** */
DMOBJ tDNSSDObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"Service", &DMREAD, NULL, NULL, NULL, browseDNSSDServiceInst, NULL, NULL, tDNSSDServiceObj, tDNSSDServiceParams, NULL, BBFDM_BOTH, NULL},
{"Advertise", &DMWRITE, addObjDNSSDAdvertise, delObjDNSSDAdvertise, NULL, browseDNSSDAdvertiseInst, NULL, NULL, tDNSSDAdvertiseObj, tDNSSDAdvertiseParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDNSSDAdvertiseParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"Enable", &DMWRITE, DMT_BOOL, get_dns_sd_advertise_enable, set_dns_sd_advertise_enable, BBFDM_BOTH},
{"Alias", &DMWRITE, DMT_STRING, get_dns_sd_advertise_alias, set_dns_sd_advertise_alias, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Status", &DMREAD, DMT_STRING, get_dns_sd_advertise_status, NULL, BBFDM_BOTH},
{"Interface", &DMWRITE, DMT_STRING, get_dns_sd_advertise_interface, set_dns_sd_advertise_interface, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"InstanceName", &DMWRITE, DMT_STRING, get_dns_sd_advertise_instance_name, set_dns_sd_advertise_instance_name, BBFDM_BOTH},
{"ApplicationProtocol", &DMWRITE, DMT_STRING, get_dns_sd_advertise_application_protocol, set_dns_sd_advertise_application_protocol, BBFDM_BOTH},
{"TransportProtocol", &DMWRITE, DMT_STRING, get_dns_sd_advertise_transport_protocol, set_dns_sd_advertise_transport_protocol, BBFDM_BOTH},
{"Domain", &DMWRITE, DMT_STRING, get_dns_sd_advertise_domain, set_dns_sd_advertise_domain, BBFDM_BOTH},
{"Port", &DMWRITE, DMT_UNINT, get_dns_sd_advertise_port, set_dns_sd_advertise_port, BBFDM_BOTH},
{"TextRecordNumberOfEntries", &DMREAD, DMT_UNINT, get_dns_sd_advertise_text_record_number_of_entries, NULL, BBFDM_BOTH},
{0}
};

DMOBJ tDNSSDAdvertiseObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys */
{"TextRecord", &DMWRITE, addObjDNSSDAdvertiseTextRecord, delObjDNSSDAdvertiseTextRecord, NULL, browseDNSSDAdvertiseTextRecordInst, NULL, NULL, NULL, tDNSSDAdvertiseTextRecordParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDNSSDAdvertiseTextRecordParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type */
{"Key", &DMWRITE, DMT_STRING, get_dns_sd_advertise_text_record_key, set_dns_sd_advertise_text_record_key, BBFDM_BOTH},
{"Value", &DMWRITE, DMT_STRING, get_dns_sd_advertise_text_record_value, set_dns_sd_advertise_text_record_value, BBFDM_BOTH},
{0}
};

DMLEAF tDNSSDParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Enable", &DMWRITE, DMT_BOOL, get_dns_sd_enable, set_dns_sd_enable, BBFDM_BOTH},
{"ServiceNumberOfEntries", &DMREAD, DMT_UNINT, get_dns_sd_service_number_of_entries, NULL, BBFDM_BOTH},
{"AdvertiseNumberOfEntries", &DMREAD, DMT_UNINT, get_dns_sd_advertise_number_of_entries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DNS.SD.Service.{i}. *** */
DMOBJ tDNSSDServiceObj[] = {
/* OBJ, permission, addobj, delobj, checkdep, browseinstobj, nextdynamicobj, dynamicleaf, nextobj, leaf, linker, bbfdm_type, uniqueKeys, version*/
{"TextRecord", &DMREAD, NULL, NULL, NULL, browseDNSSDServiceTextRecordInst, NULL, NULL, NULL, tDNSSDServiceTextRecordParams, NULL, BBFDM_BOTH, NULL},
{0}
};

DMLEAF tDNSSDServiceParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"InstanceName", &DMREAD, DMT_STRING, get_dns_sd_service_instance_name, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"ApplicationProtocol", &DMREAD, DMT_STRING, get_dns_sd_service_application_protocol, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"TransportProtocol", &DMREAD, DMT_STRING, get_dns_sd_service_transport_protocol, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Domain", &DMREAD, DMT_STRING, get_dns_sd_service_domain, NULL, BBFDM_BOTH, DM_FLAG_UNIQUE},
{"Port", &DMREAD, DMT_UNINT, get_dns_sd_service_port, NULL, BBFDM_BOTH},
{"Target", &DMREAD, DMT_STRING, get_dns_sd_service_target, NULL, BBFDM_BOTH},
{"Status", &DMREAD, DMT_STRING, get_dns_sd_service_status, NULL, BBFDM_BOTH},
{"LastUpdate", &DMREAD, DMT_TIME, get_dns_sd_service_last_update, NULL, BBFDM_BOTH},
{"Host", &DMREAD, DMT_STRING, get_dns_sd_service_host, NULL, BBFDM_BOTH, DM_FLAG_REFERENCE},
{"TimeToLive", &DMREAD, DMT_UNINT, get_dns_sd_service_time_to_live, NULL, BBFDM_BOTH},
{"Priority", &DMREAD, DMT_UNINT, get_dns_sd_service_priority, NULL, BBFDM_BOTH},
{"Weight", &DMREAD, DMT_UNINT, get_dns_sd_service_weight, NULL, BBFDM_BOTH},
{"TextRecordNumberOfEntries", &DMREAD, DMT_UNINT, get_dns_sd_service_text_record_number_of_entries, NULL, BBFDM_BOTH},
{0}
};

/* *** Device.DNS.SD.Service.{i}.TextRecord.{i}. *** */
DMLEAF tDNSSDServiceTextRecordParams[] = {
/* PARAM, permission, type, getvalue, setvalue, bbfdm_type, version*/
{"Key", &DMREAD, DMT_STRING, get_dns_sd_service_text_record_key, NULL, BBFDM_BOTH},
{"Value", &DMREAD, DMT_STRING, get_dns_sd_service_text_record_value, NULL, BBFDM_BOTH},
{0}
};

DM_MAP_OBJ tDynamicObj[] = {
/* parentobj, nextobject, parameter */
{"Device.DNS.", tDNSPluginObj, NULL},
{0}
};
