avahi_entry_group_add_record的用法

avahi_entry_group_add_record 在头文件注释及官方文档中没有任何的说明,如果想使用该接口发送自定义的消息会一点头疼,这里通过分析avahi源码来介绍一下使用方法,下面的内容需要对mDNS以及DNS-SD协议有一定程度的了解。

一、代码分析

# avahi-client/publish.h  实现在 avahi-client/entrygroup.c

client与daemon之间通过dbus通信

avahi_entry_group_add_record()
	dbus_message_new_method_call(..., "AddRecord")// 注意此处传递的method参数
	dbus_message_append_args()
	dbus_connection_send_with_reply_and_block()// 通过dbus发送消息给avahi-daemon

# avahi-daemon/dbus-entry-group.c

client的消息发送后由daemon负责处理,通过搜索"AddRecord"定位到avahi-daemon的处理动作

	else if(dbus_message_is_method_call(..., "AddRecord")) {
		avahi_rdata_parse() {
			parse_rdata()
		}
		avahi_server_add()
	}

# avahi-core/dns.c

1. 在最终的处理函数consume_labels中,第一件事情是获取字符串的长度字节。参考 RFC6763 - DNS-SD 第6章中关于TXT记录的字符串的介绍,TXT记录允许传入多个TXT 的字符串,每一个前面使用一个1字节的长度字段来标识长度,示例:

---------------------------------------------------------------------
| 0x09 | key=value | 0x08 | paper=A4 | 0x07 | passreq  |
---------------------------------------------------------------------

2. consume_labels结束处理的条件为 if(!n),也就是检查到'\0'的字符串结束符,因此我们传入的字符串中必须带有'\0'结束符,size中也必须算上这个'\0'结束符,否则程序会在不正确的位置结束处理。

3. PTR和TXT记录的处理都比较简单,SRV需要使用自定义结构体来传递数据。

static int parse_rdata(AvahiDnsPacket *p, AvahiRecord *r, uint16_t rdlength) {
    avahi_dns_packet_consume_name() {
        switch (r->key->type) {
            case AVAHI_DNS_TYPE_PTR:
            case AVAHI_DNS_TYPE_CNAME:
            case AVAHI_DNS_TYPE_NS:
                if (avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
                    return -1;

            case AVAHI_DNS_TYPE_SRV:
                // SRV记录使用到了 AvahiRecord.data.srv 结构体
                if (avahi_dns_packet_consume_uint16(p, &r->data.srv.priority) < 0 ||
                    avahi_dns_packet_consume_uint16(p, &r->data.srv.weight) < 0 ||
                    avahi_dns_packet_consume_uint16(p, &r->data.srv.port) < 0 ||
                    avahi_dns_packet_consume_name(p, buf, sizeof(buf)) < 0)
                    return -1;

            case AVAHI_DNS_TYPE_TXT:
                if (avahi_string_list_parse(avahi_dns_packet_get_rptr(p), rdlength, &r->data.txt.string_list) < 0)
                    return -1;
        }
    }
}

static int consume_labels(AvahiDnsPacket *p, unsigned idx, char *ret_name, size_t l) {
    for (i = 0; i < AVAHI_DNS_LABELS_MAX; i++) {
        n = AVAHI_DNS_PACKET_DATA(p)[idx];     // 获取字符串长度字节,参考 RFC6763 - DNS-SD 第6章

        if(!n)
            // 结束
        else if(n <= 63)
            // 未压缩的标签,参考RFC6763 7.2节,标签长度不得大于63
        else if ((n & 0xC0) == 0xC0)
            // 压缩的标签
    }
}

二、代码示例

这里我们按照DNS-SD协议的要求对外发布一项服务:

1. 发布一条PTR记录指向SRV记录

2. 发布一条SRV记录描述服务内容

3. 发布一条TXT记录描述附加信息(可选)

4. 发布一条dns-sd PTR记录用于avahi-browser的查找

注:avahi_entry_group_add_record 第四个参数flags不能为0,使用avahi_entry_group_add_service时因为会自动添加默认的flag所以可以正常使用,但是add_record则不行,必须手动将所有使用到的flag添加上。

// 参考 avahi-core/rr.h: AvahiRecord.data.srv
typedef struct mdns_dns_srv {
    uint16_t priority;
    uint16_t weight;
    uint16_t port;
    uint16_t pad;    // 对齐64位
} mdns_dns_srv_t;


int mdns_add_service() {
    /***************************************************************/
    char full_address[] = "service1._http._tcp.local";
    char service_name[] = "_http._tcp.local";
    char service_type[] = "_http._tcp";
    char dns_sd_service_name[] = "_services._dns-sd._udp.local"
    const char *host_name = avahi_client_get_host_name(client);

    uint8_t rrlen;
    int rr_size;
    uint8_t *memptr;

    /***************************************************************/
    // 记录的格式为 (1B 字符串长度) + (字符串) + (1B '\0'字符串结束符)
    // 参考 RFC6763 - DNS-SD 第6章
    rrlen = strlen(full_address);
    rr_size = rrlen + 2;    // 1B rrlen + 1B '\0'
    memptr = malloc(rr_size);
    memcpy(memptr, &rrlen, 1);
    strcpy(memptr+1, full_address);

    // PTR 记录
    ret = avahi_entry_group_add_record(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
            AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_USE_MULTICAST,
            service_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, AVAHI_DEFAULT_TTL,
            memptr, rr_size);
    free(memptr);


    /***************************************************************/
    mdns_dns_srv_t srv;
    srv.priority = 0;
    srv.weight = 0;
    srv.port = 1234;
    srv.pad = 0;

    // 记录的内容为 (3 * uint16_t) + 一个带长度的字符串
    rrlen = strlen(host_name);
    rr_size = sizeof(uint16_t) * 3 + (strlen(host_name) + 2); // 1B rrlen + 1B '\0'
    uint8_t *srv_mem = malloc(rr_size);
    memcpy(srv_mem, &srv, sizeof(uint16_t) * 3);
    memcpy(srv_mem + sizeof(uint16_t) * 3, &rrlen, 1);
    strcpy(srv_mem + sizeof(uint16_t) * 3 + 1, host_name);

    // SRV 记录
    ret = avahi_entry_group_add_record(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
            AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_USE_MULTICAST,
            full_address, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_SRV, AVAHI_DEFAULT_TTL_HOST_NAME,
            srv_mem, rr_size);
    free(srv_mem);


    /***************************************************************/
    rrlen = 3;
    // (2 * 1B rrlen) + 1B '\0'
    rr_size = (rrlen+1) * 2 + 1;
    memptr = malloc(rr_size);
    memcpy(memptr, &rrlen, 1);
    strcpy(memptr + 1, "a=1");
    memcpy(memptr + rrlen + 1, &rrlen, 1);
    strcpy(memptr + rrlen + 1 + 1, "b=2");

    // TXT 记录
    ret = avahi_entry_group_add_record(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
            AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_USE_MULTICAST,
            full_address, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_TXT, AVAHI_DEFAULT_TTL,
            memptr, rr_size);
    free(memptr);


    /***************************************************************/
    rrlen = strlen(service_type);
    rr_size = rrlen + 2;
    memptr = malloc(rr_size);
    memcpy(memptr, &rrlen, 1);
    strcpy(memptr + 1, service_type);

    // DNS-SD PTR 记录
    ret = avahi_entry_group_add_record(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
            AVAHI_PUBLISH_UNIQUE | AVAHI_PUBLISH_USE_MULTICAST,
            dns_sd_service_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, AVAHI_DEFAULT_TTL,
            memptr, rr_size);
    free(memptr);

}

你可能感兴趣的:(avahi,mdns,dns-sd,c)