chiark / gitweb /
nss-myhostname: use _cleanup_ and split function into two
[elogind.git] / src / nss-myhostname / netlink.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2008-2011 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 <sys/socket.h>
23 #include <sys/un.h>
24 #include <asm/types.h>
25 #include <inttypes.h>
26 #include <linux/netlink.h>
27 #include <linux/rtnetlink.h>
28 #include <string.h>
29 #include <assert.h>
30 #include <errno.h>
31 #include <limits.h>
32 #include <arpa/inet.h>
33 #include <unistd.h>
34 #include <inttypes.h>
35 #include <stdlib.h>
36
37 #include "ifconf.h"
38 #include "macro.h"
39 #include "util.h"
40
41 #define SEQ 4711
42
43 static int read_reply(int fd, struct address **list, unsigned *n_list) {
44         ssize_t bytes;
45         struct cmsghdr *cmsg;
46         struct ucred *ucred;
47         struct nlmsghdr *p;
48         uint8_t cred_buffer[CMSG_SPACE(sizeof(struct ucred))];
49         struct {
50                 struct nlmsghdr hdr;
51                 struct ifaddrmsg ifaddrmsg;
52                 uint8_t payload[16*1024];
53         } resp;
54         struct iovec iov = {
55                 .iov_base = &resp,
56                 .iov_len = sizeof(resp),
57         };
58         struct msghdr msg = {
59                 .msg_name = NULL,
60                 .msg_namelen = 0,
61                 .msg_iov = &iov,
62                 .msg_iovlen = 1,
63                 .msg_control = cred_buffer,
64                 .msg_controllen = sizeof(cred_buffer),
65                 .msg_flags = 0,
66         };
67
68         assert(fd >= 0);
69         assert(list);
70
71         bytes = recvmsg(fd, &msg, 0);
72         if (bytes < 0)
73                 return -errno;
74
75         cmsg = CMSG_FIRSTHDR(&msg);
76         if (!cmsg || cmsg->cmsg_type != SCM_CREDENTIALS)
77                 return -EIO;
78
79         ucred = (struct ucred*) CMSG_DATA(cmsg);
80         if (ucred->uid != 0 || ucred->pid != 0)
81                 return 0;
82
83         for (p = &resp.hdr; bytes > 0; p = NLMSG_NEXT(p, bytes)) {
84                 struct ifaddrmsg *ifaddrmsg;
85                 struct rtattr *a;
86                 size_t l;
87                 void *local = NULL, *address = NULL;
88
89                 if (!NLMSG_OK(p, (size_t) bytes))
90                         return -EIO;
91
92                 if (p->nlmsg_seq != SEQ)
93                         continue;
94
95                 if (p->nlmsg_type == NLMSG_DONE)
96                         return 1;
97
98                 if (p->nlmsg_type == NLMSG_ERROR) {
99                         struct nlmsgerr *nlmsgerr;
100
101                         nlmsgerr = NLMSG_DATA(p);
102                         return -nlmsgerr->error;
103                 }
104
105                 if (p->nlmsg_type != RTM_NEWADDR)
106                         continue;
107
108                 ifaddrmsg = NLMSG_DATA(p);
109
110                 if (ifaddrmsg->ifa_family != AF_INET &&
111                     ifaddrmsg->ifa_family != AF_INET6)
112                         continue;
113
114                 if (ifaddrmsg->ifa_scope == RT_SCOPE_HOST ||
115                     ifaddrmsg->ifa_scope == RT_SCOPE_NOWHERE)
116                         continue;
117
118                 if (ifaddrmsg->ifa_flags & IFA_F_DEPRECATED)
119                         continue;
120
121                 l = NLMSG_PAYLOAD(p, sizeof(struct ifaddrmsg));
122                 a = IFA_RTA(ifaddrmsg);
123
124                 while (RTA_OK(a, l)) {
125
126                         if (a->rta_type == IFA_ADDRESS)
127                                 address = RTA_DATA(a);
128                         else if (a->rta_type == IFA_LOCAL)
129                                 local = RTA_DATA(a);
130
131                         a = RTA_NEXT(a, l);
132                 }
133
134                 if (local)
135                         address = local;
136
137                 if (!address)
138                         continue;
139
140                 *list = realloc(*list, (*n_list+1) * sizeof(struct address));
141                 if (!*list)
142                         return -ENOMEM;
143
144                 (*list)[*n_list].family = ifaddrmsg->ifa_family;
145                 (*list)[*n_list].scope = ifaddrmsg->ifa_scope;
146                 memcpy((*list)[*n_list].address,
147                        address, ifaddrmsg->ifa_family == AF_INET ? 4 : 16);
148                 (*list)[*n_list].ifindex = ifaddrmsg->ifa_index;
149
150                 (*n_list)++;
151         }
152
153         return 0;
154 }
155
156
157 int ifconf_acquire_addresses(struct address **_list, unsigned *_n_list) {
158
159         struct {
160                 struct nlmsghdr hdr;
161                 struct rtgenmsg gen;
162         } req = { {
163                         .nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)),
164                         .nlmsg_type = RTM_GETADDR,
165                         .nlmsg_flags = NLM_F_REQUEST|NLM_F_DUMP|NLM_F_ACK,
166                         .nlmsg_seq = SEQ,
167                         .nlmsg_pid = 0,
168                 }, {
169                         .rtgen_family = AF_UNSPEC,
170                 }
171         };
172         int r, on = 1;
173         struct address *list = NULL;
174         unsigned n_list = 0;
175         int _cleanup_close_ fd;
176
177         fd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
178         if (fd < 0)
179                 return -errno;
180
181         if (setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0)
182                 return -errno;
183
184         if (send(fd, &req, req.hdr.nlmsg_len, 0) < 0)
185                 return -errno;
186
187         while((r = read_reply(fd, &list, &n_list)) == 0)
188                 ;
189
190         if (r < 0) {
191                 free(list);
192                 return r;
193         }
194
195         assert(n_list == 0 || list);
196         qsort(list, n_list, sizeof(struct address), address_compare);
197
198         *_list = list;
199         *_n_list = n_list;
200
201         return 0;
202 }