1/***
2  This file is part of avahi.
3
4  avahi is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Lesser General Public License as
6  published by the Free Software Foundation; either version 2.1 of the
7  License, or (at your option) any later version.
8
9  avahi is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General
12  Public License for more details.
13
14  You should have received a copy of the GNU Lesser General Public
15  License along with avahi; if not, write to the Free Software
16  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
17  USA.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <string.h>
25#include <errno.h>
26#include <stdio.h>
27
28#include <avahi-common/llist.h>
29#include <avahi-common/malloc.h>
30#include <avahi-common/error.h>
31#include <avahi-core/log.h>
32#include <avahi-core/publish.h>
33
34#include "main.h"
35#include "cnames.h"
36
37typedef struct CName CName;
38
39struct CName {
40    AvahiSEntryGroup *group;
41    int iteration;
42
43    char *cname;
44
45    int publish_proto;	/*0/mDNS/LLMNR*/
46
47    AVAHI_LLIST_FIELDS(CName, cnames);
48};
49
50static AVAHI_LLIST_HEAD(CName, cnames) = NULL;
51static int current_iteration = 0;
52
53static void add_cname_to_server(CName *c);
54static void remove_cname_from_server(CName *c);
55
56static void entry_group_callback(AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *eg, AvahiEntryGroupState state, void* userdata) {
57    CName *c;
58
59    assert(s);
60    assert(eg);
61
62    c = userdata;
63
64    switch (state) {
65
66        case AVAHI_ENTRY_GROUP_COLLISION:
67            avahi_log_error("Conflict for alias \"%s\", not established.", c->cname);
68            break;
69
70        case AVAHI_ENTRY_GROUP_ESTABLISHED:
71            avahi_log_notice ("Alias \"%s\" successfully established.", c->cname);
72            break;
73
74        case AVAHI_ENTRY_GROUP_FAILURE:
75            avahi_log_notice ("Failed to establish alias \"%s\": %s.", c->cname, avahi_strerror (avahi_server_errno (s)));
76            break;
77
78        case AVAHI_ENTRY_GROUP_UNCOMMITED:
79        case AVAHI_ENTRY_GROUP_REGISTERING:
80            ;
81    }
82}
83
84static CName *cname_new(void) {
85    CName *c;
86
87    c = avahi_new(CName, 1);
88
89    c->group = NULL;
90    c->cname = NULL;
91    c->iteration = current_iteration;
92
93    AVAHI_LLIST_PREPEND(CName, cnames, cnames, c);
94
95    return c;
96}
97
98static void cname_free(CName *c) {
99    assert(c);
100
101    AVAHI_LLIST_REMOVE(CName, cnames, cnames, c);
102
103    if (c->group)
104        avahi_s_entry_group_free(c->group);
105
106    avahi_free(c->cname);
107
108    avahi_free(c);
109}
110
111static CName *cname_find(const char *name) {
112    CName *c;
113
114    assert(name);
115
116    for (c = cnames; c; c = c->cnames_next)
117        if (!strcmp(c->cname, name))
118            return c;
119
120    return NULL;
121}
122
123static void add_cname_to_server(CName *c)
124{
125            avahi_log_error("add_cname_to_server start.");
126    if (!c->group)
127        if (!(c->group = avahi_s_entry_group_new (avahi_server, entry_group_callback, c))) {
128            avahi_log_error("avahi_s_entry_group_new() failed: %s", avahi_strerror(avahi_server_errno(avahi_server)));
129            return;
130        }
131
132    if (avahi_s_entry_group_is_empty(c->group)) {
133        int err;
134
135	c->publish_proto = AVAHI_PUBLISH_USE_MULTICAST;
136
137        if ((err = avahi_server_add_cname(avahi_server, c->group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, c->publish_proto, AVAHI_DEFAULT_TTL_HOST_NAME, c->cname)) < 0) {
138            avahi_log_error ("Alias %s: avahi_server_add_cname failure: %s", c->cname, avahi_strerror(err));
139            return;
140        }
141
142        avahi_s_entry_group_commit (c->group);
143    }
144}
145
146static void remove_cname_from_server(CName *c)
147{
148    if (c->group)
149        avahi_s_entry_group_reset (c->group);
150}
151
152void cnames_add_to_server(void) {
153    CName *c;
154
155    for (c = cnames; c; c = c->cnames_next){
156            avahi_log_error("cnames_add_to_server start.");
157        add_cname_to_server(c);
158    }
159}
160
161void cnames_remove_from_server(void) {
162    CName *c;
163
164    for (c = cnames; c; c = c->cnames_next)
165        remove_cname_from_server(c);
166}
167
168void cnames_register(char **aliases) {
169    CName *c, *next;
170    char **name;
171
172    current_iteration++;
173
174    for (name = aliases; name && *name; name++) {
175        if (!(c = cname_find(*name))) {
176            c = cname_new();
177            c->cname = *name;
178
179            avahi_log_info("Loading new alias %s.", c->cname);
180        }
181
182        c->iteration = current_iteration;
183    }
184
185    for (c = cnames; c; c = next) {
186        next = c->cnames_next;
187
188        if (c->iteration != current_iteration) {
189            avahi_log_info("Alias %s vanished, removing.", c->cname);
190            cname_free(c);
191        }
192    }
193}
194
195void cnames_free_all (void)
196{
197    while(cnames)
198        cname_free(cnames);
199}
200
201typedef struct LLMNR_CName LLMNR_CName;
202
203struct LLMNR_CName {
204    AvahiSEntryGroup *llmnr_group;
205    int iteration;
206
207    char *llmnr_cname;
208
209    int publish_proto;	/*0/mDNS/LLMNR*/
210
211    AVAHI_LLIST_FIELDS(LLMNR_CName, llmnr_cnames);
212};
213
214static AVAHI_LLIST_HEAD(LLMNR_CName, llmnr_cnames) = NULL;
215static int llmnr_current_iteration = 0;
216
217static void add_llmnr_cname_to_server(LLMNR_CName *lc);
218static void remove_llmnr_cname_from_server(LLMNR_CName *lc);
219
220static void llmnr_entry_group_callback(AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *eg, AvahiEntryGroupState state, void* userdata) {
221    LLMNR_CName *lc;
222
223    assert(s);
224    assert(eg);
225
226    lc = userdata;
227
228    switch (state) {
229
230        case AVAHI_ENTRY_GROUP_COLLISION:
231            avahi_log_error("Conflict for llmnr alias \"%s\", not established.", lc->llmnr_cname);
232            break;
233
234        case AVAHI_ENTRY_GROUP_ESTABLISHED:
235            avahi_log_notice ("LLMNR Alias \"%s\" successfully established.", lc->llmnr_cname);
236            break;
237
238        case AVAHI_ENTRY_GROUP_FAILURE:
239            avahi_log_notice ("Failed to establish llmnr alias \"%s\": %s.", lc->llmnr_cname, avahi_strerror (avahi_server_errno (s)));
240            break;
241
242        case AVAHI_ENTRY_GROUP_UNCOMMITED:
243        case AVAHI_ENTRY_GROUP_REGISTERING:
244            ;
245    }
246}
247
248static LLMNR_CName *llmnr_cname_new(void) {
249    LLMNR_CName *lc;
250
251    lc = avahi_new(LLMNR_CName, 1);
252
253    lc->llmnr_group = NULL;
254    lc->llmnr_cname = NULL;
255    lc->iteration = llmnr_current_iteration;
256
257    AVAHI_LLIST_PREPEND(LLMNR_CName, llmnr_cnames, llmnr_cnames, lc);
258
259    return lc;
260}
261
262static void llmnr_cname_free(LLMNR_CName *lc) {
263    assert(lc);
264
265    AVAHI_LLIST_REMOVE(LLMNR_CName, llmnr_cnames, llmnr_cnames, lc);
266
267    if (lc->llmnr_group)
268        avahi_s_entry_group_free(lc->llmnr_group);
269
270    avahi_free(lc->llmnr_cname);
271
272    avahi_free(lc);
273}
274
275static LLMNR_CName *llmnr_cname_find(const char *llmnr_name) {
276    LLMNR_CName *lc;
277
278    assert(llmnr_name);
279
280    for (lc = llmnr_cnames; lc; lc = lc->llmnr_cnames_next)
281        if (!strcmp(lc->llmnr_cname, llmnr_name))
282            return lc;
283
284    return NULL;
285}
286
287static void add_llmnr_cname_to_server(LLMNR_CName *lc)
288{
289            avahi_log_error("add_llmnr_cname_to_server start.");
290    if (!lc->llmnr_group)
291        if (!(lc->llmnr_group = avahi_s_entry_group_new (avahi_server, llmnr_entry_group_callback, lc))) {
292            avahi_log_error("avahi_s_llmnr_entry_group_new() failed: %s", avahi_strerror(avahi_server_errno(avahi_server)));
293            return;
294        }
295
296    if (avahi_s_entry_group_is_empty(lc->llmnr_group)) {
297        int err;
298
299	lc->publish_proto = AVAHI_PUBLISH_USE_LLMNR;
300
301        if ((err = avahi_server_add_llmnr_cname(avahi_server, lc->llmnr_group, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, lc->publish_proto, AVAHI_DEFAULT_TTL_HOST_NAME, lc->llmnr_cname)) < 0) {
302            avahi_log_error ("Alias %s: avahi_server_add_llmnr_cname failure: %s", lc->llmnr_cname, avahi_strerror(err));
303            return;
304        }
305
306        avahi_s_entry_group_commit (lc->llmnr_group);
307    }
308}
309
310static void remove_llmnr_cname_from_server(LLMNR_CName *lc)
311{
312    if (lc->llmnr_group)
313        avahi_s_entry_group_reset (lc->llmnr_group);
314}
315
316void llmnr_cnames_add_to_server(void) {
317    LLMNR_CName *lc;
318
319    for (lc = llmnr_cnames; lc; lc = lc->llmnr_cnames_next){
320            avahi_log_error("llmnr_cnames_add_to_server start.");
321        add_llmnr_cname_to_server(lc);
322    }
323}
324
325void llmnr_cnames_remove_from_server(void) {
326    LLMNR_CName *lc;
327
328    for (lc = llmnr_cnames; lc; lc = lc->llmnr_cnames_next)
329        remove_llmnr_cname_from_server(lc);
330}
331
332void llmnr_cnames_register(char **aliases_llmnr) {
333    LLMNR_CName *lc, *llmnr_next;
334    char **llmnr_name;
335
336    llmnr_current_iteration++;
337
338    for (llmnr_name = aliases_llmnr; llmnr_name && *llmnr_name; llmnr_name++) {
339        if (!(lc = llmnr_cname_find(*llmnr_name))) {
340            lc = llmnr_cname_new();
341            lc->llmnr_cname = *llmnr_name;
342
343            avahi_log_info("Loading new alias_llmnr %s.", lc->llmnr_cname);
344        }
345
346        lc->iteration = llmnr_current_iteration;
347    }
348
349    for (lc = llmnr_cnames; lc; lc = llmnr_next) {
350        llmnr_next = lc->llmnr_cnames_next;
351
352        if (lc->iteration != llmnr_current_iteration) {
353            avahi_log_info("Alias_llmnr %s vanished, removing.", lc->llmnr_cname);
354            llmnr_cname_free(lc);
355        }
356    }
357}
358
359void llmnr_cnames_free_all (void)
360{
361    while(llmnr_cnames)
362        llmnr_cname_free(llmnr_cnames);
363}
364
365