xref: /netbsd/external/bsd/libfido2/dist/src/netlink.c (revision 98a5e356)
1*98a5e356Schristos /*
2*98a5e356Schristos  * Copyright (c) 2020 Yubico AB. All rights reserved.
3*98a5e356Schristos  * Use of this source code is governed by a BSD-style
4*98a5e356Schristos  * license that can be found in the LICENSE file.
5*98a5e356Schristos  */
6*98a5e356Schristos 
7*98a5e356Schristos #include <sys/socket.h>
8*98a5e356Schristos 
9*98a5e356Schristos #include <linux/genetlink.h>
10*98a5e356Schristos #include <linux/netlink.h>
11*98a5e356Schristos #include <linux/nfc.h>
12*98a5e356Schristos 
13*98a5e356Schristos #include <errno.h>
14*98a5e356Schristos #include <limits.h>
15*98a5e356Schristos 
16*98a5e356Schristos #include "fido.h"
17*98a5e356Schristos #include "netlink.h"
18*98a5e356Schristos 
19*98a5e356Schristos #ifdef FIDO_FUZZ
20*98a5e356Schristos static ssize_t (*fuzz_read)(int, void *, size_t);
21*98a5e356Schristos static ssize_t (*fuzz_write)(int, const void *, size_t);
22*98a5e356Schristos # define READ	fuzz_read
23*98a5e356Schristos # define WRITE	fuzz_write
24*98a5e356Schristos #else
25*98a5e356Schristos # define READ	read
26*98a5e356Schristos # define WRITE	write
27*98a5e356Schristos #endif
28*98a5e356Schristos 
29*98a5e356Schristos #ifndef SOL_NETLINK
30*98a5e356Schristos #define SOL_NETLINK	270
31*98a5e356Schristos #endif
32*98a5e356Schristos 
33*98a5e356Schristos /* XXX avoid signed NLA_ALIGNTO */
34*98a5e356Schristos #undef NLA_HDRLEN
35*98a5e356Schristos #define NLA_HDRLEN	NLMSG_ALIGN(sizeof(struct nlattr))
36*98a5e356Schristos 
37*98a5e356Schristos typedef struct nlmsgbuf {
38*98a5e356Schristos 	size_t         siz; /* alloc size */
39*98a5e356Schristos 	size_t         len; /* of payload */
40*98a5e356Schristos 	unsigned char *ptr; /* in payload */
41*98a5e356Schristos 	union {
42*98a5e356Schristos 		struct nlmsghdr   nlmsg;
43*98a5e356Schristos 		char              buf[NLMSG_HDRLEN]; /* align */
44*98a5e356Schristos 	}              u;
45*98a5e356Schristos 	unsigned char  payload[];
46*98a5e356Schristos } nlmsgbuf_t;
47*98a5e356Schristos 
48*98a5e356Schristos typedef struct genlmsgbuf {
49*98a5e356Schristos 	union {
50*98a5e356Schristos 		struct genlmsghdr genl;
51*98a5e356Schristos 		char              buf[GENL_HDRLEN];  /* align */
52*98a5e356Schristos 	}              u;
53*98a5e356Schristos } genlmsgbuf_t;
54*98a5e356Schristos 
55*98a5e356Schristos typedef struct nlamsgbuf {
56*98a5e356Schristos 	size_t         siz; /* alloc size */
57*98a5e356Schristos 	size_t         len; /* of payload */
58*98a5e356Schristos 	unsigned char *ptr; /* in payload */
59*98a5e356Schristos 	union {
60*98a5e356Schristos 		struct nlattr     nla;
61*98a5e356Schristos 		char              buf[NLA_HDRLEN];   /* align */
62*98a5e356Schristos 	}              u;
63*98a5e356Schristos 	unsigned char  payload[];
64*98a5e356Schristos } nlamsgbuf_t;
65*98a5e356Schristos 
66*98a5e356Schristos typedef struct nl_family {
67*98a5e356Schristos 	uint16_t id;
68*98a5e356Schristos 	uint32_t mcastgrp;
69*98a5e356Schristos } nl_family_t;
70*98a5e356Schristos 
71*98a5e356Schristos typedef struct nl_poll {
72*98a5e356Schristos 	uint32_t     dev;
73*98a5e356Schristos 	unsigned int eventcnt;
74*98a5e356Schristos } nl_poll_t;
75*98a5e356Schristos 
76*98a5e356Schristos typedef struct nl_target {
77*98a5e356Schristos 	int       found;
78*98a5e356Schristos 	uint32_t *value;
79*98a5e356Schristos } nl_target_t;
80*98a5e356Schristos 
81*98a5e356Schristos static const void *
nlmsg_ptr(const nlmsgbuf_t * m)82*98a5e356Schristos nlmsg_ptr(const nlmsgbuf_t *m)
83*98a5e356Schristos {
84*98a5e356Schristos 	return (&m->u.nlmsg);
85*98a5e356Schristos }
86*98a5e356Schristos 
87*98a5e356Schristos static size_t
nlmsg_len(const nlmsgbuf_t * m)88*98a5e356Schristos nlmsg_len(const nlmsgbuf_t *m)
89*98a5e356Schristos {
90*98a5e356Schristos 	return (m->u.nlmsg.nlmsg_len);
91*98a5e356Schristos }
92*98a5e356Schristos 
93*98a5e356Schristos static uint16_t
nlmsg_type(const nlmsgbuf_t * m)94*98a5e356Schristos nlmsg_type(const nlmsgbuf_t *m)
95*98a5e356Schristos {
96*98a5e356Schristos 	return (m->u.nlmsg.nlmsg_type);
97*98a5e356Schristos }
98*98a5e356Schristos 
99*98a5e356Schristos static nlmsgbuf_t *
nlmsg_new(uint16_t type,uint16_t flags,size_t len)100*98a5e356Schristos nlmsg_new(uint16_t type, uint16_t flags, size_t len)
101*98a5e356Schristos {
102*98a5e356Schristos 	nlmsgbuf_t *m;
103*98a5e356Schristos 	size_t siz;
104*98a5e356Schristos 
105*98a5e356Schristos 	if (len > SIZE_MAX - sizeof(*m) ||
106*98a5e356Schristos 	    (siz = sizeof(*m) + len) > UINT16_MAX ||
107*98a5e356Schristos 	    (m = calloc(1, siz)) == NULL)
108*98a5e356Schristos 		return (NULL);
109*98a5e356Schristos 
110*98a5e356Schristos 	m->siz = siz;
111*98a5e356Schristos 	m->len = len;
112*98a5e356Schristos 	m->ptr = m->payload;
113*98a5e356Schristos 	m->u.nlmsg.nlmsg_type = type;
114*98a5e356Schristos 	m->u.nlmsg.nlmsg_flags = NLM_F_REQUEST | flags;
115*98a5e356Schristos 	m->u.nlmsg.nlmsg_len = NLMSG_HDRLEN;
116*98a5e356Schristos 
117*98a5e356Schristos 	return (m);
118*98a5e356Schristos }
119*98a5e356Schristos 
120*98a5e356Schristos static nlamsgbuf_t *
nla_from_buf(const unsigned char ** ptr,size_t * len)121*98a5e356Schristos nla_from_buf(const unsigned char **ptr, size_t *len)
122*98a5e356Schristos {
123*98a5e356Schristos 	nlamsgbuf_t h, *a;
124*98a5e356Schristos 	size_t nlalen, skip;
125*98a5e356Schristos 
126*98a5e356Schristos 	if (*len < sizeof(h.u))
127*98a5e356Schristos 		return (NULL);
128*98a5e356Schristos 
129*98a5e356Schristos 	memset(&h, 0, sizeof(h));
130*98a5e356Schristos 	memcpy(&h.u, *ptr, sizeof(h.u));
131*98a5e356Schristos 
132*98a5e356Schristos 	if ((nlalen = h.u.nla.nla_len) < sizeof(h.u) || nlalen > *len ||
133*98a5e356Schristos 	    nlalen - sizeof(h.u) > UINT16_MAX ||
134*98a5e356Schristos 	    nlalen > SIZE_MAX - sizeof(*a) ||
135*98a5e356Schristos 	    (skip = NLMSG_ALIGN(nlalen)) > *len ||
136*98a5e356Schristos 	    (a = calloc(1, sizeof(*a) + nlalen - sizeof(h.u))) == NULL)
137*98a5e356Schristos 		return (NULL);
138*98a5e356Schristos 
139*98a5e356Schristos 	memcpy(&a->u, *ptr, nlalen);
140*98a5e356Schristos 	a->siz = sizeof(*a) + nlalen - sizeof(h.u);
141*98a5e356Schristos 	a->ptr = a->payload;
142*98a5e356Schristos 	a->len = nlalen - sizeof(h.u);
143*98a5e356Schristos 	*ptr += skip;
144*98a5e356Schristos 	*len -= skip;
145*98a5e356Schristos 
146*98a5e356Schristos 	return (a);
147*98a5e356Schristos }
148*98a5e356Schristos 
149*98a5e356Schristos static nlamsgbuf_t *
nla_getattr(nlamsgbuf_t * a)150*98a5e356Schristos nla_getattr(nlamsgbuf_t *a)
151*98a5e356Schristos {
152*98a5e356Schristos 	return (nla_from_buf((void *)&a->ptr, &a->len));
153*98a5e356Schristos }
154*98a5e356Schristos 
155*98a5e356Schristos static uint16_t
nla_type(const nlamsgbuf_t * a)156*98a5e356Schristos nla_type(const nlamsgbuf_t *a)
157*98a5e356Schristos {
158*98a5e356Schristos 	return (a->u.nla.nla_type);
159*98a5e356Schristos }
160*98a5e356Schristos 
161*98a5e356Schristos static nlamsgbuf_t *
nlmsg_getattr(nlmsgbuf_t * m)162*98a5e356Schristos nlmsg_getattr(nlmsgbuf_t *m)
163*98a5e356Schristos {
164*98a5e356Schristos 	return (nla_from_buf((void *)&m->ptr, &m->len));
165*98a5e356Schristos }
166*98a5e356Schristos 
167*98a5e356Schristos static int
nla_read(nlamsgbuf_t * a,void * buf,size_t cnt)168*98a5e356Schristos nla_read(nlamsgbuf_t *a, void *buf, size_t cnt)
169*98a5e356Schristos {
170*98a5e356Schristos 	if (cnt > a->u.nla.nla_len ||
171*98a5e356Schristos 	    fido_buf_read((void *)&a->ptr, &a->len, buf, cnt) < 0)
172*98a5e356Schristos 		return (-1);
173*98a5e356Schristos 
174*98a5e356Schristos 	a->u.nla.nla_len = (uint16_t)(a->u.nla.nla_len - cnt);
175*98a5e356Schristos 
176*98a5e356Schristos 	return (0);
177*98a5e356Schristos }
178*98a5e356Schristos 
179*98a5e356Schristos static nlmsgbuf_t *
nlmsg_from_buf(const unsigned char ** ptr,size_t * len)180*98a5e356Schristos nlmsg_from_buf(const unsigned char **ptr, size_t *len)
181*98a5e356Schristos {
182*98a5e356Schristos 	nlmsgbuf_t h, *m;
183*98a5e356Schristos 	size_t msglen, skip;
184*98a5e356Schristos 
185*98a5e356Schristos 	if (*len < sizeof(h.u))
186*98a5e356Schristos 		return (NULL);
187*98a5e356Schristos 
188*98a5e356Schristos 	memset(&h, 0, sizeof(h));
189*98a5e356Schristos 	memcpy(&h.u, *ptr, sizeof(h.u));
190*98a5e356Schristos 
191*98a5e356Schristos 	if ((msglen = h.u.nlmsg.nlmsg_len) < sizeof(h.u) || msglen > *len ||
192*98a5e356Schristos 	    msglen - sizeof(h.u) > UINT16_MAX ||
193*98a5e356Schristos 	    (skip = NLMSG_ALIGN(msglen)) > *len ||
194*98a5e356Schristos 	    (m = nlmsg_new(0, 0, msglen - sizeof(h.u))) == NULL)
195*98a5e356Schristos 		return (NULL);
196*98a5e356Schristos 
197*98a5e356Schristos 	memcpy(&m->u, *ptr, msglen);
198*98a5e356Schristos 	*ptr += skip;
199*98a5e356Schristos 	*len -= skip;
200*98a5e356Schristos 
201*98a5e356Schristos 	return (m);
202*98a5e356Schristos }
203*98a5e356Schristos 
204*98a5e356Schristos static int
nlmsg_read(nlmsgbuf_t * m,void * buf,size_t cnt)205*98a5e356Schristos nlmsg_read(nlmsgbuf_t *m, void *buf, size_t cnt)
206*98a5e356Schristos {
207*98a5e356Schristos 	if (cnt > m->u.nlmsg.nlmsg_len ||
208*98a5e356Schristos 	    fido_buf_read((void *)&m->ptr, &m->len, buf, cnt) < 0)
209*98a5e356Schristos 		return (-1);
210*98a5e356Schristos 
211*98a5e356Schristos 	m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len - cnt);
212*98a5e356Schristos 
213*98a5e356Schristos 	return (0);
214*98a5e356Schristos }
215*98a5e356Schristos 
216*98a5e356Schristos static int
nlmsg_write(nlmsgbuf_t * m,const void * buf,size_t cnt)217*98a5e356Schristos nlmsg_write(nlmsgbuf_t *m, const void *buf, size_t cnt)
218*98a5e356Schristos {
219*98a5e356Schristos 	if (cnt > UINT32_MAX - m->u.nlmsg.nlmsg_len ||
220*98a5e356Schristos 	    fido_buf_write(&m->ptr, &m->len, buf, cnt) < 0)
221*98a5e356Schristos 		return (-1);
222*98a5e356Schristos 
223*98a5e356Schristos 	m->u.nlmsg.nlmsg_len = (uint32_t)(m->u.nlmsg.nlmsg_len + cnt);
224*98a5e356Schristos 
225*98a5e356Schristos 	return (0);
226*98a5e356Schristos }
227*98a5e356Schristos 
228*98a5e356Schristos static int
nlmsg_set_genl(nlmsgbuf_t * m,uint8_t cmd)229*98a5e356Schristos nlmsg_set_genl(nlmsgbuf_t *m, uint8_t cmd)
230*98a5e356Schristos {
231*98a5e356Schristos 	genlmsgbuf_t g;
232*98a5e356Schristos 
233*98a5e356Schristos 	memset(&g, 0, sizeof(g));
234*98a5e356Schristos 	g.u.genl.cmd = cmd;
235*98a5e356Schristos 	g.u.genl.version = NFC_GENL_VERSION;
236*98a5e356Schristos 
237*98a5e356Schristos 	return (nlmsg_write(m, &g, sizeof(g)));
238*98a5e356Schristos }
239*98a5e356Schristos 
240*98a5e356Schristos static int
nlmsg_get_genl(nlmsgbuf_t * m,uint8_t cmd)241*98a5e356Schristos nlmsg_get_genl(nlmsgbuf_t *m, uint8_t cmd)
242*98a5e356Schristos {
243*98a5e356Schristos 	genlmsgbuf_t g;
244*98a5e356Schristos 
245*98a5e356Schristos 	memset(&g, 0, sizeof(g));
246*98a5e356Schristos 
247*98a5e356Schristos 	if (nlmsg_read(m, &g, sizeof(g)) < 0 || g.u.genl.cmd != cmd)
248*98a5e356Schristos 		return (-1);
249*98a5e356Schristos 
250*98a5e356Schristos 	return (0);
251*98a5e356Schristos }
252*98a5e356Schristos 
253*98a5e356Schristos static int
nlmsg_get_status(nlmsgbuf_t * m)254*98a5e356Schristos nlmsg_get_status(nlmsgbuf_t *m)
255*98a5e356Schristos {
256*98a5e356Schristos 	int status;
257*98a5e356Schristos 
258*98a5e356Schristos 	if (nlmsg_read(m, &status, sizeof(status)) < 0 || status == INT_MIN)
259*98a5e356Schristos 		return (-1);
260*98a5e356Schristos 	if (status < 0)
261*98a5e356Schristos 		status = -status;
262*98a5e356Schristos 
263*98a5e356Schristos 	return (status);
264*98a5e356Schristos }
265*98a5e356Schristos 
266*98a5e356Schristos static int
nlmsg_setattr(nlmsgbuf_t * m,uint16_t type,const void * ptr,size_t len)267*98a5e356Schristos nlmsg_setattr(nlmsgbuf_t *m, uint16_t type, const void *ptr, size_t len)
268*98a5e356Schristos {
269*98a5e356Schristos 	int r;
270*98a5e356Schristos 	char *padding;
271*98a5e356Schristos 	size_t skip;
272*98a5e356Schristos 	nlamsgbuf_t a;
273*98a5e356Schristos 
274*98a5e356Schristos 	if ((skip = NLMSG_ALIGN(len)) > UINT16_MAX - sizeof(a.u) ||
275*98a5e356Schristos 	    skip < len || (padding = calloc(1, skip - len)) == NULL)
276*98a5e356Schristos 		return (-1);
277*98a5e356Schristos 
278*98a5e356Schristos 	memset(&a, 0, sizeof(a));
279*98a5e356Schristos 	a.u.nla.nla_type = type;
280*98a5e356Schristos 	a.u.nla.nla_len = (uint16_t)(len + sizeof(a.u));
281*98a5e356Schristos 	r = nlmsg_write(m, &a.u, sizeof(a.u)) < 0 ||
282*98a5e356Schristos 	    nlmsg_write(m, ptr, len) < 0 ||
283*98a5e356Schristos 	    nlmsg_write(m, padding, skip - len) < 0 ? -1 : 0;
284*98a5e356Schristos 
285*98a5e356Schristos 	free(padding);
286*98a5e356Schristos 
287*98a5e356Schristos 	return (r);
288*98a5e356Schristos }
289*98a5e356Schristos 
290*98a5e356Schristos static int
nlmsg_set_u16(nlmsgbuf_t * m,uint16_t type,uint16_t val)291*98a5e356Schristos nlmsg_set_u16(nlmsgbuf_t *m, uint16_t type, uint16_t val)
292*98a5e356Schristos {
293*98a5e356Schristos 	return (nlmsg_setattr(m, type, &val, sizeof(val)));
294*98a5e356Schristos }
295*98a5e356Schristos 
296*98a5e356Schristos static int
nlmsg_set_u32(nlmsgbuf_t * m,uint16_t type,uint32_t val)297*98a5e356Schristos nlmsg_set_u32(nlmsgbuf_t *m, uint16_t type, uint32_t val)
298*98a5e356Schristos {
299*98a5e356Schristos 	return (nlmsg_setattr(m, type, &val, sizeof(val)));
300*98a5e356Schristos }
301*98a5e356Schristos 
302*98a5e356Schristos static int
nlmsg_set_str(nlmsgbuf_t * m,uint16_t type,const char * val)303*98a5e356Schristos nlmsg_set_str(nlmsgbuf_t *m, uint16_t type, const char *val)
304*98a5e356Schristos {
305*98a5e356Schristos 	return (nlmsg_setattr(m, type, val, strlen(val) + 1));
306*98a5e356Schristos }
307*98a5e356Schristos 
308*98a5e356Schristos static int
nla_get_u16(nlamsgbuf_t * a,uint16_t * v)309*98a5e356Schristos nla_get_u16(nlamsgbuf_t *a, uint16_t *v)
310*98a5e356Schristos {
311*98a5e356Schristos 	return (nla_read(a, v, sizeof(*v)));
312*98a5e356Schristos }
313*98a5e356Schristos 
314*98a5e356Schristos static int
nla_get_u32(nlamsgbuf_t * a,uint32_t * v)315*98a5e356Schristos nla_get_u32(nlamsgbuf_t *a, uint32_t *v)
316*98a5e356Schristos {
317*98a5e356Schristos 	return (nla_read(a, v, sizeof(*v)));
318*98a5e356Schristos }
319*98a5e356Schristos 
320*98a5e356Schristos static char *
nla_get_str(nlamsgbuf_t * a)321*98a5e356Schristos nla_get_str(nlamsgbuf_t *a)
322*98a5e356Schristos {
323*98a5e356Schristos 	size_t n;
324*98a5e356Schristos 	char *s = NULL;
325*98a5e356Schristos 
326*98a5e356Schristos 	if ((n = a->len) < 1 || a->ptr[n - 1] != '\0' ||
327*98a5e356Schristos 	    (s = calloc(1, n)) == NULL || nla_read(a, s, n) < 0) {
328*98a5e356Schristos 		free(s);
329*98a5e356Schristos 		return (NULL);
330*98a5e356Schristos 	}
331*98a5e356Schristos 	s[n - 1] = '\0';
332*98a5e356Schristos 
333*98a5e356Schristos 	return (s);
334*98a5e356Schristos }
335*98a5e356Schristos 
336*98a5e356Schristos static int
nlmsg_tx(int fd,const nlmsgbuf_t * m)337*98a5e356Schristos nlmsg_tx(int fd, const nlmsgbuf_t *m)
338*98a5e356Schristos {
339*98a5e356Schristos 	ssize_t r;
340*98a5e356Schristos 
341*98a5e356Schristos 	if ((r = WRITE(fd, nlmsg_ptr(m), nlmsg_len(m))) == -1) {
342*98a5e356Schristos 		fido_log_error(errno, "%s: write", __func__);
343*98a5e356Schristos 		return (-1);
344*98a5e356Schristos 	}
345*98a5e356Schristos 	if (r < 0 || (size_t)r != nlmsg_len(m)) {
346*98a5e356Schristos 		fido_log_debug("%s: %zd != %zu", __func__, r, nlmsg_len(m));
347*98a5e356Schristos 		return (-1);
348*98a5e356Schristos 	}
349*98a5e356Schristos 	fido_log_xxd(nlmsg_ptr(m), nlmsg_len(m), "%s", __func__);
350*98a5e356Schristos 
351*98a5e356Schristos 	return (0);
352*98a5e356Schristos }
353*98a5e356Schristos 
354*98a5e356Schristos static ssize_t
nlmsg_rx(int fd,unsigned char * ptr,size_t len,int ms)355*98a5e356Schristos nlmsg_rx(int fd, unsigned char *ptr, size_t len, int ms)
356*98a5e356Schristos {
357*98a5e356Schristos 	ssize_t r;
358*98a5e356Schristos 
359*98a5e356Schristos 	if (len > SSIZE_MAX) {
360*98a5e356Schristos 		fido_log_debug("%s: len", __func__);
361*98a5e356Schristos 		return (-1);
362*98a5e356Schristos 	}
363*98a5e356Schristos 	if (fido_hid_unix_wait(fd, ms, NULL) < 0) {
364*98a5e356Schristos 		fido_log_debug("%s: fido_hid_unix_wait", __func__);
365*98a5e356Schristos 		return (-1);
366*98a5e356Schristos 	}
367*98a5e356Schristos 	if ((r = READ(fd, ptr, len)) == -1) {
368*98a5e356Schristos 		fido_log_error(errno, "%s: read %zd", __func__, r);
369*98a5e356Schristos 		return (-1);
370*98a5e356Schristos 	}
371*98a5e356Schristos 	fido_log_xxd(ptr, (size_t)r, "%s", __func__);
372*98a5e356Schristos 
373*98a5e356Schristos 	return (r);
374*98a5e356Schristos }
375*98a5e356Schristos 
376*98a5e356Schristos static int
nlmsg_iter(nlmsgbuf_t * m,void * arg,int (* parser)(nlamsgbuf_t *,void *))377*98a5e356Schristos nlmsg_iter(nlmsgbuf_t *m, void *arg, int (*parser)(nlamsgbuf_t *, void *))
378*98a5e356Schristos {
379*98a5e356Schristos 	nlamsgbuf_t *a;
380*98a5e356Schristos 	int r;
381*98a5e356Schristos 
382*98a5e356Schristos 	while ((a = nlmsg_getattr(m)) != NULL) {
383*98a5e356Schristos 		r = parser(a, arg);
384*98a5e356Schristos 		free(a);
385*98a5e356Schristos 		if (r < 0) {
386*98a5e356Schristos 			fido_log_debug("%s: parser", __func__);
387*98a5e356Schristos 			return (-1);
388*98a5e356Schristos 		}
389*98a5e356Schristos 	}
390*98a5e356Schristos 
391*98a5e356Schristos 	return (0);
392*98a5e356Schristos }
393*98a5e356Schristos 
394*98a5e356Schristos static int
nla_iter(nlamsgbuf_t * g,void * arg,int (* parser)(nlamsgbuf_t *,void *))395*98a5e356Schristos nla_iter(nlamsgbuf_t *g, void *arg, int (*parser)(nlamsgbuf_t *, void *))
396*98a5e356Schristos {
397*98a5e356Schristos 	nlamsgbuf_t *a;
398*98a5e356Schristos 	int r;
399*98a5e356Schristos 
400*98a5e356Schristos 	while ((a = nla_getattr(g)) != NULL) {
401*98a5e356Schristos 		r = parser(a, arg);
402*98a5e356Schristos 		free(a);
403*98a5e356Schristos 		if (r < 0) {
404*98a5e356Schristos 			fido_log_debug("%s: parser", __func__);
405*98a5e356Schristos 			return (-1);
406*98a5e356Schristos 		}
407*98a5e356Schristos 	}
408*98a5e356Schristos 
409*98a5e356Schristos 	return (0);
410*98a5e356Schristos }
411*98a5e356Schristos 
412*98a5e356Schristos static int
nl_parse_reply(const uint8_t * blob,size_t blob_len,uint16_t msg_type,uint8_t genl_cmd,void * arg,int (* parser)(nlamsgbuf_t *,void *))413*98a5e356Schristos nl_parse_reply(const uint8_t *blob, size_t blob_len, uint16_t msg_type,
414*98a5e356Schristos     uint8_t genl_cmd, void *arg, int (*parser)(nlamsgbuf_t *, void *))
415*98a5e356Schristos {
416*98a5e356Schristos 	nlmsgbuf_t *m;
417*98a5e356Schristos 	int r;
418*98a5e356Schristos 
419*98a5e356Schristos 	while (blob_len) {
420*98a5e356Schristos 		if ((m = nlmsg_from_buf(&blob, &blob_len)) == NULL) {
421*98a5e356Schristos 			fido_log_debug("%s: nlmsg", __func__);
422*98a5e356Schristos 			return (-1);
423*98a5e356Schristos 		}
424*98a5e356Schristos 		if (nlmsg_type(m) == NLMSG_ERROR) {
425*98a5e356Schristos 			r = nlmsg_get_status(m);
426*98a5e356Schristos 			free(m);
427*98a5e356Schristos 			return (r);
428*98a5e356Schristos 		}
429*98a5e356Schristos 		if (nlmsg_type(m) != msg_type ||
430*98a5e356Schristos 		    nlmsg_get_genl(m, genl_cmd) < 0) {
431*98a5e356Schristos 			fido_log_debug("%s: skipping", __func__);
432*98a5e356Schristos 			free(m);
433*98a5e356Schristos 			continue;
434*98a5e356Schristos 		}
435*98a5e356Schristos 		if (parser != NULL && nlmsg_iter(m, arg, parser) < 0) {
436*98a5e356Schristos 			fido_log_debug("%s: nlmsg_iter", __func__);
437*98a5e356Schristos 			free(m);
438*98a5e356Schristos 			return (-1);
439*98a5e356Schristos 		}
440*98a5e356Schristos 		free(m);
441*98a5e356Schristos 	}
442*98a5e356Schristos 
443*98a5e356Schristos 	return (0);
444*98a5e356Schristos }
445*98a5e356Schristos 
446*98a5e356Schristos static int
parse_mcastgrp(nlamsgbuf_t * a,void * arg)447*98a5e356Schristos parse_mcastgrp(nlamsgbuf_t *a, void *arg)
448*98a5e356Schristos {
449*98a5e356Schristos 	nl_family_t *family = arg;
450*98a5e356Schristos 	char *name;
451*98a5e356Schristos 
452*98a5e356Schristos 	switch (nla_type(a)) {
453*98a5e356Schristos 	case CTRL_ATTR_MCAST_GRP_NAME:
454*98a5e356Schristos 		if ((name = nla_get_str(a)) == NULL ||
455*98a5e356Schristos 		    strcmp(name, NFC_GENL_MCAST_EVENT_NAME) != 0) {
456*98a5e356Schristos 			free(name);
457*98a5e356Schristos 			return (-1); /* XXX skip? */
458*98a5e356Schristos 		}
459*98a5e356Schristos 		free(name);
460*98a5e356Schristos 		return (0);
461*98a5e356Schristos 	case CTRL_ATTR_MCAST_GRP_ID:
462*98a5e356Schristos 		if (family->mcastgrp)
463*98a5e356Schristos 			break;
464*98a5e356Schristos 		if (nla_get_u32(a, &family->mcastgrp) < 0) {
465*98a5e356Schristos 			fido_log_debug("%s: group", __func__);
466*98a5e356Schristos 			return (-1);
467*98a5e356Schristos 		}
468*98a5e356Schristos 		return (0);
469*98a5e356Schristos 	}
470*98a5e356Schristos 
471*98a5e356Schristos 	fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
472*98a5e356Schristos 
473*98a5e356Schristos 	return (0);
474*98a5e356Schristos }
475*98a5e356Schristos 
476*98a5e356Schristos static int
parse_mcastgrps(nlamsgbuf_t * a,void * arg)477*98a5e356Schristos parse_mcastgrps(nlamsgbuf_t *a, void *arg)
478*98a5e356Schristos {
479*98a5e356Schristos 	return (nla_iter(a, arg, parse_mcastgrp));
480*98a5e356Schristos }
481*98a5e356Schristos 
482*98a5e356Schristos static int
parse_family(nlamsgbuf_t * a,void * arg)483*98a5e356Schristos parse_family(nlamsgbuf_t *a, void *arg)
484*98a5e356Schristos {
485*98a5e356Schristos 	nl_family_t *family = arg;
486*98a5e356Schristos 
487*98a5e356Schristos 	switch (nla_type(a)) {
488*98a5e356Schristos 	case CTRL_ATTR_FAMILY_ID:
489*98a5e356Schristos 		if (family->id)
490*98a5e356Schristos 			break;
491*98a5e356Schristos 		if (nla_get_u16(a, &family->id) < 0) {
492*98a5e356Schristos 			fido_log_debug("%s: id", __func__);
493*98a5e356Schristos 			return (-1);
494*98a5e356Schristos 		}
495*98a5e356Schristos 		return (0);
496*98a5e356Schristos 	case CTRL_ATTR_MCAST_GROUPS:
497*98a5e356Schristos 		return (nla_iter(a, family, parse_mcastgrps));
498*98a5e356Schristos 	}
499*98a5e356Schristos 
500*98a5e356Schristos 	fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
501*98a5e356Schristos 
502*98a5e356Schristos 	return (0);
503*98a5e356Schristos }
504*98a5e356Schristos 
505*98a5e356Schristos static int
nl_get_nfc_family(int fd,uint16_t * type,uint32_t * mcastgrp)506*98a5e356Schristos nl_get_nfc_family(int fd, uint16_t *type, uint32_t *mcastgrp)
507*98a5e356Schristos {
508*98a5e356Schristos 	nlmsgbuf_t *m;
509*98a5e356Schristos 	uint8_t reply[512];
510*98a5e356Schristos 	nl_family_t family;
511*98a5e356Schristos 	ssize_t r;
512*98a5e356Schristos 	int ok;
513*98a5e356Schristos 
514*98a5e356Schristos 	if ((m = nlmsg_new(GENL_ID_CTRL, 0, 64)) == NULL ||
515*98a5e356Schristos 	    nlmsg_set_genl(m, CTRL_CMD_GETFAMILY) < 0 ||
516*98a5e356Schristos 	    nlmsg_set_u16(m, CTRL_ATTR_FAMILY_ID, GENL_ID_CTRL) < 0 ||
517*98a5e356Schristos 	    nlmsg_set_str(m, CTRL_ATTR_FAMILY_NAME, NFC_GENL_NAME) < 0 ||
518*98a5e356Schristos 	    nlmsg_tx(fd, m) < 0) {
519*98a5e356Schristos 		free(m);
520*98a5e356Schristos 		return (-1);
521*98a5e356Schristos 	}
522*98a5e356Schristos 	free(m);
523*98a5e356Schristos 	memset(&family, 0, sizeof(family));
524*98a5e356Schristos 	if ((r = nlmsg_rx(fd, reply, sizeof(reply), -1)) < 0) {
525*98a5e356Schristos 		fido_log_debug("%s: nlmsg_rx", __func__);
526*98a5e356Schristos 		return (-1);
527*98a5e356Schristos 	}
528*98a5e356Schristos 	if ((ok = nl_parse_reply(reply, (size_t)r, GENL_ID_CTRL,
529*98a5e356Schristos 	    CTRL_CMD_NEWFAMILY, &family, parse_family)) != 0) {
530*98a5e356Schristos 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
531*98a5e356Schristos 		return (-1);
532*98a5e356Schristos 	}
533*98a5e356Schristos 	if (family.id == 0 || family.mcastgrp == 0) {
534*98a5e356Schristos 		fido_log_debug("%s: missing attr", __func__);
535*98a5e356Schristos 		return (-1);
536*98a5e356Schristos 	}
537*98a5e356Schristos 	*type = family.id;
538*98a5e356Schristos 	*mcastgrp = family.mcastgrp;
539*98a5e356Schristos 
540*98a5e356Schristos 	return (0);
541*98a5e356Schristos }
542*98a5e356Schristos 
543*98a5e356Schristos static int
parse_target(nlamsgbuf_t * a,void * arg)544*98a5e356Schristos parse_target(nlamsgbuf_t *a, void *arg)
545*98a5e356Schristos {
546*98a5e356Schristos 	nl_target_t *t = arg;
547*98a5e356Schristos 
548*98a5e356Schristos 	if (t->found || nla_type(a) != NFC_ATTR_TARGET_INDEX) {
549*98a5e356Schristos 		fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
550*98a5e356Schristos 		return (0);
551*98a5e356Schristos 	}
552*98a5e356Schristos 	if (nla_get_u32(a, t->value) < 0) {
553*98a5e356Schristos 		fido_log_debug("%s: target", __func__);
554*98a5e356Schristos 		return (-1);
555*98a5e356Schristos 	}
556*98a5e356Schristos 	t->found = 1;
557*98a5e356Schristos 
558*98a5e356Schristos 	return (0);
559*98a5e356Schristos }
560*98a5e356Schristos 
561*98a5e356Schristos int
fido_nl_power_nfc(fido_nl_t * nl,uint32_t dev)562*98a5e356Schristos fido_nl_power_nfc(fido_nl_t *nl, uint32_t dev)
563*98a5e356Schristos {
564*98a5e356Schristos 	nlmsgbuf_t *m;
565*98a5e356Schristos 	uint8_t reply[512];
566*98a5e356Schristos 	ssize_t r;
567*98a5e356Schristos 	int ok;
568*98a5e356Schristos 
569*98a5e356Schristos 	if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
570*98a5e356Schristos 	    nlmsg_set_genl(m, NFC_CMD_DEV_UP) < 0 ||
571*98a5e356Schristos 	    nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
572*98a5e356Schristos 	    nlmsg_tx(nl->fd, m) < 0) {
573*98a5e356Schristos 		free(m);
574*98a5e356Schristos 		return (-1);
575*98a5e356Schristos 	}
576*98a5e356Schristos 	free(m);
577*98a5e356Schristos 	if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
578*98a5e356Schristos 		fido_log_debug("%s: nlmsg_rx", __func__);
579*98a5e356Schristos 		return (-1);
580*98a5e356Schristos 	}
581*98a5e356Schristos 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
582*98a5e356Schristos 	    NFC_CMD_DEV_UP, NULL, NULL)) != 0 && ok != EALREADY) {
583*98a5e356Schristos 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
584*98a5e356Schristos 		return (-1);
585*98a5e356Schristos 	}
586*98a5e356Schristos 
587*98a5e356Schristos 	return (0);
588*98a5e356Schristos }
589*98a5e356Schristos 
590*98a5e356Schristos static int
nl_nfc_poll(fido_nl_t * nl,uint32_t dev)591*98a5e356Schristos nl_nfc_poll(fido_nl_t *nl, uint32_t dev)
592*98a5e356Schristos {
593*98a5e356Schristos 	nlmsgbuf_t *m;
594*98a5e356Schristos 	uint8_t reply[512];
595*98a5e356Schristos 	ssize_t r;
596*98a5e356Schristos 	int ok;
597*98a5e356Schristos 
598*98a5e356Schristos 	if ((m = nlmsg_new(nl->nfc_type, NLM_F_ACK, 64)) == NULL ||
599*98a5e356Schristos 	    nlmsg_set_genl(m, NFC_CMD_START_POLL) < 0 ||
600*98a5e356Schristos 	    nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
601*98a5e356Schristos 	    nlmsg_set_u32(m, NFC_ATTR_PROTOCOLS, NFC_PROTO_ISO14443_MASK) < 0 ||
602*98a5e356Schristos 	    nlmsg_tx(nl->fd, m) < 0) {
603*98a5e356Schristos 		free(m);
604*98a5e356Schristos 		return (-1);
605*98a5e356Schristos 	}
606*98a5e356Schristos 	free(m);
607*98a5e356Schristos 	if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1)) < 0) {
608*98a5e356Schristos 		fido_log_debug("%s: nlmsg_rx", __func__);
609*98a5e356Schristos 		return (-1);
610*98a5e356Schristos 	}
611*98a5e356Schristos 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
612*98a5e356Schristos 	    NFC_CMD_START_POLL, NULL, NULL)) != 0) {
613*98a5e356Schristos 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
614*98a5e356Schristos 		return (-1);
615*98a5e356Schristos 	}
616*98a5e356Schristos 
617*98a5e356Schristos 	return (0);
618*98a5e356Schristos }
619*98a5e356Schristos 
620*98a5e356Schristos static int
nl_dump_nfc_target(fido_nl_t * nl,uint32_t dev,uint32_t * target,int ms)621*98a5e356Schristos nl_dump_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target, int ms)
622*98a5e356Schristos {
623*98a5e356Schristos 	nlmsgbuf_t *m;
624*98a5e356Schristos 	nl_target_t t;
625*98a5e356Schristos 	uint8_t reply[512];
626*98a5e356Schristos 	ssize_t r;
627*98a5e356Schristos 	int ok;
628*98a5e356Schristos 
629*98a5e356Schristos 	if ((m = nlmsg_new(nl->nfc_type, NLM_F_DUMP, 64)) == NULL ||
630*98a5e356Schristos 	    nlmsg_set_genl(m, NFC_CMD_GET_TARGET) < 0 ||
631*98a5e356Schristos 	    nlmsg_set_u32(m, NFC_ATTR_DEVICE_INDEX, dev) < 0 ||
632*98a5e356Schristos 	    nlmsg_tx(nl->fd, m) < 0) {
633*98a5e356Schristos 		free(m);
634*98a5e356Schristos 		return (-1);
635*98a5e356Schristos 	}
636*98a5e356Schristos 	free(m);
637*98a5e356Schristos 	if ((r = nlmsg_rx(nl->fd, reply, sizeof(reply), ms)) < 0) {
638*98a5e356Schristos 		fido_log_debug("%s: nlmsg_rx", __func__);
639*98a5e356Schristos 		return (-1);
640*98a5e356Schristos 	}
641*98a5e356Schristos 	memset(&t, 0, sizeof(t));
642*98a5e356Schristos 	t.value = target;
643*98a5e356Schristos 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
644*98a5e356Schristos 	    NFC_CMD_GET_TARGET, &t, parse_target)) != 0) {
645*98a5e356Schristos 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
646*98a5e356Schristos 		return (-1);
647*98a5e356Schristos 	}
648*98a5e356Schristos 	if (!t.found) {
649*98a5e356Schristos 		fido_log_debug("%s: target not found", __func__);
650*98a5e356Schristos 		return (-1);
651*98a5e356Schristos 	}
652*98a5e356Schristos 
653*98a5e356Schristos 	return (0);
654*98a5e356Schristos }
655*98a5e356Schristos 
656*98a5e356Schristos static int
parse_nfc_event(nlamsgbuf_t * a,void * arg)657*98a5e356Schristos parse_nfc_event(nlamsgbuf_t *a, void *arg)
658*98a5e356Schristos {
659*98a5e356Schristos 	nl_poll_t *ctx = arg;
660*98a5e356Schristos 	uint32_t dev;
661*98a5e356Schristos 
662*98a5e356Schristos 	if (nla_type(a) != NFC_ATTR_DEVICE_INDEX) {
663*98a5e356Schristos 		fido_log_debug("%s: ignoring nla 0x%x", __func__, nla_type(a));
664*98a5e356Schristos 		return (0);
665*98a5e356Schristos 	}
666*98a5e356Schristos 	if (nla_get_u32(a, &dev) < 0) {
667*98a5e356Schristos 		fido_log_debug("%s: dev", __func__);
668*98a5e356Schristos 		return (-1);
669*98a5e356Schristos 	}
670*98a5e356Schristos 	if (dev == ctx->dev)
671*98a5e356Schristos 		ctx->eventcnt++;
672*98a5e356Schristos 	else
673*98a5e356Schristos 		fido_log_debug("%s: ignoring dev 0x%x", __func__, dev);
674*98a5e356Schristos 
675*98a5e356Schristos 	return (0);
676*98a5e356Schristos }
677*98a5e356Schristos 
678*98a5e356Schristos int
fido_nl_get_nfc_target(fido_nl_t * nl,uint32_t dev,uint32_t * target)679*98a5e356Schristos fido_nl_get_nfc_target(fido_nl_t *nl, uint32_t dev, uint32_t *target)
680*98a5e356Schristos {
681*98a5e356Schristos 	uint8_t reply[512];
682*98a5e356Schristos 	nl_poll_t ctx;
683*98a5e356Schristos 	ssize_t r;
684*98a5e356Schristos 	int ok;
685*98a5e356Schristos 
686*98a5e356Schristos 	if (nl_nfc_poll(nl, dev) < 0) {
687*98a5e356Schristos 		fido_log_debug("%s: nl_nfc_poll", __func__);
688*98a5e356Schristos 		return (-1);
689*98a5e356Schristos 	}
690*98a5e356Schristos #ifndef FIDO_FUZZ
691*98a5e356Schristos 	if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP,
692*98a5e356Schristos 	    &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) {
693*98a5e356Schristos 		fido_log_error(errno, "%s: setsockopt add", __func__);
694*98a5e356Schristos 		return (-1);
695*98a5e356Schristos 	}
696*98a5e356Schristos #endif
697*98a5e356Schristos 	r = nlmsg_rx(nl->fd, reply, sizeof(reply), -1);
698*98a5e356Schristos #ifndef FIDO_FUZZ
699*98a5e356Schristos 	if (setsockopt(nl->fd, SOL_NETLINK, NETLINK_DROP_MEMBERSHIP,
700*98a5e356Schristos 	    &nl->nfc_mcastgrp, sizeof(nl->nfc_mcastgrp)) == -1) {
701*98a5e356Schristos 		fido_log_error(errno, "%s: setsockopt drop", __func__);
702*98a5e356Schristos 		return (-1);
703*98a5e356Schristos 	}
704*98a5e356Schristos #endif
705*98a5e356Schristos 	if (r < 0) {
706*98a5e356Schristos 		fido_log_debug("%s: nlmsg_rx", __func__);
707*98a5e356Schristos 		return (-1);
708*98a5e356Schristos 	}
709*98a5e356Schristos 	memset(&ctx, 0, sizeof(ctx));
710*98a5e356Schristos 	ctx.dev = dev;
711*98a5e356Schristos 	if ((ok = nl_parse_reply(reply, (size_t)r, nl->nfc_type,
712*98a5e356Schristos 	    NFC_EVENT_TARGETS_FOUND, &ctx, parse_nfc_event)) != 0) {
713*98a5e356Schristos 		fido_log_debug("%s: nl_parse_reply: %d", __func__, ok);
714*98a5e356Schristos 		return (-1);
715*98a5e356Schristos 	}
716*98a5e356Schristos 	if (ctx.eventcnt == 0) {
717*98a5e356Schristos 		fido_log_debug("%s: dev 0x%x not observed", __func__, dev);
718*98a5e356Schristos 		return (-1);
719*98a5e356Schristos 	}
720*98a5e356Schristos 	if (nl_dump_nfc_target(nl, dev, target, -1) < 0) {
721*98a5e356Schristos 		fido_log_debug("%s: nl_dump_nfc_target", __func__);
722*98a5e356Schristos 		return (-1);
723*98a5e356Schristos 	}
724*98a5e356Schristos 
725*98a5e356Schristos 	return (0);
726*98a5e356Schristos }
727*98a5e356Schristos 
728*98a5e356Schristos void
fido_nl_free(fido_nl_t ** nlp)729*98a5e356Schristos fido_nl_free(fido_nl_t **nlp)
730*98a5e356Schristos {
731*98a5e356Schristos 	fido_nl_t *nl;
732*98a5e356Schristos 
733*98a5e356Schristos 	if (nlp == NULL || (nl = *nlp) == NULL)
734*98a5e356Schristos 		return;
735*98a5e356Schristos 	if (nl->fd != -1 && close(nl->fd) == -1)
736*98a5e356Schristos 		fido_log_error(errno, "%s: close", __func__);
737*98a5e356Schristos 
738*98a5e356Schristos 	free(nl);
739*98a5e356Schristos 	*nlp = NULL;
740*98a5e356Schristos }
741*98a5e356Schristos 
742*98a5e356Schristos fido_nl_t *
fido_nl_new(void)743*98a5e356Schristos fido_nl_new(void)
744*98a5e356Schristos {
745*98a5e356Schristos 	fido_nl_t *nl;
746*98a5e356Schristos 	int ok = -1;
747*98a5e356Schristos 
748*98a5e356Schristos 	if ((nl = calloc(1, sizeof(*nl))) == NULL)
749*98a5e356Schristos 		return (NULL);
750*98a5e356Schristos 	if ((nl->fd = socket(AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC,
751*98a5e356Schristos 	    NETLINK_GENERIC)) == -1) {
752*98a5e356Schristos 		fido_log_error(errno, "%s: socket", __func__);
753*98a5e356Schristos 		goto fail;
754*98a5e356Schristos 	}
755*98a5e356Schristos 	nl->saddr.nl_family = AF_NETLINK;
756*98a5e356Schristos 	if (bind(nl->fd, (struct sockaddr *)&nl->saddr,
757*98a5e356Schristos 	    sizeof(nl->saddr)) == -1) {
758*98a5e356Schristos 		fido_log_error(errno, "%s: bind", __func__);
759*98a5e356Schristos 		goto fail;
760*98a5e356Schristos 	}
761*98a5e356Schristos 	if (nl_get_nfc_family(nl->fd, &nl->nfc_type, &nl->nfc_mcastgrp) < 0) {
762*98a5e356Schristos 		fido_log_debug("%s: nl_get_nfc_family", __func__);
763*98a5e356Schristos 		goto fail;
764*98a5e356Schristos 	}
765*98a5e356Schristos 
766*98a5e356Schristos 	ok = 0;
767*98a5e356Schristos fail:
768*98a5e356Schristos 	if (ok < 0)
769*98a5e356Schristos 		fido_nl_free(&nl);
770*98a5e356Schristos 
771*98a5e356Schristos 	return (nl);
772*98a5e356Schristos }
773*98a5e356Schristos 
774*98a5e356Schristos #ifdef FIDO_FUZZ
775*98a5e356Schristos void
set_netlink_io_functions(ssize_t (* read_f)(int,void *,size_t),ssize_t (* write_f)(int,const void *,size_t))776*98a5e356Schristos set_netlink_io_functions(ssize_t (*read_f)(int, void *, size_t),
777*98a5e356Schristos     ssize_t (*write_f)(int, const void *, size_t))
778*98a5e356Schristos {
779*98a5e356Schristos 	fuzz_read = read_f;
780*98a5e356Schristos 	fuzz_write = write_f;
781*98a5e356Schristos }
782*98a5e356Schristos #endif
783