diff --git a/mdnsd.c b/mdnsd.c index 0472c6f..15a5a85 100644 --- a/mdnsd.c +++ b/mdnsd.c @@ -554,6 +554,32 @@ static int create_recv_sock(int domain) return sd; } +static ssize_t send_packet_to(int fd, const void *data, size_t len, struct sockaddr_in *toaddr) { + + return sendto(fd, data, len, 0, (struct sockaddr *) toaddr, sizeof(struct sockaddr_in)); +} + +// populate the specified list which matches the RR name and type +static int populate_query(struct mdnsd *svr, struct rr_list **rr_head) +{ + int num_qns = 0; + + // check if we have the records + pthread_mutex_lock(&svr->data_lock); + + while (svr->query) { + struct rr_entry *qn_e = rr_list_remove(&svr->query, svr->query->e); + if (qn_e == NULL) { + break; + } + num_qns += rr_list_append(rr_head, qn_e); + } + + pthread_mutex_unlock(&svr->data_lock); + + return num_qns; +} + static ssize_t send_packet(int fd, const void *data, size_t len, int domain) { static struct sockaddr_in toaddr; char *addr; @@ -578,28 +604,7 @@ static ssize_t send_packet(int fd, const void *data, size_t len, int domain) { toaddr.sin_port = htons(port); toaddr.sin_addr.s_addr = inet_addr(addr); - return sendto(fd, data, len, 0, (struct sockaddr *) &toaddr, sizeof(struct sockaddr_in)); -} - -// populate the specified list which matches the RR name and type -static int populate_query(struct mdnsd *svr, struct rr_list **rr_head) -{ - int num_qns = 0; - - // check if we have the records - pthread_mutex_lock(&svr->data_lock); - - while (svr->query) { - struct rr_entry *qn_e = rr_list_remove(&svr->query, svr->query->e); - if (qn_e == NULL) { - break; - } - num_qns += rr_list_append(rr_head, qn_e); - } - - pthread_mutex_unlock(&svr->data_lock); - - return num_qns; + return send_packet_to( fd, data, len, &toaddr); } #ifndef MDNS_NO_RESPONDER_SUPPORT @@ -902,7 +907,7 @@ out_with_mutex: // processes the incoming MDNS packet // returns >0 if processed, 0 otherwise -static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns_pkt *reply) { +static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct sockaddr_in *fromtoaddr, struct mdns_pkt *reply) { int result; assert(pkt != NULL); @@ -919,6 +924,15 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns pkt->num_ans_rr, pkt->num_add_rr); + int unicast = 0; /* 0, 1, 2, 4 = no, direct unicast, legacy, unicast-response */ + if (fromtoaddr->sin_port != htons(MDNS_PORT)) { + /* legacy query */ + unicast |= 2; + } else if (fromtoaddr->sin_addr.s_addr != inet_addr(MDNS_ADDR)) { + /* direct unicast - send back to same addr:port */ + unicast |= 1; + } + // loop through questions struct rr_list *qnl = pkt->rr_qn; for (int i = 0; i < pkt->num_qn; i++, qnl = qnl->next) { @@ -929,10 +943,10 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns DEBUG_PRINTF("qn #%d: type %s (%02x) %s - ", i, rr_get_type_name(qn->type), qn->type, namestr); MDNS_FREE(namestr); - // check if it's a unicast query - we ignore those + // check if it's a unicast query - we don't treat those differently if (qn->unicast_query) { - DEBUG_PRINTF("skipping unicast query\n"); - continue; + DEBUG_PRINTF("treating unicast query as multicast\n"); + unicast |= 4; } num_ans_added = populate_answers(svr, &reply->rr_ans, qn->name, qn->type); @@ -943,7 +957,7 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns // remove our replies if they were already in their answers struct rr_list *prev_ans = NULL; - for (struct rr_list *ans = reply->rr_ans; ans; ) { + for (struct rr_list *ans = reply->rr_ans; ans;) { struct rr_list *next_ans = ans->next; struct rr_entry *known_ans = rr_entry_match(pkt->rr_ans, ans->e); @@ -965,12 +979,11 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns // adjust answer count reply->num_ans_rr--; } - + prev_ans = ans; ans = next_ans; } - // see if we can match additional records for answers add_related_rr(svr, reply->rr_ans, reply); @@ -979,6 +992,29 @@ static int process_mdns_pkt(struct mdnsd *svr, struct mdns_pkt *pkt, struct mdns DEBUG_PRINTF("\n"); + /* specific processing for unicast, ... or not */ + if (unicast & 2) { + /* legacy: (rfc6762#section-5.4) send back to same addr:port, no cache flush, TTL -> 10s */ + struct rr_list *rr_grps[] = { reply->rr_ans, reply->rr_add, NULL }; + for (struct rr_list **rrl = rr_grps; *rrl; ++rrl) { + for (struct rr_list *ans = *rrl; ans; ans = ans->next) { + ans->e->cache_flush = 0; + ans->e->ttl = DEFAULT_TTL_LEGACY; + } + } + } else if (unicast & 1) { + /* direct: (rfc6762#section-5.5) should be sending answers to same addr:5353 if on LAN, then multicast */ + /* let's pretend the first reply got lost ... */ + fromtoaddr->sin_port = 0; + } else if (unicast & 4) { + /* unicast-response: (rfc6762#section-5.4) should be sending answers to QUs to same addr:5353 if on LAN, then multicast */ + /* let's pretend the first reply got lost ... */ + fromtoaddr->sin_port = 0; + } else { + /* send usual multicast response */ + fromtoaddr->sin_port = 0; + } + return reply->num_ans_rr; } else { result = 0; @@ -1162,6 +1198,9 @@ static void main_loop(struct mdnsd *svr) { max_fd = svr->notify_pipe[0]; while (1) { + struct sockaddr_in fromaddr; + socklen_t sockaddr_size = sizeof(struct sockaddr_in); + svr->sendmsg_requested = 0; FD_ZERO(&sockfd_set); @@ -1176,9 +1215,6 @@ static void main_loop(struct mdnsd *svr) { if (read_pipe(svr->notify_pipe[0], (char *)¬ify_buf, 1) == -1) log_message(LOG_ERR, "read_pipe() failed; %s\n", strerror(errno)); } else if (FD_ISSET(svr->sockfd, &sockfd_set)) { - struct sockaddr_in fromaddr; - socklen_t sockaddr_size = sizeof(struct sockaddr_in); - ssize_t recvsize = recvfrom(svr->sockfd, pkt_buffer, PACKET_SIZE, 0, (struct sockaddr *) &fromaddr, &sockaddr_size); if (recvsize < 0) { @@ -1198,7 +1234,7 @@ static void main_loop(struct mdnsd *svr) { if (svr->sockfd != -1) { /* succeed to create recv socket */ break; - } + } remaining_cnt--; } if (svr->sockfd == -1) { @@ -1216,13 +1252,20 @@ static void main_loop(struct mdnsd *svr) { continue; } - DEBUG_PRINTF("data from=%s size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize); + DEBUG_PRINTF("data from=%s:%d size=%ld\n", inet_ntoa(fromaddr.sin_addr), (long) recvsize); struct mdns_pkt *mdns = mdns_parse_pkt(pkt_buffer, recvsize); if (mdns != NULL) { - if (process_mdns_pkt(svr, mdns, mdns_packet)) { + struct sockaddr_in toaddr = fromaddr; + if (process_mdns_pkt(svr, mdns, &toaddr, mdns_packet)) { #ifndef MDNS_NO_RESPONDER_SUPPORT size_t replylen = mdns_encode_pkt(mdns_packet, pkt_buffer, PACKET_SIZE); - if (send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain) == -1) + ssize_t ret = -1; + if (toaddr.sin_port != 0) { + ret = send_packet_to(svr->sockfd, pkt_buffer, replylen, &toaddr); + } else { + ret = send_packet(svr->sockfd, pkt_buffer, replylen, svr->domain); + } + if (ret == -1) log_message(LOG_ERR, "send_packet() failed; %s\n", strerror(errno)); #endif } else if (mdns->num_qn == 0) {