e.g. <literal>path=/portal/index.html</literal>. Keys and values can contain C-style escape
sequences which get translated upon reading configuration files.
</para>
+ <para>This option together with <varname>TxtData=</varname> may be specified more than once, in which
+ case multiple TXT resource records will be created for the service. If the empty string is assigned to
+ this option, the list is reset and all prior assignments will have no effect.
+ </para>
</listitem>
</varlistentry>
<varlistentry>
e.g. <literal>data=YW55IGJpbmFyeSBkYXRhCg==</literal>. Keys can contain C-style escape
sequences which get translated upon reading configuration files.
</para>
+ <para>This option together with <varname>TxtText=</varname> may be specified more than once, in which
+ case multiple TXT resource records will be created for the service. If the empty string is assigned to
+ this option, the list is reset and all prior assignments will have no effect.
+ </para>
</listitem>
</varlistentry>
</variablelist>
_cleanup_free_ char *instance_name = NULL;
Manager *m = userdata;
DnssdService *s = NULL;
- DnsTxtItem *last = NULL;
const char *name;
const char *name_template;
const char *type;
if (r < 0)
return r;
- r = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "{say}");
+ r = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "a{say}");
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
- while ((r = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "say")) > 0) {
- const char *key;
- const void *value;
- size_t size;
- DnsTxtItem *i;
+ while ((r = sd_bus_message_enter_container(message, SD_BUS_TYPE_ARRAY, "{say}")) > 0) {
+ _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
+ DnsTxtItem *last = NULL;
- r = sd_bus_message_read(message, "s", &key);
- if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ txt_data = new0(DnssdTxtData, 1);
+ if (!txt_data)
+ return log_oom();
+
+ while ((r = sd_bus_message_enter_container(message, SD_BUS_TYPE_DICT_ENTRY, "say")) > 0) {
+ const char *key;
+ const void *value;
+ size_t size;
+ DnsTxtItem *i;
+
+ r = sd_bus_message_read(message, "s", &key);
+ if (r < 0)
+ return r;
+
+ if (isempty(key))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Keys in DNS-SD TXT RRs can't be empty");
- if (strlen_ptr(key) == 0)
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "Keys in DNS-SD TXT RRs can't be empty");
+ if (!ascii_is_valid(key))
+ return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TXT key '%s' contains non-ASCII symbols", key);
- if (!ascii_is_valid(key))
- return sd_bus_error_setf(error, SD_BUS_ERROR_INVALID_ARGS, "TXT key '%s' contains non-ASCII symbols", key);
+ r = sd_bus_message_read_array(message, 'y', &value, &size);
+ if (r < 0)
+ return r;
- r = sd_bus_message_read_array(message, 'y', &value, &size);
+ r = dnssd_txt_item_new_from_data(key, value, size, &i);
+ if (r < 0)
+ return r;
+
+ LIST_INSERT_AFTER(items, txt_data->txt, last, i);
+ last = i;
+
+ r = sd_bus_message_exit_container(message);
+ if (r < 0)
+ return r;
+
+ }
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
- r = dnssd_txt_item_new_from_data(key, value, size, &i);
+ r = sd_bus_message_exit_container(message);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
- LIST_INSERT_AFTER(items, service->txt, last, i);
- last = i;
+ if (txt_data->txt) {
+ LIST_PREPEND(items, service->txt_data_items, txt_data);
+ txt_data = NULL;
+ }
}
+ if (r < 0)
+ return r;
r = sd_bus_message_exit_container(message);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
+
+ if (!service->txt_data_items) {
+ _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
- if (!service->txt) {
- r = dns_txt_item_new_empty(&service->txt);
+ txt_data = new0(DnssdTxtData, 1);
+ if (!txt_data)
+ return log_oom();
+
+ r = dns_txt_item_new_empty(&txt_data->txt);
if (r < 0)
- return sd_bus_error_set_errno(error, r);
+ return r;
+
+ LIST_PREPEND(items, service->txt_data_items, txt_data);
+ txt_data = NULL;
}
r = sd_bus_path_encode("/org/freedesktop/resolve1/dnssd", service->name, &path);
SD_BUS_METHOD("SetLinkDNSSECNegativeTrustAnchors", "ias", NULL, bus_method_set_link_dnssec_negative_trust_anchors, 0),
SD_BUS_METHOD("RevertLink", "i", NULL, bus_method_revert_link, 0),
- SD_BUS_METHOD("RegisterService", "sssqqqa{say}", "o", bus_method_register_service, SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("RegisterService", "sssqqqaa{say}", "o", bus_method_register_service, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_METHOD("UnregisterService", "o", NULL, bus_method_unregister_service, SD_BUS_VTABLE_UNPRIVILEGED),
SD_BUS_VTABLE_END,
};
}
int config_parse_dnssd_txt(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata) {
+ _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
DnssdService *s = userdata;
DnsTxtItem *last = NULL;
assert(rvalue);
assert(s);
- /* TODO: Since RFC6763 allows more than one TXT RR per service
- * this s->txt field should be implemented as a list
- * of DnsTxtItem lists. */
- s->txt = dns_txt_item_free_all(s->txt);
-
- if (isempty(rvalue))
+ if (isempty(rvalue)) {
+ /* Flush out collected items */
+ s->txt_data_items = dnssd_txtdata_free_all(s->txt_data_items);
return 0;
+ }
+
+ txt_data = new0(DnssdTxtData, 1);
+ if (!txt_data)
+ return log_oom();
for (;;) {
_cleanup_free_ char *word = NULL;
assert_not_reached("Unknown type of Txt config");
}
- LIST_INSERT_AFTER(items, s->txt, last, i);
+ LIST_INSERT_AFTER(items, txt_data->txt, last, i);
last = i;
}
+ if (!LIST_IS_EMPTY(txt_data->txt)) {
+ LIST_PREPEND(items, s->txt_data_items, txt_data);
+ txt_data = NULL;
+ }
+
return 0;
}
_cleanup_(dns_packet_unrefp) DnsPacket *p = NULL;
_cleanup_set_free_ Set *types = NULL;
DnsTransaction *t;
- DnsZoneItem *z;
+ DnsZoneItem *z, *i;
unsigned size = 0;
Iterator iterator;
char *service_type;
}
}
- size++;
+ LIST_FOREACH(by_key, i, z)
+ size++;
}
answer = dns_answer_new(size + set_size(types));
return log_oom();
/* Second iteration, actually add RRs to the answer. */
- HASHMAP_FOREACH(z, scope->zone.by_key, iterator) {
- DnsAnswerFlags flags;
+ HASHMAP_FOREACH(z, scope->zone.by_key, iterator)
+ LIST_FOREACH (by_key, i, z) {
+ DnsAnswerFlags flags;
- if (z->state != DNS_ZONE_ITEM_ESTABLISHED)
- continue;
+ if (i->state != DNS_ZONE_ITEM_ESTABLISHED)
+ continue;
- if (dns_resource_key_is_dnssd_ptr(z->rr->key))
- flags = goodbye ? DNS_ANSWER_GOODBYE : 0;
- else
- flags = goodbye ? (DNS_ANSWER_GOODBYE|DNS_ANSWER_CACHE_FLUSH) : DNS_ANSWER_CACHE_FLUSH;
+ if (dns_resource_key_is_dnssd_ptr(i->rr->key))
+ flags = goodbye ? DNS_ANSWER_GOODBYE : 0;
+ else
+ flags = goodbye ? (DNS_ANSWER_GOODBYE|DNS_ANSWER_CACHE_FLUSH) : DNS_ANSWER_CACHE_FLUSH;
- r = dns_answer_add(answer, z->rr, 0 , flags);
- if (r < 0)
- return log_debug_errno(r, "Failed to add RR to announce: %m");
- }
+ r = dns_answer_add(answer, i->rr, 0 , flags);
+ if (r < 0)
+ return log_debug_errno(r, "Failed to add RR to announce: %m");
+ }
/* Since all the active services are in the zone make them discoverable now. */
SET_FOREACH(service_type, types, iterator) {
int dns_scope_add_dnssd_services(DnsScope *scope) {
Iterator i;
DnssdService *service;
+ DnssdTxtData *txt_data;
int r;
assert(scope);
if (r < 0)
log_warning_errno(r, "Failed to add SRV record to MDNS zone: %m");
- r = dns_zone_put(&scope->zone, scope, service->txt_rr, true);
- if (r < 0)
- log_warning_errno(r, "Failed to add TXT record to MDNS zone: %m");
+ LIST_FOREACH(items, txt_data, service->txt_data_items) {
+ r = dns_zone_put(&scope->zone, scope, txt_data->rr, true);
+ if (r < 0)
+ log_warning_errno(r, "Failed to add TXT record to MDNS zone: %m");
+ }
}
return 0;
_cleanup_(dns_resource_key_unrefp) DnsResourceKey *key = NULL;
Iterator i;
DnssdService *service;
+ DnssdTxtData *txt_data;
int r;
assert(scope);
HASHMAP_FOREACH(service, scope->manager->dnssd_services, i) {
dns_zone_remove_rr(&scope->zone, service->ptr_rr);
dns_zone_remove_rr(&scope->zone, service->srv_rr);
- dns_zone_remove_rr(&scope->zone, service->txt_rr);
+ LIST_FOREACH(items, txt_data, service->txt_data_items)
+ dns_zone_remove_rr(&scope->zone, txt_data->rr);
}
return 0;
int bus_dnssd_method_unregister(sd_bus_message *message, void *userdata, sd_bus_error *error) {
DnssdService *s = userdata;
+ DnssdTxtData *txt_data;
Manager *m;
Iterator i;
Link *l;
dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, s->ptr_rr);
dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, s->srv_rr);
- dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, s->txt_rr);
+ LIST_FOREACH(items, txt_data, s->txt_data_items)
+ dns_zone_remove_rr(&l->mdns_ipv4_scope->zone, txt_data->rr);
}
if (l->mdns_ipv6_scope) {
dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, s->ptr_rr);
dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, s->srv_rr);
- dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, s->txt_rr);
+ LIST_FOREACH(items, txt_data, s->txt_data_items)
+ dns_zone_remove_rr(&l->mdns_ipv6_scope->zone, txt_data->rr);
}
}
NULL
};
+DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data) {
+ if (!txt_data)
+ return NULL;
+
+ dns_resource_record_unref(txt_data->rr);
+ dns_txt_item_free_all(txt_data->txt);
+
+ return mfree(txt_data);
+}
+
+DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data) {
+ DnssdTxtData *next;
+
+ if (!txt_data)
+ return NULL;
+
+ next = txt_data->items_next;
+
+ dnssd_txtdata_free(txt_data);
+
+ return dnssd_txtdata_free_all(next);
+}
+
DnssdService *dnssd_service_free(DnssdService *service) {
if (!service)
return NULL;
dns_resource_record_unref(service->ptr_rr);
dns_resource_record_unref(service->srv_rr);
- dns_resource_record_unref(service->txt_rr);
+
+ dnssd_txtdata_free_all(service->txt_data_items);
free(service->filename);
free(service->name);
free(service->type);
free(service->name_template);
- dns_txt_item_free_all(service->txt);
return mfree(service);
}
static int dnssd_service_load(Manager *manager, const char *filename) {
_cleanup_(dnssd_service_freep) DnssdService *service = NULL;
+ _cleanup_(dnssd_txtdata_freep) DnssdTxtData *txt_data = NULL;
char *d;
const char *dropin_dirname;
int r;
return -EINVAL;
}
- if (!service->txt) {
- r = dns_txt_item_new_empty(&service->txt);
+ if (LIST_IS_EMPTY(service->txt_data_items)) {
+ txt_data = new0(DnssdTxtData, 1);
+ if (!txt_data)
+ return log_oom();
+
+ r = dns_txt_item_new_empty(&txt_data->txt);
if (r < 0)
return r;
+
+ LIST_PREPEND(items, service->txt_data_items, txt_data);
+ txt_data = NULL;
}
r = hashmap_ensure_allocated(&manager->dnssd_services, &string_hash_ops);
_cleanup_free_ char *n = NULL;
_cleanup_free_ char *service_name = NULL;
_cleanup_free_ char *full_name = NULL;
+ DnssdTxtData *txt_data;
int r;
assert(s);
- assert(s->txt);
+ assert(s->txt_data_items);
assert(s->manager);
s->ptr_rr = dns_resource_record_unref(s->ptr_rr);
s->srv_rr = dns_resource_record_unref(s->srv_rr);
- s->txt_rr = dns_resource_record_unref(s->txt_rr);
+ LIST_FOREACH(items, txt_data, s->txt_data_items)
+ txt_data->rr = dns_resource_record_unref(txt_data->rr);
r = dnssd_render_instance_name(s, &n);
if (r < 0)
if (r < 0)
return r;
- s->txt_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_TXT,
- full_name);
- if (!s->txt_rr)
- goto oom;
+ LIST_FOREACH(items, txt_data, s->txt_data_items) {
+ txt_data->rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_TXT,
+ full_name);
+ if (!txt_data->rr)
+ goto oom;
- s->txt_rr->ttl = MDNS_DEFAULT_TTL;
- s->txt_rr->txt.items = dns_txt_item_copy(s->txt);
- if (!s->txt_rr->txt.items)
- goto oom;
+ txt_data->rr->ttl = MDNS_DEFAULT_TTL;
+ txt_data->rr->txt.items = dns_txt_item_copy(txt_data->txt);
+ if (!txt_data->rr->txt.items)
+ goto oom;
+ }
s->ptr_rr = dns_resource_record_new_full(DNS_CLASS_IN, DNS_TYPE_PTR,
service_name);
return 0;
oom:
- s->txt_rr = dns_resource_record_unref(s->txt_rr);
+ LIST_FOREACH(items, txt_data, s->txt_data_items)
+ txt_data->rr = dns_resource_record_unref(txt_data->rr);
s->ptr_rr = dns_resource_record_unref(s->ptr_rr);
s->srv_rr = dns_resource_record_unref(s->srv_rr);
return -ENOMEM;
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
+#include "list.h"
+
typedef struct DnssdService DnssdService;
+typedef struct DnssdTxtData DnssdTxtData;
typedef struct Manager Manager;
typedef struct DnsResourceRecord DnsResourceRecord;
DNS_TXT_ITEM_DATA
};
+struct DnssdTxtData {
+ DnsResourceRecord *rr;
+
+ LIST_HEAD(DnsTxtItem, txt);
+
+ LIST_FIELDS(DnssdTxtData, items);
+};
+
struct DnssdService {
char *filename;
char *name;
uint16_t port;
uint16_t priority;
uint16_t weight;
- DnsTxtItem *txt;
DnsResourceRecord *ptr_rr;
DnsResourceRecord *srv_rr;
- DnsResourceRecord *txt_rr;
+
+ /* Section 6.8 of RFC 6763 allows having service
+ * instances with multiple TXT resource records. */
+ LIST_HEAD(DnssdTxtData, txt_data_items);
Manager *manager;
};
DnssdService *dnssd_service_free(DnssdService *service);
+DnssdTxtData *dnssd_txtdata_free(DnssdTxtData *txt_data);
+DnssdTxtData *dnssd_txtdata_free_all(DnssdTxtData *txt_data);
DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdService*, dnssd_service_free);
+DEFINE_TRIVIAL_CLEANUP_FUNC(DnssdTxtData*, dnssd_txtdata_free);
int dnssd_render_instance_name(DnssdService *s, char **ret_name);
int dnssd_load(Manager *manager);