1 /*
2   Copyright 2020 Northern.tech AS
3 
4   This file is part of CFEngine 3 - written and maintained by Northern.tech AS.
5 
6   This program is free software; you can redistribute it and/or modify it
7   under the terms of the GNU General Public License as published by the
8   Free Software Foundation; version 3.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
18 
19   To the extent this program is licensed as part of the Enterprise
20   versions of CFEngine, the applicable Commercial Open Source License
21   (COSL) may apply to this file if you as a licensee so wish it. See
22   included file COSL.txt.
23 */
24 
25 #include <findhub.h>
26 #include <cleanup.h>
27 #include <string_lib.h>
28 #include <misc_lib.h>
29 #include <logging.h>
30 #include <alloc.h>
31 
32 List *hublist = NULL;
33 
34 static void CleanupDlClose(void);
35 static int CompareHosts(const void  *a, const void *b);
36 
client_callback(AvahiClient * c,AvahiClientState state,AVAHI_GCC_UNUSED void * userdata)37 void client_callback(AvahiClient *c,
38                      AvahiClientState state,
39                      AVAHI_GCC_UNUSED void *userdata)
40 {
41     assert(c);
42 
43     if (state == AVAHI_CLIENT_FAILURE)
44     {
45         Log(LOG_LEVEL_ERR, "Server connection failure '%s'", avahi_strerror_ptr(avahi_client_errno_ptr(c)));
46         avahi_simple_poll_quit_ptr(spoll);
47     }
48 }
49 
browse_callback(AvahiServiceBrowser * b,AvahiIfIndex interface,AvahiProtocol protocol,AvahiBrowserEvent event,const char * name,const char * type,const char * domain,AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,void * userdata)50 void browse_callback(AvahiServiceBrowser *b,
51                      AvahiIfIndex interface,
52                      AvahiProtocol protocol,
53                      AvahiBrowserEvent event,
54                      const char *name,
55                      const char *type,
56                      const char *domain,
57                      AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
58                      void *userdata)
59 {
60     AvahiClient *c = userdata;
61     assert(b);
62 
63     switch(event)
64     {
65     case AVAHI_BROWSER_FAILURE:
66         Log(LOG_LEVEL_ERR, "Avahi browser error '%s'", avahi_strerror_ptr(avahi_client_errno_ptr(avahi_service_browser_get_client_ptr(b))));
67         avahi_simple_poll_quit_ptr(spoll);
68         return;
69 
70     case AVAHI_BROWSER_NEW:
71         if ( !(avahi_service_resolver_new_ptr(c, interface, protocol, name,
72                                              type, domain, AVAHI_PROTO_UNSPEC, 0,
73                                              (AvahiServiceResolverCallback) resolve_callback, c)) )
74         {
75             Log(LOG_LEVEL_ERR, "Failed to resolve service '%s', error '%s'", name, avahi_strerror_ptr(avahi_client_errno_ptr(c)));
76         }
77         break;
78 
79     case AVAHI_BROWSER_REMOVE:
80         break;
81 
82     case AVAHI_BROWSER_ALL_FOR_NOW:
83         avahi_simple_poll_quit_ptr(spoll);
84         break;
85 
86     case AVAHI_BROWSER_CACHE_EXHAUSTED:
87         break;
88     }
89 }
90 
resolve_callback(AvahiServiceResolver * r,AVAHI_GCC_UNUSED AvahiIfIndex interface,AVAHI_GCC_UNUSED AvahiProtocol protocol,AvahiResolverEvent event,const char * name,const char * type,const char * domain,const char * host_name,const AvahiAddress * address,uint16_t port,AVAHI_GCC_UNUSED AvahiStringList * txt,AVAHI_GCC_UNUSED AvahiLookupFlags flags,AVAHI_GCC_UNUSED void * userdata)91 void resolve_callback(AvahiServiceResolver *r,
92                       AVAHI_GCC_UNUSED AvahiIfIndex interface,
93                       AVAHI_GCC_UNUSED AvahiProtocol protocol,
94                       AvahiResolverEvent event,
95                       const char *name,
96                       const char *type,
97                       const char *domain,
98                       const char *host_name,
99                       const AvahiAddress *address,
100                       uint16_t port,
101                       AVAHI_GCC_UNUSED AvahiStringList *txt,
102                       AVAHI_GCC_UNUSED AvahiLookupFlags flags,
103                       AVAHI_GCC_UNUSED void* userdata)
104 {
105     HostProperties *hostprop = xcalloc(1, sizeof(HostProperties));
106     assert(r);
107     char a[AVAHI_ADDRESS_STR_MAX];
108     switch(event)
109     {
110     case AVAHI_RESOLVER_FAILURE:
111         Log(LOG_LEVEL_ERR, "In Avahi resolver, failed to resolve service '%s' of type '%s' in domain '%s' (error: '%s')",
112               name, type, domain, avahi_strerror_ptr(avahi_client_errno_ptr(avahi_service_resolver_get_client_ptr(r))));
113         break;
114 
115     case AVAHI_RESOLVER_FOUND:
116         avahi_address_snprint_ptr(a, sizeof(a), address);
117 
118         strlcpy(hostprop->Hostname, host_name, 4096);
119         strlcpy(hostprop->IPAddress, a, AVAHI_ADDRESS_STR_MAX);
120         hostprop->Port = port;
121         ListAppend(hublist, hostprop);
122         break;
123     }
124 
125     avahi_service_resolver_free_ptr(r);
126 }
127 
PrintList(List * list)128 void PrintList(List *list)
129 {
130     ListIterator *i = NULL;
131 
132     i = ListIteratorGet(list);
133     if (!i)
134     {
135         ProgrammingError("Unable to get iterator for hub list");
136         return;
137     }
138 
139     do
140     {
141         HostProperties *hostprop = (HostProperties *)ListIteratorData(i);
142 
143         Log(LOG_LEVEL_NOTICE, "CFEngine Policy Server: hostname '%s', IP address '%s', port %d",
144             hostprop->Hostname, hostprop->IPAddress, hostprop->Port);
145     } while (ListIteratorNext(i) != -1);
146 
147     ListIteratorDestroy(&i);
148 }
149 
ListHubs(List ** list)150 int ListHubs(List **list)
151 {
152     AvahiClient *client = NULL;
153     AvahiServiceBrowser *sb = NULL;
154     int error;
155 
156     hublist = ListNew(&CompareHosts, NULL, &free);
157     if (!hublist)
158     {
159         return -1;
160     }
161 
162     spoll = NULL;
163     avahi_handle = NULL;
164 
165     RegisterCleanupFunction(&CleanupDlClose);
166 
167     if (loadavahi() == -1)
168     {
169         Log(LOG_LEVEL_ERR, "Avahi was not found");
170         return -1;
171     }
172 
173     if (!(spoll = avahi_simple_poll_new_ptr()))
174     {
175         Log(LOG_LEVEL_ERR, "Failed to create simple poll object.");
176 
177         if (spoll)
178         {
179             avahi_simple_poll_free_ptr(spoll);
180         }
181         return -1;
182     }
183 
184     client = avahi_client_new_ptr(avahi_simple_poll_get_ptr(spoll), 0, client_callback, NULL, &error);
185 
186     if (!client)
187     {
188         Log(LOG_LEVEL_ERR, "Failed to create client '%s'", avahi_strerror_ptr(error));
189 
190         if (client)
191         {
192             avahi_client_free_ptr(client);
193         }
194 
195         if (spoll)
196         {
197             avahi_simple_poll_free_ptr(spoll);
198         }
199 
200         return -1;
201     }
202 
203     if (!(sb = avahi_service_browser_new_ptr(client, AVAHI_IF_UNSPEC, AVAHI_PROTO_UNSPEC, "_cfenginehub._tcp", NULL, 0, browse_callback, client)))
204     {
205         Log(LOG_LEVEL_ERR, "Failed to create service browser '%s'", avahi_strerror_ptr(avahi_client_errno_ptr(client)));
206 
207         if (spoll)
208         {
209             avahi_simple_poll_free_ptr(spoll);
210         }
211 
212         if (client)
213         {
214             avahi_client_free_ptr(client);
215         }
216 
217         if (sb)
218         {
219             avahi_service_browser_free_ptr(sb);
220         }
221 
222         return -1;
223     }
224 
225     avahi_simple_poll_loop_ptr(spoll);
226 
227     if (sb)
228         avahi_service_browser_free_ptr(sb);
229     if (client)
230         avahi_client_free_ptr(client);
231     if (spoll)
232         avahi_simple_poll_free_ptr(spoll);
233 
234     *list = hublist;
235 
236     return ListCount(*list);
237 }
238 
CleanupDlClose(void)239 static void CleanupDlClose(void)
240 {
241     if (avahi_handle != NULL)
242     {
243         dlclose(avahi_handle);
244     }
245 }
246 
CompareHosts(const void * a,const void * b)247 static int CompareHosts(const void *a, const void *b)
248 {
249     return StringSafeCompare(((HostProperties*)a)->Hostname, ((HostProperties*)b)->Hostname);
250 }
251