1#include <string.h> 2#include <stdio.h> 3 4#include <avahi-common/timeval.h> 5#include <avahi-common/malloc.h> 6 7#include "log.h" 8#include "rrlist.h" 9 10#include "llmnr-query-sched.h" 11#include "dns.h" 12#include "verify.h" 13 14AvahiLLMNRQueryScheduler *avahi_llmnr_query_scheduler_new(AvahiInterface *i) { 15 AvahiLLMNRQueryScheduler *s; 16 assert(i); 17 18 if (!(s = avahi_new(AvahiLLMNRQueryScheduler, 1))) 19 return NULL; 20 21 s->i = i; 22 s->time_event_queue = i->monitor->server->time_event_queue; 23 s->next_id = 1; 24 AVAHI_LLIST_HEAD_INIT(AvahiLLMNRQueryJob, s->jobs); 25 26 return s; 27} 28 29void avahi_llmnr_query_scheduler_free(AvahiLLMNRQueryScheduler *s) { 30 assert(s); 31 32 avahi_llmnr_query_scheduler_clear(s); 33 avahi_free(s); 34} 35 36void avahi_llmnr_query_scheduler_clear(AvahiLLMNRQueryScheduler *s) { 37 assert(s); 38 39 while (s->jobs) 40 avahi_llmnr_query_job_destroy(s, s->jobs); 41} 42 43void avahi_llmnr_query_job_destroy(AvahiLLMNRQueryScheduler *s, AvahiLLMNRQueryJob *qj) { 44 assert(s); 45 assert(qj); 46 47 /* Free lq */ 48 avahi_llmnr_query_destroy(qj->lq); 49 50 /* Free dns packet and time_event */ 51 if (qj->p) 52 avahi_dns_packet_free(qj->p); 53 54 if (qj->time_event) 55 avahi_time_event_free(qj->time_event); 56 57 /* Remove from the lists */ 58 AVAHI_LLIST_REMOVE(AvahiLLMNRQueryJob, jobs_by_scheduler, s->jobs, qj); 59 AVAHI_LLIST_REMOVE(AvahiLLMNRQueryJob, jobs_by_interface, s->i->llmnr.queryjobs, qj); 60 61 avahi_free(qj); 62} 63 64static void avahi_prepare_llmnr_query_job_packet(AvahiLLMNRQueryJob *qj) { 65 assert(qj); 66 67 /* New Packet*/ 68 qj->p = avahi_llmnr_packet_new_query(512 + AVAHI_DNS_PACKET_EXTRA_SIZE); 69 70 /* Set ID*/ 71 avahi_dns_packet_set_field(qj->p, AVAHI_LLMNR_FIELD_ID, (uint16_t)(qj->lq->id)); 72 73 /*Append Key*/ 74 if (!avahi_llmnr_packet_append_key(qj->p, qj->lq->key)) 75 return; 76 77 /* Set QDCOUNT */ 78 avahi_dns_packet_set_field(qj->p, AVAHI_LLMNR_FIELD_QDCOUNT, 1); 79 80} 81 82static AvahiLLMNRQueryJob *job_new(AvahiLLMNRQueryScheduler *s, AvahiLLMNRQuery *lq) { 83 AvahiLLMNRQueryJob *qj; 84 85 assert(s); 86 assert(lq); 87 88 if (!(qj = avahi_new(AvahiLLMNRQueryJob, 1))) 89 return NULL; 90 91 qj->scheduler = s; 92 qj->lq = lq; 93 94 /* Set lq parameters */ 95 lq->post_id_valid = 1; 96 lq->post_id = s->next_id++; 97 98 /* qj parameters */ 99 qj->n_sent = 0; 100 qj->prev_scheduled = 0; 101 qj->time_event = NULL; 102 103 /* Prepend in Lists */ 104 AVAHI_LLIST_PREPEND(AvahiLLMNRQueryJob, jobs_by_scheduler, s->jobs, qj); 105 AVAHI_LLIST_PREPEND(AvahiLLMNRQueryJob, jobs_by_interface, s->i->llmnr.queryjobs, qj); 106 107 /*Just prepare dns packet, don't send it */ 108 avahi_prepare_llmnr_query_job_packet(qj); 109 110 return qj; 111} 112 113 114static void resend_llmnr_query(AvahiLLMNRQueryJob *qj) { 115 AvahiLLMNRQuery *lq = qj->lq; 116 struct timeval tv; 117 assert(qj); 118 119 if (lq->type == AVAHI_LLMNR_SIMPLE_QUERY || lq->type == AVAHI_LLMNR_UNIQUENESS_VERIFICATION_QUERY) { 120 /**Check whether we have already sent this query three times */ 121 if (qj->n_sent >= 3) { 122 lq->callback(lq->interface->hardware->index, lq->interface->protocol, NULL, lq->userdata); 123 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 124 /* Free Timeevent */ 125/* avahi_time_event_free(qj->time_event); 126 qj->time_event = NULL;*/ 127 128 } else { 129 130 /* Send packet */ 131 avahi_interface_send_packet(lq->interface, qj->p, AVAHI_LLMNR); 132 (qj->n_sent)++; 133 /* Schedule further queries*/ 134 avahi_elapse_time(&tv, AVAHI_LLMNR_INTERVAL, 0); 135 avahi_time_event_update(qj->time_event, &tv); 136 } 137 138 } else { 139 140 assert(lq->type == AVAHI_LLMNR_CONFLICT_QUERY); 141 assert(qj->n_sent == 1); 142 143 /* Destroy this query */ 144 lq->callback(lq->interface->hardware->index, lq->interface->protocol, NULL, lq->userdata); 145 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 146 } 147 148 return; 149} 150 151static void reschedule_llmnr_query_job(AvahiLLMNRQueryJob *qj) { 152 struct timeval tv; 153 154 assert(qj); 155 assert(!avahi_record_list_is_empty(qj->lq->c_bit_clear)); 156 157 if (!(qj->prev_scheduled)) { 158 159 qj->prev_scheduled = 1; 160 avahi_elapse_time(&tv, AVAHI_LLMNR_INTERVAL + AVAHI_LLMNR_JITTER, 0); 161 avahi_time_event_update(qj->time_event, &tv); 162 163 } else { 164 /* We have already waited but we still have not received any response/s 165 with 'c' bit clear. */ 166 qj->lq->callback(qj->lq->interface->hardware->index, qj->lq->interface->protocol, NULL, qj->lq->userdata); 167 /*avahi_time_event_free(qj->time_event); 168 qj->time_event = NULL;*/ 169 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 170 } 171 return; 172} 173 174static void call_llmnr_query_callback(AvahiLLMNRQuery *lq) { 175 AvahiRecord *r; 176 177 assert(lq); 178 assert(avahi_record_list_is_empty(lq->c_bit_set)); 179 180 while ((r = avahi_record_list_next(lq->c_bit_clear, NULL, NULL, NULL))) 181 lq->callback(lq->interface->hardware->index, lq->interface->protocol, r, lq->userdata); 182 183 return; 184} 185 186static void send_conflict_query(AvahiLLMNRQueryJob *qj) { 187 /* Send the packet */ 188 AvahiRecord *r; 189 AvahiLLMNRQuery *lq; 190 assert(qj); 191 192 /* We use the same lq */ 193 194 /*1. Change 'lq->type' and reset 'n_sent' */ 195 lq = qj->lq; 196 lq->callback(lq->interface->hardware->index, lq->interface->protocol, NULL, lq->userdata); 197 lq->type = AVAHI_LLMNR_CONFLICT_QUERY; 198 qj->n_sent = 0; 199 200 /* Reset 'c' bit in existing packet */ 201 avahi_dns_packet_set_field(qj->p, AVAHI_LLMNR_FIELD_FLAGS, AVAHI_LLMNR_FLAGS(0, 0, 1, 0, 0, 0, 0)); 202 203 /* Append records */ 204 while ((r = avahi_record_list_next(lq->c_bit_clear, NULL, NULL, NULL))) { 205 /* Append Record TODO TTL*/ 206 if (!(avahi_llmnr_packet_append_record(qj->p, r, AVAHI_DEFAULT_TTL))) 207 /* we send only those much of record which fits in */ 208 break; 209 avahi_dns_packet_inc_field(qj->p, AVAHI_LLMNR_FIELD_ARCOUNT); 210 } 211 212 /* Send packet */ 213 if (avahi_dns_packet_get_field(qj->p, AVAHI_LLMNR_FIELD_ARCOUNT) != 0) 214 avahi_interface_send_packet(lq->interface, qj->p, AVAHI_LLMNR); 215 216 /* Destroy this AvahiLLMNRQueryJob */ 217 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 218 219 return; 220 221} 222 223static void elapse_timeout_callback(AVAHI_GCC_UNUSED AvahiTimeEvent *e, void *userdata) { 224 AvahiLLMNRQueryJob *qj = userdata; 225 AvahiLLMNRQuery *lq = qj->lq; 226 int c_bit_set, c_bit_clear, counter; 227 228 c_bit_set = avahi_record_list_is_empty(lq->c_bit_set); 229 c_bit_clear = avahi_record_list_is_empty(lq->c_bit_clear); 230 231 counter = (2*(!c_bit_set)) + (!c_bit_clear); 232 233 switch (counter)/*TODO, clean it*/ { 234 235 case 0 : 236 /*We have not yet received any responses. Try to resend the same query*/ 237 resend_llmnr_query(qj); 238 break; 239 240 case 1 : 241 /*We have received one or multiple reponse/s with 'c' bit clear 242 and none with 'c' bit set within AVAHI_LLMNR_TIMEOUT. It means 243 query has been replied. */ 244 call_llmnr_query_callback(lq); 245 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 246 /*avahi_time_event_free(qj->time_event); 247 qj->time_event = NULL;*/ 248 break; 249 250 case 2 : 251 /*We have received atleast one response with 'c' bit set but we didn't 252 receive any response with 'c' bit clear. We don't want to send this query 253 further but wait for atleast LLMNR_TIMEOUT + JITTER_INTERVAL to collect 254 more responses, if any */ 255 reschedule_llmnr_query_job(qj); 256 break; 257 258 case 3 : 259 /*This is conflict. Let us send AVAHI_LLMNR_CONFLICT_QUERY (with c bit set 260 in query) along with responses*/ 261 avahi_log_info("CONFLICT..CONFLICT..CONFLICT"); 262 send_conflict_query(qj); 263 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 264 break; 265 } 266 267 return; 268} 269 270int avahi_llmnr_query_scheduler_post(AvahiLLMNRQueryScheduler *s, AvahiLLMNRQuery *lq, int immediately) { 271 AvahiLLMNRQueryJob *qj; 272 struct timeval tv; 273 274 assert(s); 275 assert(lq); 276 277 if (!(qj = job_new(s, lq))) 278 return 0; 279 280 qj->time_event = avahi_time_event_new(s->time_event_queue, 281 avahi_elapse_time(&tv, 0, immediately ? 0 : AVAHI_LLMNR_JITTER), 282 elapse_timeout_callback, 283 qj); 284 285 return 1; 286} 287 288int avahi_llmnr_query_scheduler_withdraw_by_id(AvahiLLMNRQueryScheduler *s, unsigned post_id) { 289 AvahiLLMNRQueryJob *qj; 290 291 assert(s); 292 293 for (qj = s->jobs; qj; qj = qj->jobs_by_scheduler_next) { 294 assert(!(qj->lq->dead)); 295 296 if (qj->lq->post_id == post_id) { 297 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 298 return 1; 299 } 300 } 301 302 return 0; 303} 304 305void avahi_llmnr_query_job_remove(AvahiInterface *i, AvahiKey *key) { 306 AvahiLLMNRQueryJob *qj; 307 308 assert(i); 309 assert(key); 310 311 if (!(qj = avahi_hashmap_lookup(i->llmnr.queryjobs_by_key, key))) 312 /* No AvahiLLMNRQueryJob object present for this key*/ 313 return; 314 315 avahi_llmnr_query_job_destroy(qj->scheduler, qj); 316 317 return; 318} 319