int mdns_serve(struct mdns_ctx *ctx, mdns_stop_func stop, void *p_cookie) { int r; struct mdns_svc *svc; struct mdns_hdr qhdr = {0}; struct rr_entry *question; if (setsockopt(ctx->sock, SOL_SOCKET, SO_RCVTIMEO, (const void *) &os_deadline, sizeof(os_deadline)) < 0) return (MDNS_NETERR); if (setsockopt(ctx->sock, SOL_SOCKET, SO_SNDTIMEO, (const void *) &os_deadline, sizeof(os_deadline)) < 0) return (MDNS_NETERR); for (; stop(p_cookie) == false;) { r = mdns_recv(ctx, &qhdr, &question); if (r == MDNS_NETERR) continue; if (qhdr.num_qn == 0) goto again; for (svc = ctx->services; svc; svc = svc->next) { if (!strrcmp(question->name, svc->name) && question->type == svc->type) { svc->callback(svc->p_cookie, r, question); goto again; } } again: mdns_free(question); } return (0); }
int mdns_recv(const struct mdns_ctx *ctx, struct mdns_hdr *hdr, struct rr_entry **entries) { uint8_t buf[MDNS_PKT_MAXSZ]; size_t num_entry, n; ssize_t length; struct rr_entry *entry; *entries = NULL; if ((length = recv(ctx->sock, (char *) buf, sizeof(buf), 0)) < 0) return (MDNS_NETERR); const uint8_t *ptr = mdns_read_header(buf, length, hdr); num_entry = hdr->num_qn + hdr->num_ans_rr + hdr->num_add_rr; for (size_t i = 0; i < num_entry; ++i) { entry = calloc(1, sizeof(struct rr_entry)); if (!entry) goto err; ptr = rr_read(ptr, &n, buf, entry, (hdr->flags & FLAG_QR) > 0); if (!ptr) { errno = ENOSPC; goto err; } entry->next = *entries; *entries = entry; } if (*entries == NULL) { return (MDNS_STDERR); } return (0); err: mdns_free(*entries); return (MDNS_STDERR); }
int mdns_listen(const struct mdns_ctx *ctx, const char *name, enum rr_type type, unsigned int interval, mdns_stop_func stop, mdns_callback callback, void *p_cookie) { int r; time_t t1, t2; struct mdns_hdr hdr = {0}; struct rr_entry qn = {0}; hdr.num_qn = 1; qn.name = (char *)name; qn.type = type; qn.rr_class = RR_IN; if (setsockopt(ctx->sock, SOL_SOCKET, SO_RCVTIMEO, (const void *) &os_deadline, sizeof(os_deadline)) < 0) return (MDNS_NETERR); if (setsockopt(ctx->sock, SOL_SOCKET, SO_SNDTIMEO, (const void *) &os_deadline, sizeof(os_deadline)) < 0) return (MDNS_NETERR); if ((r = mdns_send(ctx, &hdr, &qn)) < 0) // send a first probe request callback(p_cookie, r, NULL); for (t1 = t2 = time(NULL); stop(p_cookie) == false; t2 = time(NULL)) { struct mdns_hdr ahdr = {0}; struct rr_entry *entries; if (difftime(t2, t1) >= (double) interval) { if ((r = mdns_send(ctx, &hdr, &qn)) < 0) { callback(p_cookie, r, NULL); continue; } t1 = t2; } r = mdns_recv(ctx, &ahdr, &entries); if (r == MDNS_NETERR && os_wouldblock()) continue; if (ahdr.num_ans_rr + ahdr.num_add_rr == 0) continue; for (struct rr_entry *entry = entries; entry; entry = entry->next) { if (!strrcmp(entry->name, name)) { callback(p_cookie, r, entries); break; } } mdns_free(entries); } return (0); }
int mdns_listen(const struct mdns_ctx *ctx, const char *const names[], unsigned int nb_names, enum rr_type type, unsigned int interval, mdns_stop_func stop, mdns_callback callback, void *p_cookie) { int r; time_t t1, t2; struct mdns_hdr hdr = {0}; struct rr_entry *qns = malloc(nb_names * sizeof(struct rr_entry)); if (qns == NULL) return (MDNS_STDERR); memset(qns, 0, nb_names * sizeof(struct rr_entry)); hdr.num_qn = nb_names; for (unsigned int i = 0; i < nb_names; ++i) { qns[i].name = (char *)names[i]; qns[i].type = type; qns[i].rr_class = RR_IN; if (i + 1 < nb_names) qns[i].next = &qns[i+1]; } if (setsockopt(ctx->sock, SOL_SOCKET, SO_RCVTIMEO, (const void *) &os_deadline, sizeof(os_deadline)) < 0) { free(qns); return (MDNS_NETERR); } if (setsockopt(ctx->sock, SOL_SOCKET, SO_SNDTIMEO, (const void *) &os_deadline, sizeof(os_deadline)) < 0) { free(qns); return (MDNS_NETERR); } if ((r = mdns_entries_send(ctx, &hdr, qns)) < 0) // send a first probe request callback(p_cookie, r, NULL); for (t1 = t2 = time(NULL); stop(p_cookie) == false; t2 = time(NULL)) { struct mdns_hdr ahdr = {0}; struct rr_entry *entries; if (difftime(t2, t1) >= (double) interval) { if ((r = mdns_entries_send(ctx, &hdr, qns)) < 0) { callback(p_cookie, r, NULL); continue; } t1 = t2; } r = mdns_recv(ctx, &ahdr, &entries); if (r == MDNS_NETERR && os_wouldblock()) { mdns_free(entries); continue; } if (ahdr.num_ans_rr + ahdr.num_add_rr == 0) { mdns_free(entries); continue; } for (struct rr_entry *entry = entries; entry; entry = entry->next) { for (unsigned int i = 0; i < nb_names; ++i) { if (!strrcmp(entry->name, names[i])) { callback(p_cookie, r, entries); break; } } } mdns_free(entries); } free(qns); return (0); }