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/avahi-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 "static-hosts.h"
36 
37 typedef struct StaticHost StaticHost;
38 
39 struct StaticHost {
40     AvahiSEntryGroup *group;
41     int iteration;
42 
43     char *host;
44     AvahiAddress address;
45 
46     AVAHI_LLIST_FIELDS(StaticHost, hosts);
47 };
48 
49 static AVAHI_LLIST_HEAD(StaticHost, hosts) = NULL;
50 static int current_iteration = 0;
51 
52 static void add_static_host_to_server(StaticHost *h);
53 static void remove_static_host_from_server(StaticHost *h);
54 
entry_group_callback(AvahiServer * s,AVAHI_GCC_UNUSED AvahiSEntryGroup * eg,AvahiEntryGroupState state,void * userdata)55 static void entry_group_callback(AvahiServer *s, AVAHI_GCC_UNUSED AvahiSEntryGroup *eg, AvahiEntryGroupState state, void* userdata) {
56     StaticHost *h;
57 
58     assert(s);
59     assert(eg);
60 
61     h = userdata;
62 
63     switch (state) {
64 
65         case AVAHI_ENTRY_GROUP_COLLISION:
66             avahi_log_error("Host name conflict for \"%s\", not established.", h->host);
67             break;
68 
69         case AVAHI_ENTRY_GROUP_ESTABLISHED:
70             avahi_log_notice ("Static host name \"%s\" successfully established.", h->host);
71             break;
72 
73         case AVAHI_ENTRY_GROUP_FAILURE:
74             avahi_log_notice ("Failed to establish static host name \"%s\": %s.", h->host, avahi_strerror (avahi_server_errno (s)));
75             break;
76 
77         case AVAHI_ENTRY_GROUP_UNCOMMITED:
78         case AVAHI_ENTRY_GROUP_REGISTERING:
79             ;
80     }
81 }
82 
static_host_new(void)83 static StaticHost *static_host_new(void) {
84     StaticHost *s;
85 
86     s = avahi_new(StaticHost, 1);
87 
88     s->group = NULL;
89     s->host = NULL;
90     s->iteration = current_iteration;
91 
92     AVAHI_LLIST_PREPEND(StaticHost, hosts, hosts, s);
93 
94     return s;
95 }
96 
static_host_free(StaticHost * s)97 static void static_host_free(StaticHost *s) {
98     assert(s);
99 
100     AVAHI_LLIST_REMOVE(StaticHost, hosts, hosts, s);
101 
102     if (s->group)
103         avahi_s_entry_group_free (s->group);
104 
105     avahi_free(s->host);
106 
107     avahi_free(s);
108 }
109 
static_host_find(const char * host,const AvahiAddress * a)110 static StaticHost *static_host_find(const char *host, const AvahiAddress *a) {
111     StaticHost *h;
112 
113     assert(host);
114     assert(a);
115 
116     for (h = hosts; h; h = h->hosts_next)
117         if (!strcmp(h->host, host) && !avahi_address_cmp(a, &h->address))
118             return h;
119 
120     return NULL;
121 }
122 
add_static_host_to_server(StaticHost * h)123 static void add_static_host_to_server(StaticHost *h)
124 {
125 
126     if (!h->group)
127         if (!(h->group = avahi_s_entry_group_new (avahi_server, entry_group_callback, h))) {
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(h->group)) {
133         AvahiProtocol p;
134         int err;
135         const AvahiServerConfig *config;
136         config = avahi_server_get_config(avahi_server);
137 
138         p = (h->address.proto == AVAHI_PROTO_INET && config->publish_a_on_ipv6) ||
139             (h->address.proto == AVAHI_PROTO_INET6 && config->publish_aaaa_on_ipv4) ? AVAHI_PROTO_UNSPEC : h->address.proto;
140 
141         if ((err = avahi_server_add_address(avahi_server, h->group, AVAHI_IF_UNSPEC, p, 0, h->host, &h->address)) < 0) {
142             avahi_log_error ("Static host name %s: avahi_server_add_address failure: %s", h->host, avahi_strerror(err));
143             return;
144         }
145 
146         avahi_s_entry_group_commit (h->group);
147     }
148 }
149 
remove_static_host_from_server(StaticHost * h)150 static void remove_static_host_from_server(StaticHost *h)
151 {
152     if (h->group)
153         avahi_s_entry_group_reset (h->group);
154 }
155 
static_hosts_add_to_server(void)156 void static_hosts_add_to_server(void) {
157     StaticHost *h;
158 
159     for (h = hosts; h; h = h->hosts_next)
160         add_static_host_to_server(h);
161 }
162 
static_hosts_remove_from_server(void)163 void static_hosts_remove_from_server(void) {
164     StaticHost *h;
165 
166     for (h = hosts; h; h = h->hosts_next)
167         remove_static_host_from_server(h);
168 }
169 
static_hosts_load(int in_chroot)170 void static_hosts_load(int in_chroot) {
171     FILE *f;
172     unsigned int line = 0;
173     StaticHost *h, *next;
174     const char *filename = AVAHI_HOSTS_FILE;
175 
176     if (!(f = fopen(filename, "r"))) {
177         if (errno != ENOENT)
178             avahi_log_error ("Failed to open static hosts file: %s", strerror (errno));
179         return;
180     }
181 
182     current_iteration++;
183 
184     while (!feof(f)) {
185         unsigned int len;
186         char ln[256], *s;
187         char *host, *ip;
188         AvahiAddress a;
189 
190         if (!fgets(ln, sizeof (ln), f))
191             break;
192 
193         line++;
194 
195         /* Find the start of the line, ignore whitespace */
196         s = ln + strspn(ln, " \t");
197         /* Set the end of the string to NULL */
198         s[strcspn(s, "#\r\n")] = 0;
199 
200         /* Ignore blank lines */
201         if (*s == 0)
202             continue;
203 
204         /* Read the first string (ip) up to the next whitespace */
205         len = strcspn(s, " \t");
206         ip = avahi_strndup(s, len);
207 
208         /* Skip past it */
209         s += len;
210 
211         /* Find the next token */
212         s += strspn(s, " \t");
213         len = strcspn(s, " \t");
214         host = avahi_strndup(s, len);
215 
216         if (*host == 0)
217         {
218             avahi_log_error("%s:%d: Error, unexpected end of line!", filename, line);
219             avahi_free(host);
220             avahi_free(ip);
221             goto fail;
222         }
223 
224         /* Skip over the host */
225         s += len;
226 
227         /* Skip past any more spaces */
228         s += strspn(s, " \t");
229 
230         /* Anything left? */
231         if (*s != 0) {
232             avahi_log_error ("%s:%d: Junk on the end of the line!", filename, line);
233             avahi_free(host);
234             avahi_free(ip);
235             goto fail;
236         }
237 
238         if (!avahi_address_parse(ip, AVAHI_PROTO_UNSPEC, &a)) {
239             avahi_log_error("Static host name %s: failed to parse address %s", host, ip);
240             avahi_free(host);
241             avahi_free(ip);
242             goto fail;
243         }
244 
245         avahi_free(ip);
246 
247         if ((h = static_host_find(host, &a)))
248             avahi_free(host);
249         else {
250             h = static_host_new();
251             h->host = host;
252             h->address = a;
253 
254             avahi_log_info("Loading new static hostname %s.", h->host);
255         }
256 
257         h->iteration = current_iteration;
258     }
259 
260     for (h = hosts; h; h = next) {
261         next = h->hosts_next;
262 
263         if (h->iteration != current_iteration) {
264             avahi_log_info("Static hostname %s vanished, removing.", h->host);
265             static_host_free(h);
266         }
267     }
268 
269 fail:
270 
271     fclose(f);
272 }
273 
static_hosts_free_all(void)274 void static_hosts_free_all (void)
275 {
276     while(hosts)
277         static_host_free(hosts);
278 }
279