| /* Low level mDNS encoding and decoding functions of the qname IE, header/question sections and resource records, |
| * as described in these RFCs: |
| * - RFC 1035 (Domain names - implementation and specification) |
| * - RFC 3596 (DNS Extensions to Support IP Version 6) */ |
| |
| /* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> |
| * |
| * All Rights Reserved |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along |
| * with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <string.h> |
| #include <ctype.h> |
| #include <errno.h> |
| #include <osmocom/core/msgb.h> |
| #include <osmocom/core/bitvec.h> |
| #include <osmocom/core/logging.h> |
| #include <osmocom/gsm/apn.h> |
| #include <osmocom/mslookup/mdns_rfc.h> |
| |
| /* |
| * Encode/decode message sections |
| */ |
| |
| /*! Encode header section (RFC 1035 4.1.1). |
| * \param[in] msgb mesage buffer to which the encoded data will be appended. |
| */ |
| void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr) |
| { |
| struct osmo_mdns_rfc_header *buf = (struct osmo_mdns_rfc_header *) msgb_put(msg, sizeof(*hdr)); |
| memcpy(buf, hdr, sizeof(*hdr)); |
| |
| osmo_store16be(buf->id, &buf->id); |
| osmo_store16be(buf->qdcount, &buf->qdcount); |
| osmo_store16be(buf->ancount, &buf->ancount); |
| osmo_store16be(buf->nscount, &buf->nscount); |
| osmo_store16be(buf->arcount, &buf->arcount); |
| } |
| |
| /*! Decode header section (RFC 1035 4.1.1). */ |
| int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr) |
| { |
| if (data_len != sizeof(*hdr)) |
| return -EINVAL; |
| |
| memcpy(hdr, data, data_len); |
| |
| hdr->id = osmo_load16be(&hdr->id); |
| hdr->qdcount = osmo_load16be(&hdr->qdcount); |
| hdr->ancount = osmo_load16be(&hdr->ancount); |
| hdr->nscount = osmo_load16be(&hdr->nscount); |
| hdr->arcount = osmo_load16be(&hdr->arcount); |
| |
| return 0; |
| } |
| |
| /*! Encode question section (RFC 1035 4.1.2). |
| * \param[in] msgb mesage buffer to which the encoded data will be appended. |
| */ |
| int osmo_mdns_rfc_question_encode(struct msgb *msg, const struct osmo_mdns_rfc_question *qst) |
| { |
| uint8_t *buf; |
| size_t buf_len; |
| |
| /* qname */ |
| buf_len = strlen(qst->domain) + 1; |
| buf = msgb_put(msg, buf_len); |
| if (osmo_apn_from_str(buf, buf_len, qst->domain) < 0) |
| return -EINVAL; |
| msgb_put_u8(msg, 0x00); |
| |
| /* qtype and qclass */ |
| msgb_put_u16(msg, qst->qtype); |
| msgb_put_u16(msg, qst->qclass); |
| |
| return 0; |
| } |
| |
| /*! Decode question section (RFC 1035 4.1.2). */ |
| struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len) |
| { |
| struct osmo_mdns_rfc_question *ret; |
| size_t qname_len = data_len - 4; |
| |
| if (data_len < 6) |
| return NULL; |
| |
| ret = talloc_zero(ctx, struct osmo_mdns_rfc_question); |
| if (!ret) |
| return NULL; |
| |
| /* qname */ |
| ret->domain = talloc_size(ret, qname_len - 1); |
| if (!ret->domain) |
| goto error; |
| if (!osmo_apn_to_str(ret->domain, data, qname_len - 1)) |
| goto error; |
| |
| /* qtype and qclass */ |
| ret->qtype = osmo_load16be(data + qname_len); |
| ret->qclass = osmo_load16be(data + qname_len + 2); |
| |
| return ret; |
| error: |
| talloc_free(ret); |
| return NULL; |
| } |
| |
| /* |
| * Encode/decode resource records |
| */ |
| |
| /*! Encode one resource record (RFC 1035 4.1.3). |
| * \param[in] msgb mesage buffer to which the encoded data will be appended. |
| */ |
| int osmo_mdns_rfc_record_encode(struct msgb *msg, const struct osmo_mdns_rfc_record *rec) |
| { |
| uint8_t *buf; |
| size_t buf_len; |
| |
| /* name */ |
| buf_len = strlen(rec->domain) + 1; |
| buf = msgb_put(msg, buf_len); |
| if (osmo_apn_from_str(buf, buf_len, rec->domain) < 0) |
| return -EINVAL; |
| msgb_put_u8(msg, 0x00); |
| |
| /* type, class, ttl, rdlength */ |
| msgb_put_u16(msg, rec->type); |
| msgb_put_u16(msg, rec->class); |
| msgb_put_u32(msg, rec->ttl); |
| msgb_put_u16(msg, rec->rdlength); |
| |
| /* rdata */ |
| buf = msgb_put(msg, rec->rdlength); |
| memcpy(buf, rec->rdata, rec->rdlength); |
| return 0; |
| } |
| |
| /*! Decode one resource record (RFC 1035 4.1.3). */ |
| struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len, |
| size_t *record_len) |
| { |
| struct osmo_mdns_rfc_record *ret; |
| size_t name_len; |
| |
| /* name length: represented as a series of labels, and terminated by a |
| * label with zero length (RFC 1035 3.3). A label with zero length is a |
| * NUL byte. */ |
| name_len = strnlen((const char *)data, data_len - 10) + 1; |
| if (data[name_len]) |
| return NULL; |
| |
| /* allocate ret + ret->domain */ |
| ret = talloc_zero(ctx, struct osmo_mdns_rfc_record); |
| if (!ret) |
| return NULL; |
| ret->domain = talloc_size(ctx, name_len - 1); |
| if (!ret->domain) |
| goto error; |
| |
| /* name */ |
| if (!osmo_apn_to_str(ret->domain, data, name_len - 1)) |
| goto error; |
| |
| /* type, class, ttl, rdlength */ |
| ret->type = osmo_load16be(data + name_len); |
| ret->class = osmo_load16be(data + name_len + 2); |
| ret->ttl = osmo_load32be(data + name_len + 4); |
| ret->rdlength = osmo_load16be(data + name_len + 8); |
| if (name_len + 10 + ret->rdlength > data_len) |
| goto error; |
| |
| /* rdata */ |
| ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength); |
| if (!ret->rdata) |
| goto error; |
| |
| *record_len = name_len + 10 + ret->rdlength; |
| return ret; |
| error: |
| talloc_free(ret); |
| return NULL; |
| } |
| |