blob: e1fc184d3919f36f4eaa372742bdaf9db3037e58 [file] [log] [blame]
Oliver Smith3a9f2672019-11-20 10:56:35 +01001/* Low level mDNS encoding and decoding functions of the qname IE, header/question sections and resource records,
2 * as described in these RFCs:
3 * - RFC 1035 (Domain names - implementation and specification)
4 * - RFC 3596 (DNS Extensions to Support IP Version 6) */
5
6/* Copyright 2019 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
7 *
8 * All Rights Reserved
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License along
21 * with this program. If not, see <http://www.gnu.org/licenses/>.
22 */
23
24#include <string.h>
25#include <ctype.h>
26#include <errno.h>
27#include <osmocom/core/msgb.h>
28#include <osmocom/core/bitvec.h>
29#include <osmocom/core/logging.h>
30#include <osmocom/mslookup/mdns_rfc.h>
31
32/*
33 * Encode/decode IEs
34 */
35
36/*! Encode a domain string as qname (RFC 1035 4.1.2).
37 * \param[in] domain multiple labels separated by dots, e.g. "sip.voice.1234.msisdn".
38 * \returns allocated buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...), NULL on error.
39 */
40char *osmo_mdns_rfc_qname_encode(void *ctx, const char *domain)
41{
42 char *domain_dup;
43 char *domain_iter;
44 char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 2] = ""; /* len(qname) is len(domain) +1 */
45 struct osmo_strbuf sb = { .buf = buf, .len = sizeof(buf) };
46 char *label;
47
48 if (strlen(domain) > OSMO_MDNS_RFC_MAX_NAME_LEN)
49 return NULL;
50
51 domain_iter = domain_dup = talloc_strdup(ctx, domain);
52 while ((label = strsep(&domain_iter, "."))) {
53 size_t len = strlen(label);
54
55 /* Empty domain, dot at start, two dots in a row, or ending with a dot */
56 if (!len)
57 goto error;
58
59 OSMO_STRBUF_PRINTF(sb, "%c%s", (char)len, label);
60 }
61
62 talloc_free(domain_dup);
63 return talloc_strdup(ctx, buf);
64
65error:
66 talloc_free(domain_dup);
67 return NULL;
68}
69
70/*! Decode a domain string from a qname (RFC 1035 4.1.2).
71 * \param[in] qname buffer with length-value pairs for each label (e.g. 0x03 "sip" 0x05 "voice" ...)
72 * \param[in] qname_max_len amount of bytes that can be read at most from the memory location that qname points to.
73 * \returns allocated buffer with domain string, multiple labels separated by dots (e.g. "sip.voice.1234.msisdn"),
74 * NULL on error.
75 */
76char *osmo_mdns_rfc_qname_decode(void *ctx, const char *qname, size_t qname_max_len)
77{
78 const char *next_label, *qname_end = qname + qname_max_len;
79 char buf[OSMO_MDNS_RFC_MAX_NAME_LEN + 1];
80 int i = 0;
81
82 if (qname_max_len < 1)
83 return NULL;
84
85 while (*qname) {
86 size_t len = *qname;
87 next_label = qname + len + 1;
88
89 if (next_label >= qname_end || i + len > OSMO_MDNS_RFC_MAX_NAME_LEN)
90 return NULL;
91
92 if (i) {
93 /* Two dots in a row is not allowed */
94 if (buf[i - 1] == '.')
95 return NULL;
96
97 buf[i] = '.';
98 i++;
99 }
100
101 memcpy(buf + i, qname + 1, len);
102 i += len;
103 qname = next_label;
104 }
105 buf[i] = '\0';
106
107 return talloc_strdup(ctx, buf);
108}
109
110/*
111 * Encode/decode message sections
112 */
113
114/*! Encode header section (RFC 1035 4.1.1).
115 * \param[in] msgb mesage buffer to which the encoded data will be appended.
116 */
117void osmo_mdns_rfc_header_encode(struct msgb *msg, const struct osmo_mdns_rfc_header *hdr)
118{
119 struct osmo_mdns_rfc_header *buf = (struct osmo_mdns_rfc_header *) msgb_put(msg, sizeof(*hdr));
120 memcpy(buf, hdr, sizeof(*hdr));
121
122 osmo_store16be(buf->id, &buf->id);
123 osmo_store16be(buf->qdcount, &buf->qdcount);
124 osmo_store16be(buf->ancount, &buf->ancount);
125 osmo_store16be(buf->nscount, &buf->nscount);
126 osmo_store16be(buf->arcount, &buf->arcount);
127}
128
129/*! Decode header section (RFC 1035 4.1.1). */
130int osmo_mdns_rfc_header_decode(const uint8_t *data, size_t data_len, struct osmo_mdns_rfc_header *hdr)
131{
132 if (data_len != sizeof(*hdr))
133 return -EINVAL;
134
135 memcpy(hdr, data, data_len);
136
137 hdr->id = osmo_load16be(&hdr->id);
138 hdr->qdcount = osmo_load16be(&hdr->qdcount);
139 hdr->ancount = osmo_load16be(&hdr->ancount);
140 hdr->nscount = osmo_load16be(&hdr->nscount);
141 hdr->arcount = osmo_load16be(&hdr->arcount);
142
143 return 0;
144}
145
146/*! Encode question section (RFC 1035 4.1.2).
147 * \param[in] msgb mesage buffer to which the encoded data will be appended.
148 */
149int osmo_mdns_rfc_question_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_question *qst)
150{
151 char *qname;
152 size_t qname_len;
153 uint8_t *qname_buf;
154
155 /* qname */
156 qname = osmo_mdns_rfc_qname_encode(ctx, qst->domain);
157 if (!qname)
158 return -EINVAL;
159 qname_len = strlen(qname) + 1;
160 qname_buf = msgb_put(msg, qname_len);
161 memcpy(qname_buf, qname, qname_len);
162 talloc_free(qname);
163
164 /* qtype and qclass */
165 msgb_put_u16(msg, qst->qtype);
166 msgb_put_u16(msg, qst->qclass);
167
168 return 0;
169}
170
171/*! Decode question section (RFC 1035 4.1.2). */
172struct osmo_mdns_rfc_question *osmo_mdns_rfc_question_decode(void *ctx, const uint8_t *data, size_t data_len)
173{
174 struct osmo_mdns_rfc_question *ret;
175 size_t qname_len = data_len - 4;
176
177 if (data_len < 6)
178 return NULL;
179
180 /* qname */
181 ret = talloc_zero(ctx, struct osmo_mdns_rfc_question);
182 if (!ret)
183 return NULL;
184 ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, qname_len);
185 if (!ret->domain) {
186 talloc_free(ret);
187 return NULL;
188 }
189
190 /* qtype and qclass */
191 ret->qtype = osmo_load16be(data + qname_len);
192 ret->qclass = osmo_load16be(data + qname_len + 2);
193
194 return ret;
195}
196
197/*
198 * Encode/decode resource records
199 */
200
201/*! Encode one resource record (RFC 1035 4.1.3).
202 * \param[in] msgb mesage buffer to which the encoded data will be appended.
203 */
204int osmo_mdns_rfc_record_encode(void *ctx, struct msgb *msg, const struct osmo_mdns_rfc_record *rec)
205{
206 char *name;
207 size_t name_len;
208 uint8_t *buf;
209
210 /* name */
211 name = osmo_mdns_rfc_qname_encode(ctx, rec->domain);
212 if (!name)
213 return -EINVAL;
214 name_len = strlen(name) + 1;
215 buf = msgb_put(msg, name_len);
216 memcpy(buf, name, name_len);
217 talloc_free(name);
218
219 /* type, class, ttl, rdlength */
220 msgb_put_u16(msg, rec->type);
221 msgb_put_u16(msg, rec->class);
222 msgb_put_u32(msg, rec->ttl);
223 msgb_put_u16(msg, rec->rdlength);
224
225 /* rdata */
226 buf = msgb_put(msg, rec->rdlength);
227 memcpy(buf, rec->rdata, rec->rdlength);
228 return 0;
229}
230
231/*! Decode one resource record (RFC 1035 4.1.3). */
232struct osmo_mdns_rfc_record *osmo_mdns_rfc_record_decode(void *ctx, const uint8_t *data, size_t data_len,
233 size_t *record_len)
234{
235 struct osmo_mdns_rfc_record *ret = talloc_zero(ctx, struct osmo_mdns_rfc_record);
236 size_t name_len;
237
238 /* name */
239 ret->domain = osmo_mdns_rfc_qname_decode(ret, (const char *)data, data_len - 10);
240 if (!ret->domain)
241 goto error;
242 name_len = strlen(ret->domain) + 2;
243 if (name_len + 10 > data_len)
244 goto error;
245
246 /* type, class, ttl, rdlength */
247 ret->type = osmo_load16be(data + name_len);
248 ret->class = osmo_load16be(data + name_len + 2);
249 ret->ttl = osmo_load32be(data + name_len + 4);
250 ret->rdlength = osmo_load16be(data + name_len + 8);
251 if (name_len + 10 + ret->rdlength > data_len)
252 goto error;
253
254 /* rdata */
255 ret->rdata = talloc_memdup(ret, data + name_len + 10, ret->rdlength);
256 if (!ret->rdata)
257 return NULL;
258
259 *record_len = name_len + 10 + ret->rdlength;
260 return ret;
261error:
262 talloc_free(ret);
263 return NULL;
264}
265