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) {
    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;
    }
}

int avahi_dns_packet_consume_name(AvahiDnsPacket *p, char *ret_name, size_t l) {
    if((r = consume_label(p, p->rindex, ret_name, l)) < 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 full_address_withlen[] = "\1service1\5_http\4_tcp\5local";  // 每个.分隔的字段前需要标记长度
    full_address_withlen[0] = 8;
    char service_name[] = "_http._tcp.local";
    char service_name_withlen[] = "\5_http.\4_tcp\5local";
    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 字符串长度) + (字符串) + ... + (1B '\0'字符串结束符)
    // 参考 RFC6763 - DNS-SD 第6章
    rrlen = strlen(full_address_withlen);
    rr_size = rrlen + 1;    // 1B '\0'

    // PTR 记录,不使用 AVAHI_PUBLISH_UNIQUE 标志
    ret = avahi_entry_group_add_record(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
            AVAHI_PUBLISH_USE_MULTICAST,
            service_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, AVAHI_DEFAULT_TTL,
            full_address_withlen, rr_size);


    /***************************************************************/
    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 记录,TTL值与其他的不一样
    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_name_withlen);
    rr_size = rrlen + 1;    // 1B '\0'

    // DNS-SD PTR 记录,要把 AVAHI_PUBLISH_UNIQUE 标志去掉
    ret = avahi_entry_group_add_record(entry_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC,
            AVAHI_PUBLISH_USE_MULTICAST,
            dns_sd_service_name, AVAHI_DNS_CLASS_IN, AVAHI_DNS_TYPE_PTR, AVAHI_DEFAULT_TTL,
            service_name_withlen, rr_size);
    
    // 提交所有请求
    ret = avahi_entry_group_commit(entry_group)

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值