]> git.meshlink.io Git - catta/blob - avahi-core/announce.c
* remove lots of DOXYGEN_SHOULD_SKIP_THIS from the header files, use doxygen macro...
[catta] / avahi-core / announce.c
1 /* $Id$ */
2
3 /***
4   This file is part of avahi.
5  
6   avahi is free software; you can redistribute it and/or modify it
7   under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10  
11   avahi is distributed in the hope that it will be useful, but WITHOUT
12   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13   or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
14   Public License for more details.
15  
16   You should have received a copy of the GNU Lesser General Public
17   License along with avahi; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <avahi-common/timeval.h>
27 #include <avahi-common/malloc.h>
28
29 #include "announce.h"
30 #include "log.h"
31 #include "rr-util.h"
32
33 #define AVAHI_ANNOUNCEMENT_JITTER_MSEC 250
34 #define AVAHI_PROBE_JITTER_MSEC 250
35 #define AVAHI_PROBE_INTERVAL_MSEC 250
36
37 static void remove_announcer(AvahiServer *s, AvahiAnnouncer *a) {
38     assert(s);
39     assert(a);
40
41     if (a->time_event)
42         avahi_time_event_free(a->time_event);
43
44     AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_interface, a->interface->announcers, a);
45     AVAHI_LLIST_REMOVE(AvahiAnnouncer, by_entry, a->entry->announcers, a);
46     
47     avahi_free(a);
48 }
49
50 static void elapse_announce(AvahiTimeEvent *e, void *userdata);
51
52 static void set_timeout(AvahiAnnouncer *a, const struct timeval *tv) {
53     assert(a);
54
55     if (!tv) {
56         if (a->time_event) {
57             avahi_time_event_free(a->time_event);
58             a->time_event = NULL;
59         }
60     } else {
61
62         if (a->time_event) 
63             avahi_time_event_update(a->time_event, tv);
64         else
65             a->time_event = avahi_time_event_new(a->server->time_event_queue, tv, elapse_announce, a);
66     }
67 }
68
69 static void next_state(AvahiAnnouncer *a);
70
71 void avahi_s_entry_group_check_probed(AvahiSEntryGroup *g, int immediately) {
72     AvahiEntry *e;
73     assert(g);
74     assert(!g->dead);
75
76     /* Check whether all group members have been probed */
77     
78     if (g->state != AVAHI_ENTRY_GROUP_REGISTERING || g->n_probing > 0) 
79         return;
80
81     avahi_s_entry_group_change_state(g, AVAHI_ENTRY_GROUP_ESTABLISHED);
82
83     if (g->dead)
84         return;
85     
86     for (e = g->entries; e; e = e->entries_next) {
87         AvahiAnnouncer *a;
88         
89         for (a = e->announcers; a; a = a->by_entry_next) {
90
91             if (a->state != AVAHI_WAITING)
92                 continue;
93             
94             a->state = AVAHI_ANNOUNCING;
95
96             if (immediately) {
97                 /* Shortcut */
98                 
99                 a->n_iteration = 1;
100                 next_state(a);
101             } else {
102                 struct timeval tv;
103                 a->n_iteration = 0;
104                 avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
105                 set_timeout(a, &tv);
106             }
107         }
108     }
109 }
110
111 static void next_state(AvahiAnnouncer *a) {
112     assert(a);
113
114 /*     avahi_log_debug("%i -- %u", a->state, a->n_iteration);   */
115     
116     if (a->state == AVAHI_WAITING) {
117
118         assert(a->entry->group);
119
120         avahi_s_entry_group_check_probed(a->entry->group, 1);
121         
122     } else if (a->state == AVAHI_PROBING) {
123
124         if (a->n_iteration >= 4) {
125             /* Probing done */
126             
127 /*             char *t; */
128
129 /*             avahi_log_debug("Enough probes for record [%s]", t = avahi_record_to_string(a->entry->record)); */
130 /*             avahi_free(t); */
131
132             if (a->entry->group) {
133                 assert(a->entry->group->n_probing);
134                 a->entry->group->n_probing--;
135             }
136             
137             if (a->entry->group && a->entry->group->state == AVAHI_ENTRY_GROUP_REGISTERING)
138                 a->state = AVAHI_WAITING;
139             else {
140                 a->state = AVAHI_ANNOUNCING;
141                 a->n_iteration = 1;
142             }
143
144             set_timeout(a, NULL);
145             next_state(a);
146         } else {
147             struct timeval tv;
148
149             avahi_interface_post_probe(a->interface, a->entry->record, 0);
150             
151             avahi_elapse_time(&tv, AVAHI_PROBE_INTERVAL_MSEC, 0);
152             set_timeout(a, &tv);
153             
154             a->n_iteration++;
155         }
156
157     } else if (a->state == AVAHI_ANNOUNCING) {
158
159         if (a->entry->flags & AVAHI_PUBLISH_UNIQUE)
160             /* Send the whole rrset at once */
161             avahi_server_prepare_matching_responses(a->server, a->interface, a->entry->record->key, 0);
162         else
163             avahi_server_prepare_response(a->server, a->interface, a->entry, 0, 0);
164
165         avahi_server_generate_response(a->server, a->interface, NULL, NULL, 0, 0, 0);
166
167         if (++a->n_iteration >= 4) {
168 /*             char *t; */
169             /* Announcing done */
170
171 /*             avahi_log_debug("Enough announcements for record [%s]", t = avahi_record_to_string(a->entry->record)); */
172 /*             avahi_free(t); */
173
174             a->state = AVAHI_ESTABLISHED;
175
176             set_timeout(a, NULL);
177         } else {
178             struct timeval tv;
179             avahi_elapse_time(&tv, a->sec_delay*1000, AVAHI_ANNOUNCEMENT_JITTER_MSEC);
180         
181             if (a->n_iteration < 10)
182                 a->sec_delay *= 2;
183             
184             set_timeout(a, &tv);
185         }
186     }
187 }
188
189 static void elapse_announce(AvahiTimeEvent *e, void *userdata) {
190     assert(e);
191
192     next_state(userdata);
193 }
194
195 static AvahiAnnouncer *get_announcer(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
196     AvahiAnnouncer *a;
197     
198     assert(s);
199     assert(e);
200     assert(i);
201
202     for (a = e->announcers; a; a = a->by_entry_next)
203         if (a->interface == i)
204             return a;
205
206     return NULL;
207 }
208
209 static void go_to_initial_state(AvahiAnnouncer *a, int immediately) {
210     AvahiEntry *e;
211     struct timeval tv;
212         
213     assert(a);
214     e = a->entry;
215
216     if ((e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE))
217         a->state = AVAHI_PROBING;
218     else if (!(e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)) {
219
220         if (!e->group || e->group->state == AVAHI_ENTRY_GROUP_ESTABLISHED)
221             a->state = AVAHI_ANNOUNCING;
222         else
223             a->state = AVAHI_WAITING;
224         
225     } else
226         a->state = AVAHI_ESTABLISHED;
227
228     a->n_iteration = 1;
229     a->sec_delay = 1;
230
231     if (a->state == AVAHI_PROBING && e->group)
232         e->group->n_probing++;
233
234     if (a->state == AVAHI_PROBING) 
235         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
236     else if (a->state == AVAHI_ANNOUNCING) 
237         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
238     else
239         set_timeout(a, NULL);
240 }
241
242 static void new_announcer(AvahiServer *s, AvahiInterface *i, AvahiEntry *e) {
243     AvahiAnnouncer *a;
244 /*     char *t;  */
245
246     assert(s);
247     assert(i);
248     assert(e);
249     assert(!e->dead);
250
251 /*     avahi_log_debug("NEW ANNOUNCER: %s.%i [%s]", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record)); */
252 /*     avahi_free(t); */
253     
254     if (!avahi_interface_match(i, e->interface, e->protocol) || !i->announcing || !avahi_entry_is_commited(e))
255         return;
256
257     /* We don't want duplicate announcers */
258     if (get_announcer(s, e, i))
259         return;    
260
261     if ((!(a = avahi_new(AvahiAnnouncer, 1)))) {
262         avahi_log_error(__FILE__": Out of memory.");
263         return;
264     }
265     
266     a->server = s;
267     a->interface = i;
268     a->entry = e;
269     a->time_event = NULL;
270
271     AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_interface, i->announcers, a);
272     AVAHI_LLIST_PREPEND(AvahiAnnouncer, by_entry, e->announcers, a);
273
274     go_to_initial_state(a, 0);
275     
276 /*     avahi_log_debug("New announcer on interface %s.%i for entry [%s] state=%i", i->hardware->name, i->protocol, t = avahi_record_to_string(e->record), a->state); */
277 /*     avahi_free(t); */
278 }
279
280 void avahi_announce_interface(AvahiServer *s, AvahiInterface *i) {
281     AvahiEntry *e;
282     
283     assert(s);
284     assert(i);
285
286     if (!i->announcing)
287         return;
288
289     for (e = s->entries; e; e = e->entries_next)
290         if (!e->dead)
291             new_announcer(s, i, e);
292 }
293
294 static void announce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
295     AvahiEntry *e = userdata;
296     
297     assert(m);
298     assert(i);
299     assert(e);
300     assert(!e->dead);
301
302     new_announcer(m->server, i, e);
303 }
304
305 void avahi_announce_entry(AvahiServer *s, AvahiEntry *e) {
306     assert(s);
307     assert(e);
308     assert(!e->dead);
309
310     avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, announce_walk_callback, e);
311 }
312
313 void avahi_announce_group(AvahiServer *s, AvahiSEntryGroup *g) {
314     AvahiEntry *e;
315     
316     assert(s);
317     assert(g);
318
319     for (e = g->entries; e; e = e->by_group_next)
320         if (!e->dead)
321             avahi_announce_entry(s, e);
322 }
323
324 int avahi_entry_is_registered(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
325     AvahiAnnouncer *a;
326
327     assert(s);
328     assert(e);
329     assert(i);
330     assert(!e->dead);
331
332     if (!(a = get_announcer(s, e, i)))
333         return 0;
334     
335     return
336         a->state == AVAHI_ANNOUNCING ||
337         a->state == AVAHI_ESTABLISHED ||
338         (a->state == AVAHI_WAITING && !(e->flags & AVAHI_PUBLISH_UNIQUE));
339 }
340
341 int avahi_entry_is_probing(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
342     AvahiAnnouncer *a;
343
344     assert(s);
345     assert(e);
346     assert(i);
347     assert(!e->dead);
348
349     if (!(a = get_announcer(s, e, i)))
350         return 0;
351
352 /*     avahi_log_debug("state: %i", a->state); */
353     
354     return
355         a->state == AVAHI_PROBING ||
356         (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE));
357 }
358
359 void avahi_entry_return_to_initial_state(AvahiServer *s, AvahiEntry *e, AvahiInterface *i) {
360     AvahiAnnouncer *a;
361     
362     assert(s);
363     assert(e);
364     assert(i);
365
366     if (!(a = get_announcer(s, e, i)))
367         return;
368
369     if (a->state == AVAHI_PROBING && a->entry->group)
370         a->entry->group->n_probing--;
371
372     go_to_initial_state(a, 1);
373 }
374
375 static AvahiRecord *make_goodbye_record(AvahiRecord *r) {
376 /*     char *t; */
377     AvahiRecord *g;
378     
379     assert(r);
380
381 /*     avahi_log_debug("Preparing goodbye for record [%s]", t = avahi_record_to_string(r)); */
382 /*     avahi_free(t); */
383
384     if (!(g = avahi_record_copy(r)))
385         return NULL; /* OOM */
386     
387     assert(g->ref == 1);
388     g->ttl = 0;
389
390     return g;
391 }
392
393 static int is_duplicate_entry(AvahiServer *s, AvahiEntry *e) {
394     AvahiEntry *i;
395     
396     assert(s);
397     assert(e);
398
399     for (i = avahi_hashmap_lookup(s->entries_by_key, e->record->key); i; i = i->by_key_next) {
400
401         if (i == e)
402             continue;
403
404         if (!avahi_record_equal_no_ttl(i->record, e->record))
405             continue;
406         
407         return 1;
408     }
409
410     return 0;
411 }
412
413 static void send_goodbye_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
414     AvahiEntry *e = userdata;
415     AvahiRecord *g;
416     
417     assert(m);
418     assert(i);
419     assert(e);
420     assert(!e->dead);
421
422     if (!avahi_interface_match(i, e->interface, e->protocol))
423         return;
424
425     if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
426         return;
427
428     if (!avahi_entry_is_registered(m->server, e, i))
429         return;
430
431     if (is_duplicate_entry(m->server, e))
432         return;
433     
434     if (!(g = make_goodbye_record(e->record)))
435         return; /* OOM */
436     
437     avahi_interface_post_response(i, g, e->flags & AVAHI_PUBLISH_UNIQUE, NULL, 1);
438     avahi_record_unref(g);
439 }
440
441 static void reannounce(AvahiAnnouncer *a) {
442     AvahiEntry *e;
443     struct timeval tv;
444         
445     assert(a);
446     e = a->entry;
447
448     /* If the group this entry belongs to is not even commited, there's nothing to reannounce */
449     if (e->group && (e->group->state == AVAHI_ENTRY_GROUP_UNCOMMITED || e->group->state == AVAHI_ENTRY_GROUP_COLLISION))
450         return;
451
452     /* Because we might change state we decrease the probing counter first */
453     if (a->state == AVAHI_PROBING && a->entry->group)
454         a->entry->group->n_probing--;
455     
456     if (a->state == AVAHI_PROBING ||
457         (a->state == AVAHI_WAITING && (e->flags & AVAHI_PUBLISH_UNIQUE) && !(e->flags & AVAHI_PUBLISH_NO_PROBE)))
458
459         /* We were probing or waiting after probe, so we restart probing from the beginning here */
460         
461         a->state = AVAHI_PROBING;
462     else if (a->state == AVAHI_WAITING)
463
464         /* We were waiting, but were not probing before, so we continue waiting  */
465         a->state = AVAHI_WAITING;
466
467     else if (e->flags & AVAHI_PUBLISH_NO_ANNOUNCE)
468         
469         /* No announcer needed */
470         a->state = AVAHI_ESTABLISHED;
471
472     else {
473         
474         /* Ok, let's restart announcing */
475         a->state = AVAHI_ANNOUNCING;
476     } 
477         
478     /* Now let's increase the probing counter again */
479     if (a->state == AVAHI_PROBING && e->group)
480         e->group->n_probing++;
481     
482     a->n_iteration = 1;
483     a->sec_delay = 1;
484
485     if (a->state == AVAHI_PROBING)
486         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_PROBE_JITTER_MSEC));
487     else if (a->state == AVAHI_ANNOUNCING) 
488         set_timeout(a, avahi_elapse_time(&tv, 0, AVAHI_ANNOUNCEMENT_JITTER_MSEC));
489     else
490         set_timeout(a, NULL);
491 }
492
493
494 static void reannounce_walk_callback(AvahiInterfaceMonitor *m, AvahiInterface *i, void* userdata) {
495     AvahiEntry *e = userdata;
496     AvahiAnnouncer *a;
497     
498     assert(m);
499     assert(i);
500     assert(e);
501     assert(!e->dead);
502
503     if (!(a = get_announcer(m->server, e, i)))
504         return;
505
506     reannounce(a);
507 }
508
509 void avahi_reannounce_entry(AvahiServer *s, AvahiEntry *e) {
510
511     assert(s);
512     assert(e);
513     assert(!e->dead);
514
515     avahi_interface_monitor_walk(s->monitor, e->interface, e->protocol, reannounce_walk_callback, e);
516 }
517     
518 void avahi_goodbye_interface(AvahiServer *s, AvahiInterface *i, int send_goodbye, int remove) {
519     assert(s);
520     assert(i);
521
522     if (send_goodbye)
523         if (avahi_interface_is_relevant(i)) {
524             AvahiEntry *e;
525             
526             for (e = s->entries; e; e = e->entries_next)
527                 if (!e->dead)
528                     send_goodbye_callback(s->monitor, i, e);
529         }
530
531     if (remove)
532         while (i->announcers)
533             remove_announcer(s, i->announcers);
534 }
535
536 void avahi_goodbye_entry(AvahiServer *s, AvahiEntry *e, int send_goodbye, int remove) {
537     assert(s);
538     assert(e);
539     
540     if (send_goodbye)
541         if (!e->dead)
542             avahi_interface_monitor_walk(s->monitor, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, send_goodbye_callback, e);
543
544     if (remove)
545         while (e->announcers)
546             remove_announcer(s, e->announcers);
547 }
548