chiark / gitweb /
985ebb7134b4fa43d32b2b6ed3ebe78869846e27
[elogind.git] / src / nss-mymachines / nss-mymachines.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <nss.h>
23 #include <netdb.h>
24
25 #include "sd-bus.h"
26 #include "sd-login.h"
27 #include "macro.h"
28 #include "util.h"
29 #include "nss-util.h"
30 #include "bus-util.h"
31 #include "in-addr-util.h"
32
33 NSS_GETHOSTBYNAME_PROTOTYPES(mymachines);
34
35 static int count_addresses(sd_bus_message *m, int af, unsigned *ret) {
36         unsigned c = 0;
37         int r;
38
39         assert(m);
40         assert(ret);
41
42         while ((r = sd_bus_message_enter_container(m, 'r', "yay")) > 0) {
43                 int family;
44
45                 r = sd_bus_message_read(m, "i", &family);
46                 if (r < 0)
47                         return r;
48
49                 r = sd_bus_message_skip(m, "ay");
50                 if (r < 0)
51                         return r;
52
53                 r = sd_bus_message_exit_container(m);
54                 if (r < 0)
55                         return r;
56
57                 if (af != AF_UNSPEC && family != af)
58                         continue;
59
60                 c ++;
61         }
62         if (r < 0)
63                 return r;
64
65         r = sd_bus_message_rewind(m, false);
66         if (r < 0)
67                 return r;
68
69         *ret = c;
70         return 0;
71 }
72
73 enum nss_status _nss_mymachines_gethostbyname4_r(
74                 const char *name,
75                 struct gaih_addrtuple **pat,
76                 char *buffer, size_t buflen,
77                 int *errnop, int *h_errnop,
78                 int32_t *ttlp) {
79
80         struct gaih_addrtuple *r_tuple, *r_tuple_first = NULL;
81         _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
82         _cleanup_bus_unref_ sd_bus *bus = NULL;
83         _cleanup_free_ int *ifindices = NULL;
84         _cleanup_free_ char *class = NULL;
85         size_t l, ms, idx;
86         unsigned i = 0, c = 0;
87         char *r_name;
88         int n_ifindices, r;
89
90         assert(name);
91         assert(pat);
92         assert(buffer);
93         assert(errnop);
94         assert(h_errnop);
95
96         r = sd_machine_get_class(name, &class);
97         if (r < 0)
98                 goto fail;
99         if (!streq(class, "container")) {
100                 r = -ENOTTY;
101                 goto fail;
102         }
103
104         n_ifindices = sd_machine_get_ifindices(name, &ifindices);
105         if (n_ifindices < 0) {
106                 r = n_ifindices;
107                 goto fail;
108         }
109
110         r = sd_bus_open_system(&bus);
111         if (r < 0)
112                 goto fail;
113
114         r = sd_bus_call_method(bus,
115                                "org.freedesktop.machine1",
116                                "/org/freedesktop/machine1",
117                                "org.freedesktop.machine1.Manager",
118                                "GetMachineAddresses",
119                                NULL,
120                                &reply,
121                                "s", name);
122         if (r < 0)
123                 goto fail;
124
125         r = sd_bus_message_enter_container(reply, 'a', "(iay)");
126         if (r < 0)
127                 goto fail;
128
129         r = count_addresses(reply, AF_UNSPEC, &c);
130         if (r < 0)
131                 goto fail;
132
133         if (c <= 0) {
134                 *errnop = ESRCH;
135                 *h_errnop = HOST_NOT_FOUND;
136                 return NSS_STATUS_NOTFOUND;
137         }
138
139         l = strlen(name);
140         ms = ALIGN(l+1) + ALIGN(sizeof(struct gaih_addrtuple)) * c;
141         if (buflen < ms) {
142                 *errnop = ENOMEM;
143                 *h_errnop = TRY_AGAIN;
144                 return NSS_STATUS_TRYAGAIN;
145         }
146
147         /* First, append name */
148         r_name = buffer;
149         memcpy(r_name, name, l+1);
150         idx = ALIGN(l+1);
151
152         /* Second, append addresses */
153         r_tuple_first = (struct gaih_addrtuple*) (buffer + idx);
154         while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
155                 int family;
156                 const void *a;
157                 size_t sz;
158
159                 r = sd_bus_message_read(reply, "i", &family);
160                 if (r < 0)
161                         goto fail;
162
163                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
164                 if (r < 0)
165                         goto fail;
166
167                 r = sd_bus_message_exit_container(reply);
168                 if (r < 0)
169                         goto fail;
170
171                 if (!IN_SET(family, AF_INET, AF_INET6)) {
172                         r = -EAFNOSUPPORT;
173                         goto fail;
174                 }
175
176                 if (sz != FAMILY_ADDRESS_SIZE(family)) {
177                         r = -EINVAL;
178                         goto fail;
179                 }
180
181                 r_tuple = (struct gaih_addrtuple*) (buffer + idx);
182                 r_tuple->next = i == c-1 ? NULL : (struct gaih_addrtuple*) ((char*) r_tuple + ALIGN(sizeof(struct gaih_addrtuple)));
183                 r_tuple->name = r_name;
184                 r_tuple->family = family;
185                 r_tuple->scopeid = n_ifindices == 1 ? ifindices[0] : 0;
186                 memcpy(r_tuple->addr, a, sz);
187
188                 idx += ALIGN(sizeof(struct gaih_addrtuple));
189                 i++;
190         }
191
192         assert(i == c);
193
194         r = sd_bus_message_exit_container(reply);
195         if (r < 0)
196                 goto fail;
197
198         assert(idx == ms);
199
200         if (*pat)
201                 **pat = *r_tuple_first;
202         else
203                 *pat = r_tuple_first;
204
205         if (ttlp)
206                 *ttlp = 0;
207
208         return NSS_STATUS_SUCCESS;
209
210 fail:
211         *errnop = -r;
212         *h_errnop = NO_DATA;
213         return NSS_STATUS_UNAVAIL;
214 }
215
216 enum nss_status _nss_mymachines_gethostbyname3_r(
217                 const char *name,
218                 int af,
219                 struct hostent *result,
220                 char *buffer, size_t buflen,
221                 int *errnop, int *h_errnop,
222                 int32_t *ttlp,
223                 char **canonp) {
224
225         _cleanup_bus_message_unref_ sd_bus_message* reply = NULL;
226         _cleanup_bus_unref_ sd_bus *bus = NULL;
227         _cleanup_free_ char *class = NULL;
228         unsigned c = 0, i = 0;
229         char *r_name, *r_aliases, *r_addr, *r_addr_list;
230         size_t l, idx, ms, alen;
231         int r;
232
233         assert(name);
234         assert(result);
235         assert(buffer);
236         assert(errnop);
237         assert(h_errnop);
238
239         if (af == AF_UNSPEC)
240                 af = AF_INET;
241
242         if (af != AF_INET && af != AF_INET6) {
243                 r = -EAFNOSUPPORT;
244                 goto fail;
245         }
246
247         r = sd_machine_get_class(name, &class);
248         if (r < 0)
249                 goto fail;
250         if (!streq(class, "container")) {
251                 r = -ENOTTY;
252                 goto fail;
253         }
254
255         r = sd_bus_open_system(&bus);
256         if (r < 0)
257                 goto fail;
258
259         r = sd_bus_call_method(bus,
260                                "org.freedesktop.machine1",
261                                "/org/freedesktop/machine1",
262                                "org.freedesktop.machine1.Manager",
263                                "GetMachineAddresses",
264                                NULL,
265                                &reply,
266                                "s", name);
267         if (r < 0)
268                 goto fail;
269
270         r = sd_bus_message_enter_container(reply, 'a', "(iay)");
271         if (r < 0)
272                 goto fail;
273
274         r = count_addresses(reply, af, &c);
275         if (r < 0)
276                 goto fail;
277
278         if (c <= 0) {
279                 *errnop = ENOENT;
280                 *h_errnop = HOST_NOT_FOUND;
281                 return NSS_STATUS_NOTFOUND;
282         }
283
284         alen = FAMILY_ADDRESS_SIZE(af);
285         l = strlen(name);
286
287         ms = ALIGN(l+1) +
288                 sizeof(char*) +
289                 (c > 0 ? c : 1) * ALIGN(alen) +
290                 (c > 0 ? c+1 : 2) * sizeof(char*);
291
292         if (buflen < ms) {
293                 *errnop = ENOMEM;
294                 *h_errnop = NO_RECOVERY;
295                 return NSS_STATUS_TRYAGAIN;
296         }
297
298         /* First, append name */
299         r_name = buffer;
300         memcpy(r_name, name, l+1);
301         idx = ALIGN(l+1);
302
303         /* Second, create aliases array */
304         r_aliases = buffer + idx;
305         ((char**) r_aliases)[0] = NULL;
306         idx += sizeof(char*);
307
308         /* Third, append addresses */
309         r_addr = buffer + idx;
310         while ((r = sd_bus_message_enter_container(reply, 'r', "iay")) > 0) {
311                 int family;
312                 const void *a;
313                 size_t sz;
314
315                 r = sd_bus_message_read(reply, "i", &family);
316                 if (r < 0)
317                         goto fail;
318
319                 r = sd_bus_message_read_array(reply, 'y', &a, &sz);
320                 if (r < 0)
321                         goto fail;
322
323                 r = sd_bus_message_exit_container(reply);
324                 if (r < 0)
325                         goto fail;
326
327                 if (family != af)
328                         continue;
329
330                 if (sz != alen) {
331                         r = -EINVAL;
332                         goto fail;
333                 }
334
335                 memcpy(r_addr + i*ALIGN(alen), a, alen);
336                 i++;
337         }
338
339         assert(i == c);
340         idx += c * ALIGN(alen);
341
342         r = sd_bus_message_exit_container(reply);
343         if (r < 0)
344                 goto fail;
345
346         /* Third, append address pointer array */
347         r_addr_list = buffer + idx;
348         for (i = 0; i < c; i++)
349                 ((char**) r_addr_list)[i] = r_addr + i*ALIGN(alen);
350
351         ((char**) r_addr_list)[i] = NULL;
352         idx += (c+1) * sizeof(char*);
353
354         assert(idx == ms);
355
356         result->h_name = r_name;
357         result->h_aliases = (char**) r_aliases;
358         result->h_addrtype = af;
359         result->h_length = alen;
360         result->h_addr_list = (char**) r_addr_list;
361
362         if (ttlp)
363                 *ttlp = 0;
364
365         if (canonp)
366                 *canonp = r_name;
367
368         return NSS_STATUS_SUCCESS;
369
370 fail:
371         *errnop = -r;
372         *h_errnop = NO_DATA;
373         return NSS_STATUS_UNAVAIL;
374 }
375
376 NSS_GETHOSTBYNAME_FALLBACKS(mymachines)