1/*
2 * Copyright (c) 1999, Boris Popov
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include <sys/param.h>
34#include <sys/ioctl.h>
35#include <sys/socket.h>
36#include <sys/sysctl.h>
37#include <sys/time.h>
38
39#include <arpa/inet.h>
40#include <net/if.h>
41#include <net/if_var.h>
42#include <net/if_dl.h>
43#include <net/if_types.h>
44#include <net/route.h>
45
46/* IPX */
47#include <netipx/ipx.h>
48#include <netipx/ipx_if.h>
49
50#include <ctype.h>
51#include <err.h>
52#include <errno.h>
53#include <fcntl.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59#include <netncp/ncp_lib.h>
60
61#define	IPX_NODE_LEN	6
62
63typedef u_long		IPXNet;
64typedef u_short		IPXPort;
65typedef union ipx_host	IPXNode;
66
67
68void
69ipx_fprint_node(FILE * file, IPXNode node){
70	fprintf(file, "%02X%02X%02X%02X%02X%02X",
71		(unsigned char) node.c_host[0],
72		(unsigned char) node.c_host[1],
73		(unsigned char) node.c_host[2],
74		(unsigned char) node.c_host[3],
75		(unsigned char) node.c_host[4],
76		(unsigned char) node.c_host[5]
77	    );
78}
79
80void
81ipx_fprint_network(FILE * file, const IPXNet net){
82	fprintf(file, "%08X", (u_int32_t)ntohl(net));
83}
84
85void
86ipx_fprint_port(FILE * file, IPXPort port)
87{
88	fprintf(file, "%04X", ntohs(port));
89}
90
91void
92ipx_fprint_addr(FILE * file, struct ipx_addr *ipx)
93{
94	ipx_fprint_network(file, ipx_netlong(*ipx));
95	fprintf(file, ":");
96	ipx_fprint_node(file, ipx->x_host);
97	fprintf(file, ":");
98	ipx_fprint_port(file, ipx->x_port);
99}
100
101void
102ipx_print_node(IPXNode node)
103{
104	ipx_fprint_node(stdout, node);
105}
106
107void
108ipx_print_network(IPXNet net)
109{
110	ipx_fprint_network(stdout, net);
111}
112
113void
114ipx_print_port(IPXPort port)
115{
116	ipx_fprint_port(stdout, port);
117}
118
119void
120ipx_print_addr(struct ipx_addr *ipx)
121{
122	ipx_fprint_addr(stdout, ipx);
123}
124
125int
126ipx_sscanf_node(char *buf, unsigned char node[6])
127{
128	int i;
129	int n[6];
130
131	if ((i = sscanf(buf, "%2x%2x%2x%2x%2x%2x",
132			&(n[0]), &(n[1]), &(n[2]),
133			&(n[3]), &(n[4]), &(n[5]))) != 6)
134	{
135		return i;
136	}
137	for (i = 0; i < 6; i++)
138	{
139		node[i] = n[i];
140	}
141	return 6;
142}
143
144int
145ipx_sscanf_saddr(char *buf, struct sockaddr_ipx *target)
146{
147	char *p;
148	struct sockaddr_ipx addr;
149	unsigned long sipx_net;
150
151	addr.sipx_family = AF_IPX;
152/*!!	addr.sipx_type = NCP_PTYPE;*/
153
154	if (sscanf(buf, "%lx", &sipx_net) != 1)
155	{
156		return 1;
157	}
158	((union ipx_net_u*)(&addr.sipx_addr.x_net))->long_e = htonl(sipx_net);
159	if ((p = strchr(buf, ':')) == NULL){
160		return 1;
161	}
162	p += 1;
163	if (ipx_sscanf_node(p, addr.sipx_node) != 6)
164	{
165		return 1;
166	}
167	if ((p = strchr(p, ':')) == NULL)
168	{
169		return 1;
170	}
171	p += 1;
172	if (sscanf(p, "%hx", &addr.sipx_port) != 1)
173	{
174		return 1;
175	}
176	addr.sipx_port = htons(addr.sipx_port);
177	*target = addr;
178	return 0;
179}
180
181
182void ipx_assign_node(IPXNode *dest, IPXNode *src) {
183	memcpy(dest, src, IPX_NODE_LEN);
184}
185
186
187static void	rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *);
188static int	if_ipxscan(int addrcount, struct sockaddr_dl *sdl,
189		    struct if_msghdr *ifm, struct ifa_msghdr *ifam,
190		    struct ipx_addr *addr);
191
192/*
193 * Find an IPX interface.
194 * ifname specifies interface name, if NULL search for all interfaces
195 *        if ifname[0]='0', also all interfaces, but return its name
196 * addr   on input preferred net address can be specified or 0 for any,
197 *        on return contains full address (except port)
198 * returns 0 if interface was found
199 */
200int
201ipx_iffind(char *ifname,struct ipx_addr *addr){
202	char name[32];
203	int all=0, flags, foundit = 0, addrcount;
204	struct	if_msghdr *ifm, *nextifm;
205	struct	ifa_msghdr *ifam;
206	struct	sockaddr_dl *sdl;
207	char	*buf, *lim, *next;
208	size_t	needed;
209	int mib[6];
210
211	if( ifname!=NULL ) {
212	    strncpy(name,ifname,sizeof(name)-1);
213	    if( name[0]==0 )
214		all=1;
215	} else
216	    all = 1;
217
218	mib[0] = CTL_NET;
219	mib[1] = PF_ROUTE;
220	mib[2] = 0;
221	mib[3] = AF_IPX;
222	mib[4] = NET_RT_IFLIST;
223	mib[5] = 0;
224
225	if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
226		return(1);
227	if ((buf = malloc(needed)) == NULL)
228		return(1);
229	if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
230		free(buf);
231		return(1);
232	}
233	lim = buf + needed;
234
235	next = buf;
236	while (next < lim) {
237		ifm = (struct if_msghdr *)next;
238		if (ifm->ifm_type == RTM_IFINFO) {
239			sdl = (struct sockaddr_dl *)(ifm + 1);
240			flags = ifm->ifm_flags;
241		} else {
242			fprintf(stderr, "if_ipxfind: out of sync parsing NET_RT_IFLIST\n");
243			fprintf(stderr, "expected %d, got %d\n", RTM_IFINFO, ifm->ifm_type);
244			fprintf(stderr, "msglen = %d\n", ifm->ifm_msglen);
245			fprintf(stderr, "buf:%p, next:%p, lim:%p\n", buf, next, lim);
246			free(buf);
247			return(1);
248		}
249
250		next += ifm->ifm_msglen;
251		ifam = NULL;
252		addrcount = 0;
253		while (next < lim) {
254			nextifm = (struct if_msghdr *)next;
255			if (nextifm->ifm_type != RTM_NEWADDR)
256				break;
257			if (ifam == NULL)
258				ifam = (struct ifa_msghdr *)nextifm;
259			addrcount++;
260			next += nextifm->ifm_msglen;
261		}
262
263		if (all) {
264			if ((flags & IFF_UP) == 0)
265				continue; /* not up */
266			strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
267			name[sdl->sdl_nlen] = '\0';
268		} else {
269			if (strlen(name) != sdl->sdl_nlen)
270				continue; /* not same len */
271			if (strncmp(name, sdl->sdl_data, sdl->sdl_nlen) != 0)
272				continue; /* not same name */
273		}
274
275		foundit=if_ipxscan(addrcount, sdl, ifm, ifam, addr);
276		if( foundit ) {
277			if( ifname!=NULL && ifname[0]==0) {
278			    strncpy(ifname,sdl->sdl_data, sdl->sdl_nlen);
279			    ifname[sdl->sdl_nlen]=0;
280			}
281			break;
282		}
283	}
284	free(buf);
285
286	return foundit ? 0:1;
287}
288
289
290int
291if_ipxscan(addrcount, sdl, ifm, ifam, addr)
292	int addrcount;
293	struct	sockaddr_dl *sdl;
294	struct if_msghdr *ifm;
295	struct ifa_msghdr *ifam;
296	struct ipx_addr *addr;
297{
298	struct	rt_addrinfo info;
299	struct sockaddr_ipx *sipx;
300	int s;
301
302	if ((s = socket(AF_IPX, SOCK_DGRAM, 0)) < 0) {
303		perror("ifconfig: socket");
304		return 0;
305	}
306
307	while (addrcount > 0) {
308		info.rti_addrs = ifam->ifam_addrs;
309		/* Expand the compacted addresses */
310		rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam, &info);
311		addrcount--;
312		ifam = (struct ifa_msghdr *)((char *)ifam + ifam->ifam_msglen);
313		if (info.rti_info[RTAX_IFA]->sa_family == AF_IPX) {
314			sipx = (struct sockaddr_ipx *)info.rti_info[RTAX_IFA];
315			if( ipx_nullnet(sipx->sipx_addr) ) continue;
316			if( ipx_nullnet(*addr) ||
317			    ipx_neteq(sipx->sipx_addr,*addr) ) {
318			    *addr=sipx->sipx_addr;
319			    close(s);
320			    return(1);
321			}
322		}
323	}
324	close(s);
325	return(0);
326}
327/*
328 * Expand the compacted form of addresses as returned via the
329 * configuration read via sysctl().
330 */
331
332#define ROUNDUP(a) \
333	((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
334#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
335
336static void
337rt_xaddrs(cp, cplim, rtinfo)
338	caddr_t cp, cplim;
339	struct rt_addrinfo *rtinfo;
340{
341	struct sockaddr *sa;
342	int i;
343
344	memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
345	for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
346		if ((rtinfo->rti_addrs & (1 << i)) == 0)
347			continue;
348		rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
349		ADVANCE(cp, sa);
350	}
351}
352
353