Bug Summary

File:.build-ci/../discoverd/src/mdns.c
Warning:line 865, column 2
Use of memory after it is released

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-redhat-linux-gnu -O3 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name mdns.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=none -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -fcoverage-compilation-dir=/__w/nvme-cli/nvme-cli/.build-ci -resource-dir /usr/bin/../lib/clang/22 -include /__w/nvme-cli/nvme-cli/.build-ci/nvme-config.h -I discoverd/nvme-discoverd.p -I discoverd -I ../discoverd -I ../discoverd/src -I ccan -I ../ccan -I . -I .. -I libnvme/src -I ../libnvme/src -D _FILE_OFFSET_BITS=64 -D _GNU_SOURCE -D NVME_PATH="/usr/local/sbin/nvme" -U NDEBUG -internal-isystem /usr/bin/../lib/clang/22/include -internal-isystem /usr/local/include -internal-isystem /usr/bin/../lib/gcc/x86_64-redhat-linux/16/../../../../x86_64-redhat-linux/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -std=gnu11 -ferror-limit 19 -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fcolor-diagnostics -vectorize-loops -vectorize-slp -analyzer-opt-analyze-headers -analyzer-output=html -faddrsig -fdwarf2-cfi-asm -o /__w/nvme-cli/nvme-cli/.build-ci/scan-results/2026-10-24-125112-639-1 -x c ../discoverd/src/mdns.c
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This file is part of nvme-cli.
4 * Copyright (c) 2026 Dell Technologies Inc. or its subsidiaries.
5 *
6 * Authors: Martin Belanger <martin.belanger@dell.com>
7 */
8
9#include <arpa/inet.h>
10#include <errno(*__errno_location ()).h>
11#include <netinet/in.h>
12#include <stdbool.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <strings.h>
17#include <sys/epoll.h>
18#include <sys/socket.h>
19#include <unistd.h>
20
21#include <ccan/array_size/array_size.h>
22#include <ccan/list/list.h>
23#include <ccan/str/str.h>
24#include <daemon-util/log.h>
25#include <shared/time-util.h>
26#include <systemd/sd-json.h>
27#include <systemd/sd-varlink.h>
28
29#include "mdns.h"
30#include "netif.h"
31
32#define RESOLVE_VARLINK_ADDRESS"/run/systemd/resolve/io.systemd.Resolve" "/run/systemd/resolve/io.systemd.Resolve"
33#define RESOLVE_INTERFACE"io.systemd.Resolve" "io.systemd.Resolve"
34#define VARLINK_GET_DESCRIPTION"org.varlink.service.GetInterfaceDescription" "org.varlink.service.GetInterfaceDescription"
35#define VARLINK_BROWSE_SERVICES"io.systemd.Resolve.BrowseServices" "io.systemd.Resolve.BrowseServices"
36#define VARLINK_RESOLVE_SERVICE"io.systemd.Resolve.ResolveService" "io.systemd.Resolve.ResolveService"
37
38/*
39 * NVMe Base Spec 2.4 §8.3.1.1.1, Figure 800 (TP8009): TCP and iWARP DCs
40 * advertise "_tcp", RoCE DCs advertise "_udp". Each type needs its own
41 * BrowseServices call.
42 */
43static const char * const mdns_service_types[] = {
44 "_nvme-disc._tcp",
45 "_nvme-disc._udp",
46};
47
48/*
49 * Retry intervals, in seconds, for the TCP reachability check. Some DCs
50 * advertise before their TCP listener is up. An NVMe connect through the
51 * kernel fails at that point, and the nvme driver logs errors. A plain
52 * TCP connect from userspace fails silently. So a tcp endpoint is
53 * reported only after a userspace TCP connect to it succeeds. The last
54 * interval repeats: the check stops only when the advertisement is
55 * withdrawn.
56 */
57static const unsigned int tcp_check_retry_sec[] = {
58 2, 5, 10, 30, 60, 300, 600,
59};
60
61/*
62 * Restart delay, in seconds, after a browse call fails, e.g. because
63 * systemd-resolved restarted. Doubles on each failed attempt, up to the
64 * maximum.
65 */
66#define BROWSE_RESTART_MIN_SEC1 1
67#define BROWSE_RESTART_MAX_SEC60 60
68
69/* One resolved connect endpoint for a discovered mDNS service instance. */
70struct mdns_endpoint {
71 struct list_node entry;
72 char *traddr;
73 char *trsvcid;
74};
75
76/* A TCP reachability check for a tcp endpoint not yet reported. */
77struct tcp_check {
78 struct list_node entry;
79 struct mdns_browse *br;
80 struct mdns_service *svc;
81 char *traddr;
82 char *trsvcid;
83 struct sockaddr_storage addr;
84 socklen_t addrlen;
85 int fd; // -1 when not connecting
86 sd_event_source *io_source;
87 sd_event_source *retry_timer;
88 size_t retry_idx; // into tcp_check_retry_sec[]
89 bool_Bool ceiling_logged; // for log_info_once()
90};
91
92/* One discovered mDNS service instance, keyed by name within its browse. */
93struct mdns_service {
94 struct list_node entry;
95 char *name;
96 const char *transport; // "tcp" or "rdma", from the TXT record's p= key
97 char *nqn; // TXT record's optional nqn= key, NULL if absent
98 struct list_head endpoints;
99 struct list_head checks; // struct tcp_check
100};
101
102/* One outstanding ResolveService call for a just-added service instance. */
103struct resolve_req {
104 struct list_node entry;
105 struct mdns_browse *br;
106 sd_varlink *link;
107 char *name;
108};
109
110/* One per-interface BrowseServices subscription. */
111struct mdns_browse {
112 struct list_node entry;
113 struct mdns_ctx *mctx;
114 int ifindex;
115 char *ifname;
116 const char *type; // one of mdns_service_types[], static duration
117 sd_varlink *link; // NULL while waiting to restart
118 sd_event_source *restart_timer;
119 unsigned int restart_sec; // next restart delay
120 struct list_head services; // struct mdns_service, resolved instances
121 struct list_head pending; // struct resolve_req, in-flight resolves
122};
123
124struct mdns_ctx {
125 sd_event *event;
126 struct mdns_callbacks callbacks;
127 void *user_data;
128 struct netif_ctx *nctx;
129 struct list_head browses; // struct mdns_browse, one per candidate iface
130};
131
132/* @v's "key" member as a string, or NULL if absent or not a string. */
133static const char *json_str(sd_json_variant *v, const char *key)
134{
135 v = sd_json_variant_by_key(v, key);
136
137 return v ? sd_json_variant_string(v) : NULL((void*)0);
138}
139
140/* @v's "key" member, or NULL if absent or not an array. */
141static sd_json_variant *json_array(sd_json_variant *v, const char *key)
142{
143 v = sd_json_variant_by_key(v, key);
144
145 return v && sd_json_variant_is_array(v) ? v : NULL((void*)0);
146}
147
148/*
149 * Returns 0 if io.systemd.Resolve has a BrowseServices method, -EOPNOTSUPP
150 * if not, or another negative errno.
151 */
152static int resolved_has_browse_services(sd_varlink *link)
153{
154 sd_json_variant *reply = NULL((void*)0);
155 const char *error_id = NULL((void*)0);
156 const char *text;
157 int r;
158
159 r = sd_varlink_callbo(link, VARLINK_GET_DESCRIPTION, &reply, &error_id,sd_varlink_callb((link), ("org.varlink.service.GetInterfaceDescription"
), (&reply), (&error_id), _SD_JSON_BUILD_OBJECT_BEGIN
, _SD_JSON_BUILD_PAIR, (const char*) { "interface" }, _SD_JSON_BUILD_STRING
, (const char*) { "io.systemd.Resolve" }, _SD_JSON_BUILD_OBJECT_END
)
160 SD_JSON_BUILD_PAIR_STRING("interface",sd_varlink_callb((link), ("org.varlink.service.GetInterfaceDescription"
), (&reply), (&error_id), _SD_JSON_BUILD_OBJECT_BEGIN
, _SD_JSON_BUILD_PAIR, (const char*) { "interface" }, _SD_JSON_BUILD_STRING
, (const char*) { "io.systemd.Resolve" }, _SD_JSON_BUILD_OBJECT_END
)
161 RESOLVE_INTERFACE))sd_varlink_callb((link), ("org.varlink.service.GetInterfaceDescription"
), (&reply), (&error_id), _SD_JSON_BUILD_OBJECT_BEGIN
, _SD_JSON_BUILD_PAIR, (const char*) { "interface" }, _SD_JSON_BUILD_STRING
, (const char*) { "io.systemd.Resolve" }, _SD_JSON_BUILD_OBJECT_END
)
;
162 if (r < 0)
163 return r;
164 if (error_id)
165 return -EPROTO71;
166
167 text = json_str(reply, "description");
168 if (!text || !strstr(text, "method BrowseServices(")_Generic (0 ? (text) : (void *) 1, const void *: (const char *
) (strstr (text, "method BrowseServices(")), default: strstr (
text, "method BrowseServices("))
)
169 return -EOPNOTSUPP95;
170
171 return 0;
172}
173
174static void mdns_endpoint_free(struct mdns_endpoint *ep)
175{
176 if (!ep)
177 return;
178 list_del_init(&ep->entry)list_del_init_(&ep->entry, "../discoverd/src/mdns.c" ":"
"178")
;
179 free(ep->traddr);
180 free(ep->trsvcid);
181 free(ep);
182}
183
184static void tcp_check_free(struct tcp_check *tc)
185{
186 if (!tc)
187 return;
188 list_del_init(&tc->entry)list_del_init_(&tc->entry, "../discoverd/src/mdns.c" ":"
"188")
;
189 sd_event_source_unref(tc->io_source);
190 sd_event_source_unref(tc->retry_timer);
191 if (tc->fd >= 0)
192 close(tc->fd);
193 free(tc->traddr);
194 free(tc->trsvcid);
195 free(tc);
196}
197
198static struct mdns_service *service_find(struct mdns_browse *br,
199 const char *name)
200{
201 struct mdns_service *svc;
202
203 list_for_each(&br->services, svc, entry)for ((svc) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "203", (&br->services))->n.next, (((__builtin_offsetof
(typeof(*svc), entry) + ((typeof(svc->entry) *)0 != (struct
list_node *)0))))); list_node_from_off_((void *)(svc), (((__builtin_offsetof
(typeof(*svc), entry) + ((typeof(svc->entry) *)0 != (struct
list_node *)0))))) != &((&br->services))->n; (
svc) = list_node_to_off_(list_node_from_off_((void *)(svc), (
((__builtin_offsetof(typeof(*svc), entry) + ((typeof(svc->
entry) *)0 != (struct list_node *)0)))))->next, (((__builtin_offsetof
(typeof(*svc), entry) + ((typeof(svc->entry) *)0 != (struct
list_node *)0))))))
{
204 if (streq(svc->name, name)(strcmp((svc->name),(name)) == 0))
205 return svc;
206 }
207 return NULL((void*)0);
208}
209
210/* Cancel the checks, report every endpoint as removed, then free @svc. */
211static void service_free(struct mdns_browse *br, struct mdns_service *svc)
212{
213 struct mdns_endpoint *ep, *next;
214 struct tcp_check *tc, *next_tc;
215
216 list_for_each_safe(&svc->checks, tc, next_tc, entry)for ((tc) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "216", (&svc->checks))->n.next, (((__builtin_offsetof
(typeof(*tc), entry) + ((typeof(tc->entry) *)0 != (struct list_node
*)0))))), (next_tc) = list_node_to_off_(list_node_from_off_(
(tc), (((__builtin_offsetof(typeof(*tc), entry) + ((typeof(tc
->entry) *)0 != (struct list_node *)0)))))->next, (((__builtin_offsetof
(typeof(*tc), entry) + ((typeof(tc->entry) *)0 != (struct list_node
*)0))))); list_node_from_off_((tc), (((__builtin_offsetof(typeof
(*tc), entry) + ((typeof(tc->entry) *)0 != (struct list_node
*)0))))) != &((&svc->checks))->n; (tc) = (next_tc
), (next_tc) = list_node_to_off_(list_node_from_off_((tc), ((
(__builtin_offsetof(typeof(*tc), entry) + ((typeof(tc->entry
) *)0 != (struct list_node *)0)))))->next, (((__builtin_offsetof
(typeof(*tc), entry) + ((typeof(tc->entry) *)0 != (struct list_node
*)0))))))
217 tcp_check_free(tc);
218
219 list_for_each_safe(&svc->endpoints, ep, next, entry)for ((ep) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "219", (&svc->endpoints))->n.next, (((__builtin_offsetof
(typeof(*ep), entry) + ((typeof(ep->entry) *)0 != (struct list_node
*)0))))), (next) = list_node_to_off_(list_node_from_off_((ep
), (((__builtin_offsetof(typeof(*ep), entry) + ((typeof(ep->
entry) *)0 != (struct list_node *)0)))))->next, (((__builtin_offsetof
(typeof(*ep), entry) + ((typeof(ep->entry) *)0 != (struct list_node
*)0))))); list_node_from_off_((ep), (((__builtin_offsetof(typeof
(*ep), entry) + ((typeof(ep->entry) *)0 != (struct list_node
*)0))))) != &((&svc->endpoints))->n; (ep) = (next
), (next) = list_node_to_off_(list_node_from_off_((ep), (((__builtin_offsetof
(typeof(*ep), entry) + ((typeof(ep->entry) *)0 != (struct list_node
*)0)))))->next, (((__builtin_offsetof(typeof(*ep), entry)
+ ((typeof(ep->entry) *)0 != (struct list_node *)0))))))
{
220 if (br->mctx->callbacks.service_remove)
221 br->mctx->callbacks.service_remove(
222 ep->traddr, ep->trsvcid, svc->transport,
223 svc->nqn, br->ifname, br->ifindex,
224 br->mctx->user_data);
225 mdns_endpoint_free(ep);
226 }
227
228 list_del_init(&svc->entry)list_del_init_(&svc->entry, "../discoverd/src/mdns.c" ":"
"228")
;
229 free(svc->name);
230 free(svc->nqn);
231 free(svc);
232}
233
234/*
235 * NVMe Base Spec 2.4 §8.3.1.1.2 (TP8009): the TXT record's p= key gives the
236 * transport. The service type cannot: iWARP is RDMA but advertises "_tcp".
237 * Matched case-insensitively.
238 */
239static const char *proto_to_transport(const char *proto)
240{
241 if (!strcasecmp(proto, "tcp"))
242 return "tcp";
243 if (!strcasecmp(proto, "roce") || !strcasecmp(proto, "iwarp") ||
244 !strcasecmp(proto, "rdma"))
245 return "rdma";
246
247 return NULL((void*)0);
248}
249
250/*
251 * Read the TXT record's p= and nqn= keys. Returns -EINVAL if p= is missing
252 * or unknown. *ret_nqn is borrowed from @parameters, or NULL.
253 */
254static int resolve_txt(sd_json_variant *parameters,
255 const char **ret_transport, const char **ret_nqn)
256{
257 sd_json_variant *txt, *item;
258 size_t i, n;
259
260 *ret_transport = NULL((void*)0);
261 *ret_nqn = NULL((void*)0);
262
263 txt = json_array(parameters, "txt");
264 if (!txt)
265 return -EINVAL22;
266
267 n = sd_json_variant_elements(txt);
268 for (i = 0; i < n; i++) {
269 const char *kv;
270
271 item = sd_json_variant_by_index(txt, i);
272 kv = sd_json_variant_string(item);
273 if (!kv)
274 continue;
275
276 if (!*ret_transport && !strncmp(kv, "p=", 2))
277 *ret_transport = proto_to_transport(kv + 2);
278 else if (!*ret_nqn && !strncmp(kv, "nqn=", 4))
279 *ret_nqn = kv + 4;
280 }
281
282 return *ret_transport ? 0 : -EINVAL22;
283}
284
285/* Link @traddr/@trsvcid (ownership taken) into @svc and report it. */
286static void report_endpoint(struct mdns_browse *br, struct mdns_service *svc,
287 char *traddr, char *trsvcid)
288{
289 struct mdns_endpoint *ep;
290
291 if (!traddr || !trsvcid) {
292 free(traddr);
293 free(trsvcid);
294 return;
295 }
296
297 ep = calloc(1, sizeof(*ep));
298 if (!ep) {
299 free(traddr);
300 free(trsvcid);
301 return;
302 }
303 list_node_init(&ep->entry); // safe to free unlinked
304 ep->traddr = traddr;
305 ep->trsvcid = trsvcid;
306 list_add(&svc->endpoints, &ep->entry)list_add_(&svc->endpoints, &ep->entry, "../discoverd/src/mdns.c"
":" "306")
;
307
308 if (br->mctx->callbacks.service_add)
309 br->mctx->callbacks.service_add(ep->traddr, ep->trsvcid,
310 svc->transport, svc->nqn,
311 br->ifname, br->ifindex,
312 br->mctx->user_data);
313}
314
315static void tcp_check_start(struct tcp_check *tc);
316
317static int tcp_check_retry_cback(sd_event_source *s __attribute__((unused)),
318 uint64_t usec __attribute__((unused)),
319 void *userdata)
320{
321 struct tcp_check *tc = userdata;
322
323 tc->retry_timer = sd_event_source_unref(tc->retry_timer);
324 tcp_check_start(tc);
325
326 return 0;
327}
328
329/* Close the current attempt, if any, and arm the next retry. */
330static void tcp_check_schedule_retry(struct tcp_check *tc)
331{
332 size_t last = ARRAY_SIZE(tcp_check_retry_sec)(sizeof(tcp_check_retry_sec) / sizeof((tcp_check_retry_sec)[0
]) + 0)
- 1;
333 unsigned int sec;
334 uint64_t now;
335 int r;
336
337 tc->io_source = sd_event_source_unref(tc->io_source);
338 if (tc->fd >= 0) {
339 close(tc->fd);
340 tc->fd = -1;
341 }
342
343 sec = tcp_check_retry_sec[tc->retry_idx];
344 if (tc->retry_idx < last)
345 tc->retry_idx++;
346 else
347 log_info_once(&tc->ceiling_logged,dmn_log_msg_once(&tc->ceiling_logged, DMN_LOG_INFO, "mdns: %s: %s:%s still unreachable, retrying every %us"
, tc->br->ifname, tc->traddr, tc->trsvcid, sec)
348 "mdns: %s: %s:%s still unreachable, retrying every %us",dmn_log_msg_once(&tc->ceiling_logged, DMN_LOG_INFO, "mdns: %s: %s:%s still unreachable, retrying every %us"
, tc->br->ifname, tc->traddr, tc->trsvcid, sec)
349 tc->br->ifname, tc->traddr, tc->trsvcid, sec)dmn_log_msg_once(&tc->ceiling_logged, DMN_LOG_INFO, "mdns: %s: %s:%s still unreachable, retrying every %us"
, tc->br->ifname, tc->traddr, tc->trsvcid, sec)
;
350
351 r = sd_event_now(tc->br->mctx->event, CLOCK_BOOTTIME7, &now);
352 if (r >= 0)
353 r = sd_event_add_time(tc->br->mctx->event, &tc->retry_timer,
354 CLOCK_BOOTTIME7,
355 now + sec * UINT64_C(1000000)1000000UL, 0,
356 tcp_check_retry_cback, tc);
357 if (r < 0)
358 log_err("mdns: %s: %s:%s: cannot arm retry timer: %s",dmn_log_msg(DMN_LOG_ERR, "mdns: %s: %s:%s: cannot arm retry timer: %s"
, tc->br->ifname, tc->traddr, tc->trsvcid, strerror
(-r))
359 tc->br->ifname, tc->traddr, tc->trsvcid,dmn_log_msg(DMN_LOG_ERR, "mdns: %s: %s:%s: cannot arm retry timer: %s"
, tc->br->ifname, tc->traddr, tc->trsvcid, strerror
(-r))
360 strerror(-r))dmn_log_msg(DMN_LOG_ERR, "mdns: %s: %s:%s: cannot arm retry timer: %s"
, tc->br->ifname, tc->traddr, tc->trsvcid, strerror
(-r))
;
361}
362
363/* Reachable: report the endpoint and free @tc. */
364static void tcp_check_succeeded(struct tcp_check *tc)
365{
366 char *traddr = tc->traddr;
367 char *trsvcid = tc->trsvcid;
368
369 tc->traddr = NULL((void*)0);
370 tc->trsvcid = NULL((void*)0);
371 report_endpoint(tc->br, tc->svc, traddr, trsvcid);
372 tcp_check_free(tc);
373}
374
375static int tcp_check_io_cback(sd_event_source *s __attribute__((unused)),
376 int fd,
377 uint32_t revents __attribute__((unused)),
378 void *userdata)
379{
380 struct tcp_check *tc = userdata;
381 socklen_t len = sizeof(int);
382 int err = 0;
383
384 if (getsockopt(fd, SOL_SOCKET1, SO_ERROR4, &err, &len) < 0 || err) {
385 tcp_check_schedule_retry(tc);
386 return 0;
387 }
388
389 tcp_check_succeeded(tc);
390
391 return 0;
392}
393
394static void tcp_check_start(struct tcp_check *tc)
395{
396 int r;
397
398 tc->fd = socket(tc->addr.ss_family,
399 SOCK_STREAMSOCK_STREAM | SOCK_NONBLOCKSOCK_NONBLOCK | SOCK_CLOEXECSOCK_CLOEXEC,
400 IPPROTO_TCPIPPROTO_TCP);
401 if (tc->fd < 0) {
402 tcp_check_schedule_retry(tc);
403 return;
404 }
405
406 r = connect(tc->fd, (struct sockaddr *)&tc->addr, tc->addrlen);
407 if (r < 0 && errno(*__errno_location ()) != EINPROGRESS115) {
408 tcp_check_schedule_retry(tc);
409 return;
410 }
411
412 r = sd_event_add_io(tc->br->mctx->event, &tc->io_source, tc->fd,
413 EPOLLOUTEPOLLOUT, tcp_check_io_cback, tc);
414 if (r < 0)
415 tcp_check_schedule_retry(tc);
416}
417
418/*
419 * Start a reachability check for a tcp endpoint. Takes ownership of
420 * @traddr and @trsvcid.
421 */
422static void tcp_check_new(struct mdns_browse *br, struct mdns_service *svc,
423 char *traddr, char *trsvcid,
424 const struct sockaddr_storage *ss, socklen_t sslen)
425{
426 struct tcp_check *tc;
427
428 if (!traddr || !trsvcid)
429 goto err;
430
431 tc = calloc(1, sizeof(*tc));
432 if (!tc)
433 goto err;
434 tc->br = br;
435 tc->svc = svc;
436 tc->traddr = traddr;
437 tc->trsvcid = trsvcid;
438 tc->addr = *ss;
439 tc->addrlen = sslen;
440 tc->fd = -1;
441 list_add(&svc->checks, &tc->entry)list_add_(&svc->checks, &tc->entry, "../discoverd/src/mdns.c"
":" "441")
;
442
443 tcp_check_start(tc);
444 return;
445err:
446 free(traddr);
447 free(trsvcid);
448}
449
450/* Build the sockaddr for a tcp endpoint. Returns its length. */
451static socklen_t endpoint_sockaddr(int family, const void *addr, int port,
452 int ifindex, struct sockaddr_storage *ss)
453{
454 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)ss;
455 struct sockaddr_in *sin = (struct sockaddr_in *)ss;
456
457 memset(ss, 0, sizeof(*ss));
458
459 if (family == AF_INET2) {
460 sin->sin_family = AF_INET2;
461 sin->sin_port = htons((uint16_t)port)__bswap_16 ((uint16_t)port);
462 memcpy(&sin->sin_addr, addr, sizeof(sin->sin_addr));
463
464 return sizeof(*sin);
465 }
466
467 sin6->sin6_family = AF_INET610;
468 sin6->sin6_port = htons((uint16_t)port)__bswap_16 ((uint16_t)port);
469 memcpy(&sin6->sin6_addr, addr, sizeof(sin6->sin6_addr));
470 if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)(__extension__ ({ const struct in6_addr *__a = (const struct in6_addr
*) (&sin6->sin6_addr); (__a->__in6_u.__u6_addr32[0
] & __bswap_32 (0xffc00000)) == __bswap_32 (0xfe800000); }
))
)
471 sin6->sin6_scope_id = ifindex;
472
473 return sizeof(*sin6);
474}
475
476/* Parse one ResolveService "services[]" entry's port and addresses. */
477static void add_endpoints(struct mdns_browse *br, struct mdns_service *svc,
478 sd_json_variant *service_item)
479{
480 sd_json_variant *port_v, *addrs, *addr_item, *family_v, *bytes_v, *b;
481 char *trsvcid;
482 size_t i, n;
483 int port;
484
485 port_v = sd_json_variant_by_key(service_item, "port");
486 if (!port_v)
487 return;
488
489 addrs = json_array(service_item, "addresses");
490 if (!addrs)
491 return;
492
493 port = (int)sd_json_variant_integer(port_v);
494 if (port < 1 || port > 65535) {
495 log_warn("mdns: %s: '%s': bad port %d, ignoring",dmn_log_msg(DMN_LOG_WARN, "mdns: %s: '%s': bad port %d, ignoring"
, br->ifname, svc->name, port)
496 br->ifname, svc->name, port)dmn_log_msg(DMN_LOG_WARN, "mdns: %s: '%s': bad port %d, ignoring"
, br->ifname, svc->name, port)
;
497 return;
498 }
499
500 if (asprintf(&trsvcid, "%d", port) < 0)
501 return;
502
503 n = sd_json_variant_elements(addrs);
504 for (i = 0; i < n; i++) {
505 union {
506 struct in_addr in4;
507 struct in6_addr in6;
508 } a;
509 char buf[INET6_ADDRSTRLEN46];
510 unsigned char *p;
511 int family;
512 size_t j, nbytes;
513
514 addr_item = sd_json_variant_by_index(addrs, i);
515 family_v = sd_json_variant_by_key(addr_item, "family");
516 if (!family_v)
517 continue;
518
519 bytes_v = json_array(addr_item, "address");
520 if (!bytes_v)
521 continue;
522
523 family = (int)sd_json_variant_integer(family_v);
524 nbytes = sd_json_variant_elements(bytes_v);
525 if (family == AF_INET2 && nbytes == sizeof(a.in4))
526 p = (unsigned char *)&a.in4;
527 else if (family == AF_INET610 && nbytes == sizeof(a.in6))
528 p = (unsigned char *)&a.in6;
529 else
530 continue;
531
532 for (j = 0; j < nbytes; j++) {
533 b = sd_json_variant_by_index(bytes_v, j);
534 p[j] = (unsigned char)sd_json_variant_integer(b);
535 }
536 if (!inet_ntop(family, &a, buf, sizeof(buf)))
537 continue;
538
539 if (streq(svc->transport, "tcp")(strcmp((svc->transport),("tcp")) == 0)) {
540 struct sockaddr_storage ss;
541 socklen_t sslen;
542
543 sslen = endpoint_sockaddr(family, &a, port, br->ifindex,
544 &ss);
545 tcp_check_new(br, svc, strdup(buf), strdup(trsvcid),
546 &ss, sslen);
547 } else {
548 report_endpoint(br, svc, strdup(buf), strdup(trsvcid));
549 }
550 }
551
552 free(trsvcid);
553}
554
555static void resolve_req_free(struct resolve_req *req)
556{
557 if (!req)
558 return;
559 list_del_init(&req->entry)list_del_init_(&req->entry, "../discoverd/src/mdns.c" ":"
"559")
;
560 sd_varlink_flush_close_unref(req->link);
561 free(req->name);
562 free(req);
563}
564
565static int resolve_reply_cb(sd_varlink *link __attribute__((unused)),
566 sd_json_variant *parameters, const char *error_id,
567 sd_varlink_reply_flags_t flags
568 __attribute__((unused)),
569 void *userdata)
570{
571 struct resolve_req *req = userdata;
572 struct mdns_browse *br = req->br;
573 sd_json_variant *services;
574 struct mdns_service *svc;
575 const char *transport, *nqn;
576 size_t i, n;
577
578 if (error_id) {
579 log_warn("mdns: %s: resolve '%s': %s", br->ifname, req->name,dmn_log_msg(DMN_LOG_WARN, "mdns: %s: resolve '%s': %s", br->
ifname, req->name, error_id)
580 error_id)dmn_log_msg(DMN_LOG_WARN, "mdns: %s: resolve '%s': %s", br->
ifname, req->name, error_id)
;
581 goto out;
582 }
583
584 services = json_array(parameters, "services");
585 if (!services)
586 goto out;
587
588 if (resolve_txt(parameters, &transport, &nqn) < 0) {
589 log_warn("mdns: %s: resolve '%s': missing/invalid transport in TXT record",dmn_log_msg(DMN_LOG_WARN, "mdns: %s: resolve '%s': missing/invalid transport in TXT record"
, br->ifname, req->name)
590 br->ifname, req->name)dmn_log_msg(DMN_LOG_WARN, "mdns: %s: resolve '%s': missing/invalid transport in TXT record"
, br->ifname, req->name)
;
591 goto out;
592 }
593
594 svc = calloc(1, sizeof(*svc));
595 if (!svc)
596 goto out;
597 list_node_init(&svc->entry); // safe to free even if never list_add()'d
598 list_head_init(&svc->endpoints);
599 list_head_init(&svc->checks);
600 svc->name = req->name;
601 svc->transport = transport;
602 svc->nqn = nqn ? strdup(nqn) : NULL((void*)0);
603 req->name = NULL((void*)0);
604
605 n = sd_json_variant_elements(services);
606 for (i = 0; i < n; i++)
607 add_endpoints(br, svc, sd_json_variant_by_index(services, i));
608
609 if (list_empty(&svc->endpoints)list_empty_(&svc->endpoints, "../discoverd/src/mdns.c"
":" "609")
&& list_empty(&svc->checks)list_empty_(&svc->checks, "../discoverd/src/mdns.c" ":"
"609")
)
610 service_free(br, svc); // nothing resolved
611 else
612 list_add(&br->services, &svc->entry)list_add_(&br->services, &svc->entry, "../discoverd/src/mdns.c"
":" "612")
;
613
614out:
615 resolve_req_free(req);
616 return 0;
617}
618
619static void resolve_service(struct mdns_browse *br, const char *name,
620 const char *type, const char *domain)
621{
622 struct resolve_req *req;
623 int r;
624
625 req = calloc(1, sizeof(*req));
626 if (!req)
627 return;
628 req->br = br;
629 req->name = strdup(name);
630 if (!req->name)
631 goto err_req;
632
633 r = sd_varlink_connect_address(&req->link, RESOLVE_VARLINK_ADDRESS"/run/systemd/resolve/io.systemd.Resolve");
634 if (r < 0) {
635 log_warn("mdns: %s: resolve '%s': connect: %s", br->ifname,dmn_log_msg(DMN_LOG_WARN, "mdns: %s: resolve '%s': connect: %s"
, br->ifname, name, strerror(-r))
636 name, strerror(-r))dmn_log_msg(DMN_LOG_WARN, "mdns: %s: resolve '%s': connect: %s"
, br->ifname, name, strerror(-r))
;
637 goto err_name;
638 }
639
640 r = sd_varlink_attach_event(req->link, br->mctx->event,
641 SD_EVENT_PRIORITY_NORMAL);
642 if (r < 0)
643 goto err_link;
644
645 sd_varlink_set_userdata(req->link, req);
646 r = sd_varlink_bind_reply(req->link, resolve_reply_cb);
647 if (r < 0)
648 goto err_link;
649
650 r = sd_varlink_invokebo(req->link, VARLINK_RESOLVE_SERVICE,sd_varlink_invokeb((req->link), ("io.systemd.Resolve.ResolveService"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "name" }, _SD_JSON_BUILD_STRING, (const char*) { name },
_SD_JSON_BUILD_PAIR, (const char*) { "type" }, _SD_JSON_BUILD_STRING
, (const char*) { type }, _SD_JSON_BUILD_PAIR, (const char*) {
"domain" }, _SD_JSON_BUILD_STRING, (const char*) { domain },
_SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
651 SD_JSON_BUILD_PAIR_STRING("name", name),sd_varlink_invokeb((req->link), ("io.systemd.Resolve.ResolveService"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "name" }, _SD_JSON_BUILD_STRING, (const char*) { name },
_SD_JSON_BUILD_PAIR, (const char*) { "type" }, _SD_JSON_BUILD_STRING
, (const char*) { type }, _SD_JSON_BUILD_PAIR, (const char*) {
"domain" }, _SD_JSON_BUILD_STRING, (const char*) { domain },
_SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
652 SD_JSON_BUILD_PAIR_STRING("type", type),sd_varlink_invokeb((req->link), ("io.systemd.Resolve.ResolveService"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "name" }, _SD_JSON_BUILD_STRING, (const char*) { name },
_SD_JSON_BUILD_PAIR, (const char*) { "type" }, _SD_JSON_BUILD_STRING
, (const char*) { type }, _SD_JSON_BUILD_PAIR, (const char*) {
"domain" }, _SD_JSON_BUILD_STRING, (const char*) { domain },
_SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
653 SD_JSON_BUILD_PAIR_STRING("domain", domain),sd_varlink_invokeb((req->link), ("io.systemd.Resolve.ResolveService"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "name" }, _SD_JSON_BUILD_STRING, (const char*) { name },
_SD_JSON_BUILD_PAIR, (const char*) { "type" }, _SD_JSON_BUILD_STRING
, (const char*) { type }, _SD_JSON_BUILD_PAIR, (const char*) {
"domain" }, _SD_JSON_BUILD_STRING, (const char*) { domain },
_SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
654 SD_JSON_BUILD_PAIR_INTEGER("ifindex",sd_varlink_invokeb((req->link), ("io.systemd.Resolve.ResolveService"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "name" }, _SD_JSON_BUILD_STRING, (const char*) { name },
_SD_JSON_BUILD_PAIR, (const char*) { "type" }, _SD_JSON_BUILD_STRING
, (const char*) { type }, _SD_JSON_BUILD_PAIR, (const char*) {
"domain" }, _SD_JSON_BUILD_STRING, (const char*) { domain },
_SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
655 br->ifindex))sd_varlink_invokeb((req->link), ("io.systemd.Resolve.ResolveService"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "name" }, _SD_JSON_BUILD_STRING, (const char*) { name },
_SD_JSON_BUILD_PAIR, (const char*) { "type" }, _SD_JSON_BUILD_STRING
, (const char*) { type }, _SD_JSON_BUILD_PAIR, (const char*) {
"domain" }, _SD_JSON_BUILD_STRING, (const char*) { domain },
_SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
;
656 if (r < 0)
657 goto err_link;
658
659 list_add(&br->pending, &req->entry)list_add_(&br->pending, &req->entry, "../discoverd/src/mdns.c"
":" "659")
;
660 return;
661
662err_link:
663 sd_varlink_flush_close_unref(req->link);
664err_name:
665 free(req->name);
666err_req:
667 free(req);
668}
669
670/* Is a ResolveService for @name already in flight on @br? */
671static bool_Bool resolve_pending(struct mdns_browse *br, const char *name)
672{
673 struct resolve_req *req;
674
675 list_for_each(&br->pending, req, entry)for ((req) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "675", (&br->pending))->n.next, (((__builtin_offsetof
(typeof(*req), entry) + ((typeof(req->entry) *)0 != (struct
list_node *)0))))); list_node_from_off_((void *)(req), (((__builtin_offsetof
(typeof(*req), entry) + ((typeof(req->entry) *)0 != (struct
list_node *)0))))) != &((&br->pending))->n; (req
) = list_node_to_off_(list_node_from_off_((void *)(req), (((__builtin_offsetof
(typeof(*req), entry) + ((typeof(req->entry) *)0 != (struct
list_node *)0)))))->next, (((__builtin_offsetof(typeof(*req
), entry) + ((typeof(req->entry) *)0 != (struct list_node *
)0))))))
{
676 if (req->name && streq(req->name, name)(strcmp((req->name),(name)) == 0))
677 return true1;
678 }
679 return false0;
680}
681
682static void service_added(struct mdns_browse *br, const char *name,
683 const char *type, const char *domain)
684{
685 /*
686 * A service joins br->services only when its resolve replies. Check
687 * the pending resolves too, or a repeated announcement starts a
688 * second resolve for the same name.
689 */
690 if (service_find(br, name) || resolve_pending(br, name))
691 return;
692
693 resolve_service(br, name, type, domain);
694}
695
696static void service_removed(struct mdns_browse *br, const char *name)
697{
698 struct mdns_service *svc = service_find(br, name);
699
700 if (svc)
701 service_free(br, svc);
702}
703
704static void handle_service_data(struct mdns_browse *br, sd_json_variant *item)
705{
706 const char *update = json_str(item, "updateFlag");
707 const char *name = json_str(item, "name");
708 const char *type = json_str(item, "type");
709 const char *domain = json_str(item, "domain");
710
711 if (!update || !name)
712 return;
713
714 if (streq(update, "added")(strcmp((update),("added")) == 0) && type && domain)
715 service_added(br, name, type, domain);
716 else if (streq(update, "removed")(strcmp((update),("removed")) == 0))
717 service_removed(br, name);
718}
719
720static void browse_schedule_restart(struct mdns_browse *br);
721
722/*
723 * Close the browse call. Every service found through it is reported as
724 * removed. It is found again when the browse restarts.
725 */
726static void browse_disconnect(struct mdns_browse *br)
727{
728 struct mdns_service *svc, *next_svc;
729 struct resolve_req *req, *next_req;
730
731 list_for_each_safe(&br->pending, req, next_req, entry)for ((req) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "731", (&br->pending))->n.next, (((__builtin_offsetof
(typeof(*req), entry) + ((typeof(req->entry) *)0 != (struct
list_node *)0))))), (next_req) = list_node_to_off_(list_node_from_off_
((req), (((__builtin_offsetof(typeof(*req), entry) + ((typeof
(req->entry) *)0 != (struct list_node *)0)))))->next, (
((__builtin_offsetof(typeof(*req), entry) + ((typeof(req->
entry) *)0 != (struct list_node *)0))))); list_node_from_off_
((req), (((__builtin_offsetof(typeof(*req), entry) + ((typeof
(req->entry) *)0 != (struct list_node *)0))))) != &((&
br->pending))->n; (req) = (next_req), (next_req) = list_node_to_off_
(list_node_from_off_((req), (((__builtin_offsetof(typeof(*req
), entry) + ((typeof(req->entry) *)0 != (struct list_node *
)0)))))->next, (((__builtin_offsetof(typeof(*req), entry) +
((typeof(req->entry) *)0 != (struct list_node *)0))))))
732 resolve_req_free(req);
733
734 list_for_each_safe(&br->services, svc, next_svc, entry)for ((svc) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "734", (&br->services))->n.next, (((__builtin_offsetof
(typeof(*svc), entry) + ((typeof(svc->entry) *)0 != (struct
list_node *)0))))), (next_svc) = list_node_to_off_(list_node_from_off_
((svc), (((__builtin_offsetof(typeof(*svc), entry) + ((typeof
(svc->entry) *)0 != (struct list_node *)0)))))->next, (
((__builtin_offsetof(typeof(*svc), entry) + ((typeof(svc->
entry) *)0 != (struct list_node *)0))))); list_node_from_off_
((svc), (((__builtin_offsetof(typeof(*svc), entry) + ((typeof
(svc->entry) *)0 != (struct list_node *)0))))) != &((&
br->services))->n; (svc) = (next_svc), (next_svc) = list_node_to_off_
(list_node_from_off_((svc), (((__builtin_offsetof(typeof(*svc
), entry) + ((typeof(svc->entry) *)0 != (struct list_node *
)0)))))->next, (((__builtin_offsetof(typeof(*svc), entry) +
((typeof(svc->entry) *)0 != (struct list_node *)0))))))
735 service_free(br, svc);
736
737 br->link = sd_varlink_flush_close_unref(br->link);
738}
739
740static int browse_reply_cb(sd_varlink *link __attribute__((unused)),
741 sd_json_variant *parameters, const char *error_id,
742 sd_varlink_reply_flags_t flags, void *userdata)
743{
744 struct mdns_browse *br = userdata;
745 sd_json_variant *arr;
746 size_t i, n;
747
748 if (error_id) {
749 log_warn("mdns: %s: browsing %s failed: %s, restarting",dmn_log_msg(DMN_LOG_WARN, "mdns: %s: browsing %s failed: %s, restarting"
, br->ifname, br->type, error_id)
750 br->ifname, br->type, error_id)dmn_log_msg(DMN_LOG_WARN, "mdns: %s: browsing %s failed: %s, restarting"
, br->ifname, br->type, error_id)
;
751 goto restart;
752 }
753
754 arr = json_array(parameters, "browserServiceData");
755 if (!arr)
756 log_warn("mdns: %s: BrowseServices reply without browserServiceData",dmn_log_msg(DMN_LOG_WARN, "mdns: %s: BrowseServices reply without browserServiceData"
, br->ifname)
757 br->ifname)dmn_log_msg(DMN_LOG_WARN, "mdns: %s: BrowseServices reply without browserServiceData"
, br->ifname)
;
758 n = arr ? sd_json_variant_elements(arr) : 0;
759 for (i = 0; i < n; i++)
760 handle_service_data(br, sd_json_variant_by_index(arr, i));
761
762 if (flags & SD_VARLINK_REPLY_CONTINUES)
763 return 0;
764
765 log_warn("mdns: %s: browsing %s ended, restarting", br->ifname,dmn_log_msg(DMN_LOG_WARN, "mdns: %s: browsing %s ended, restarting"
, br->ifname, br->type)
766 br->type)dmn_log_msg(DMN_LOG_WARN, "mdns: %s: browsing %s ended, restarting"
, br->ifname, br->type)
;
767restart:
768 browse_disconnect(br);
769 browse_schedule_restart(br);
770
771 return 0;
772}
773
774/* Open a link to systemd-resolved and start the BrowseServices call. */
775static int browse_connect(struct mdns_browse *br)
776{
777 int r;
778
779 r = sd_varlink_connect_address(&br->link, RESOLVE_VARLINK_ADDRESS"/run/systemd/resolve/io.systemd.Resolve");
780 if (r < 0)
781 return r;
782
783 /*
784 * The default 45 s timeout also applies to an observe call, counted
785 * from when it was sent. Browsing must run until stopped.
786 */
787 r = sd_varlink_set_relative_timeout(br->link, SHR_USEC_INFINITY(18446744073709551615UL));
788 if (r < 0)
789 goto err;
790
791 r = sd_varlink_attach_event(br->link, br->mctx->event,
792 SD_EVENT_PRIORITY_NORMAL);
793 if (r < 0)
794 goto err;
795
796 sd_varlink_set_userdata(br->link, br);
797 r = sd_varlink_bind_reply(br->link, browse_reply_cb);
798 if (r < 0)
799 goto err;
800
801 r = sd_varlink_observebo(br->link, VARLINK_BROWSE_SERVICES,sd_varlink_observeb((br->link), ("io.systemd.Resolve.BrowseServices"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "type" }, _SD_JSON_BUILD_STRING, (const char*) { br->
type }, _SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
802 SD_JSON_BUILD_PAIR_STRING("type", br->type),sd_varlink_observeb((br->link), ("io.systemd.Resolve.BrowseServices"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "type" }, _SD_JSON_BUILD_STRING, (const char*) { br->
type }, _SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
803 SD_JSON_BUILD_PAIR_INTEGER("ifindex",sd_varlink_observeb((br->link), ("io.systemd.Resolve.BrowseServices"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "type" }, _SD_JSON_BUILD_STRING, (const char*) { br->
type }, _SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
804 br->ifindex))sd_varlink_observeb((br->link), ("io.systemd.Resolve.BrowseServices"
), _SD_JSON_BUILD_OBJECT_BEGIN, _SD_JSON_BUILD_PAIR, (const char
*) { "type" }, _SD_JSON_BUILD_STRING, (const char*) { br->
type }, _SD_JSON_BUILD_PAIR, (const char*) { "ifindex" }, _SD_JSON_BUILD_INTEGER
, (int64_t) { br->ifindex }, _SD_JSON_BUILD_OBJECT_END)
;
805 if (r < 0)
806 goto err;
807
808 return 0;
809err:
810 br->link = sd_varlink_flush_close_unref(br->link);
811
812 return r;
813}
814
815static int browse_restart_cback(sd_event_source *s __attribute__((unused)),
816 uint64_t usec __attribute__((unused)),
817 void *userdata)
818{
819 struct mdns_browse *br = userdata;
820 int r;
821
822 br->restart_timer = sd_event_source_unref(br->restart_timer);
823
824 r = browse_connect(br);
825 if (r < 0) {
826 log_dbg("mdns: %s: browsing %s: %s", br->ifname, br->type,dmn_log_msg(DMN_LOG_DEBUG, "%s() - " "mdns: %s: browsing %s: %s"
, __func__, br->ifname, br->type, strerror(-r))
827 strerror(-r))dmn_log_msg(DMN_LOG_DEBUG, "%s() - " "mdns: %s: browsing %s: %s"
, __func__, br->ifname, br->type, strerror(-r))
;
828 browse_schedule_restart(br);
829 return 0;
830 }
831
832 br->restart_sec = BROWSE_RESTART_MIN_SEC1;
833 log_info("mdns: browsing %s for %s again", br->ifname, br->type)dmn_log_msg(DMN_LOG_INFO, "mdns: browsing %s for %s again", br
->ifname, br->type)
;
834
835 return 0;
836}
837
838static void browse_schedule_restart(struct mdns_browse *br)
839{
840 uint64_t now;
841 int r;
842
843 r = sd_event_now(br->mctx->event, CLOCK_BOOTTIME7, &now);
844 if (r >= 0)
845 r = sd_event_add_time(br->mctx->event, &br->restart_timer,
846 CLOCK_BOOTTIME7,
847 now + br->restart_sec * UINT64_C(1000000)1000000UL,
848 0, browse_restart_cback, br);
849 if (r < 0) {
850 log_err("mdns: %s: browsing %s: cannot arm restart timer: %s",dmn_log_msg(DMN_LOG_ERR, "mdns: %s: browsing %s: cannot arm restart timer: %s"
, br->ifname, br->type, strerror(-r))
851 br->ifname, br->type, strerror(-r))dmn_log_msg(DMN_LOG_ERR, "mdns: %s: browsing %s: cannot arm restart timer: %s"
, br->ifname, br->type, strerror(-r))
;
852 return;
853 }
854
855 br->restart_sec *= 2;
856 if (br->restart_sec > BROWSE_RESTART_MAX_SEC60)
857 br->restart_sec = BROWSE_RESTART_MAX_SEC60;
858}
859
860static struct mdns_browse *browse_find(struct mdns_ctx *mctx, int ifindex,
861 const char *type)
862{
863 struct mdns_browse *br;
864
865 list_for_each(&mctx->browses, br, entry)for ((br) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "865", (&mctx->browses))->n.next, (((__builtin_offsetof
(typeof(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0))))); list_node_from_off_((void *)(br), (((__builtin_offsetof
(typeof(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0))))) != &((&mctx->browses))->n; (br) = list_node_to_off_
(list_node_from_off_((void *)(br), (((__builtin_offsetof(typeof
(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0)))))->next, (((__builtin_offsetof(typeof(*br), entry)
+ ((typeof(br->entry) *)0 != (struct list_node *)0))))))
{
9
Use of memory after it is released
866 if (br->ifindex == ifindex && streq(br->type, type)(strcmp((br->type),(type)) == 0))
867 return br;
868 }
869 return NULL((void*)0);
870}
871
872static void browse_free(struct mdns_browse *br)
873{
874 if (!br
3.1
'br' is non-null
)
4
Taking false branch
875 return;
876
877 browse_disconnect(br);
878 sd_event_source_unref(br->restart_timer);
879 list_del_init(&br->entry)list_del_init_(&br->entry, "../discoverd/src/mdns.c" ":"
"879")
;
880 free(br->ifname);
881 free(br);
5
Memory is released
882}
883
884/*
885 * Start browsing @ifname for @type. If systemd-resolved is not reachable,
886 * the browse is retried later.
887 */
888static void browse_start(struct mdns_ctx *mctx, int ifindex,
889 const char *ifname, const char *type)
890{
891 struct mdns_browse *br;
892 int r;
893
894 br = calloc(1, sizeof(*br));
895 if (!br)
896 return;
897 list_node_init(&br->entry); // safe to free even if never list_add()'d
898 list_head_init(&br->services);
899 list_head_init(&br->pending);
900 br->mctx = mctx;
901 br->ifindex = ifindex;
902 br->type = type;
903 br->restart_sec = BROWSE_RESTART_MIN_SEC1;
904 br->ifname = strdup(ifname);
905 if (!br->ifname) {
906 browse_free(br);
907 return;
908 }
909 list_add(&mctx->browses, &br->entry)list_add_(&mctx->browses, &br->entry, "../discoverd/src/mdns.c"
":" "909")
;
910
911 r = browse_connect(br);
912 if (r < 0) {
913 log_warn("mdns: %s: browsing %s: %s, retrying", ifname, type,dmn_log_msg(DMN_LOG_WARN, "mdns: %s: browsing %s: %s, retrying"
, ifname, type, strerror(-r))
914 strerror(-r))dmn_log_msg(DMN_LOG_WARN, "mdns: %s: browsing %s: %s, retrying"
, ifname, type, strerror(-r))
;
915 browse_schedule_restart(br);
916 return;
917 }
918
919 log_info("mdns: browsing %s for %s", ifname, type)dmn_log_msg(DMN_LOG_INFO, "mdns: browsing %s for %s", ifname,
type)
;
920}
921
922static void on_iface_add(int ifindex, const char *ifname, void *user_data)
923{
924 struct mdns_ctx *mctx = user_data;
925 size_t i;
926
927 for (i = 0; i < ARRAY_SIZE(mdns_service_types)(sizeof(mdns_service_types) / sizeof((mdns_service_types)[0])
+ 0)
; i++) {
928 const char *type = mdns_service_types[i];
929
930 if (!browse_find(mctx, ifindex, type))
931 browse_start(mctx, ifindex, ifname, type);
932 }
933}
934
935static void on_iface_remove(int ifindex,
936 const char *ifname __attribute__((unused)),
937 void *user_data)
938{
939 struct mdns_ctx *mctx = user_data;
940 size_t i;
941
942 for (i = 0; i < ARRAY_SIZE(mdns_service_types)(sizeof(mdns_service_types) / sizeof((mdns_service_types)[0])
+ 0)
; i++) {
1
Loop condition is true. Entering loop body
7
Loop condition is true. Entering loop body
943 struct mdns_browse *br =
944 browse_find(mctx, ifindex, mdns_service_types[i]);
8
Calling 'browse_find'
945
946 if (br
1.1
'br' is non-null
)
2
Taking true branch
947 browse_free(br);
3
Calling 'browse_free'
6
Returning; memory was released via 1st parameter
948 }
949}
950
951int mdns_start(sd_event *event, const struct mdns_callbacks *callbacks,
952 void *user_data, struct mdns_ctx **mctxp)
953{
954 static const struct netif_callbacks netif_cbs = {
955 .iface_add = on_iface_add,
956 .iface_remove = on_iface_remove,
957 };
958 struct mdns_ctx *mctx;
959 sd_varlink *probe;
960 int r;
961
962 r = sd_varlink_connect_address(&probe, RESOLVE_VARLINK_ADDRESS"/run/systemd/resolve/io.systemd.Resolve");
963 if (r < 0)
964 return r;
965
966 r = resolved_has_browse_services(probe);
967 sd_varlink_flush_close_unref(probe);
968 if (r < 0)
969 return r;
970
971 mctx = calloc(1, sizeof(*mctx));
972 if (!mctx)
973 return -ENOMEM12;
974 mctx->event = event;
975 mctx->callbacks = *callbacks;
976 mctx->user_data = user_data;
977 list_head_init(&mctx->browses);
978
979 r = netif_start(event, &netif_cbs, mctx, &mctx->nctx);
980 if (r < 0) {
981 mdns_stop(mctx);
982 return r;
983 }
984
985 *mctxp = mctx;
986
987 return 0;
988}
989
990void mdns_stop(struct mdns_ctx *mctx)
991{
992 struct mdns_browse *br, *next;
993
994 if (!mctx)
995 return;
996
997 netif_stop(mctx->nctx);
998
999 list_for_each_safe(&mctx->browses, br, next, entry)for ((br) = list_node_to_off_(((void)"../discoverd/src/mdns.c"
":" "999", (&mctx->browses))->n.next, (((__builtin_offsetof
(typeof(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0))))), (next) = list_node_to_off_(list_node_from_off_((br
), (((__builtin_offsetof(typeof(*br), entry) + ((typeof(br->
entry) *)0 != (struct list_node *)0)))))->next, (((__builtin_offsetof
(typeof(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0))))); list_node_from_off_((br), (((__builtin_offsetof(typeof
(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0))))) != &((&mctx->browses))->n; (br) = (next
), (next) = list_node_to_off_(list_node_from_off_((br), (((__builtin_offsetof
(typeof(*br), entry) + ((typeof(br->entry) *)0 != (struct list_node
*)0)))))->next, (((__builtin_offsetof(typeof(*br), entry)
+ ((typeof(br->entry) *)0 != (struct list_node *)0))))))
1000 browse_free(br);
1001
1002 free(mctx);
1003}