Commit | Line | Data |
---|---|---|
5ff67f98 | 1 | /* |
7b3729cc | 2 | * Copyright (c) 1983, 1989 The Regents of the University of California. |
fca7493d KB |
3 | * All rights reserved. |
4 | * | |
5 | * Redistribution and use in source and binary forms are permitted | |
6 | * provided that the above copyright notice and this paragraph are | |
7 | * duplicated in all such forms and that any documentation, | |
8 | * advertising materials, and other materials related to such | |
9 | * distribution and use acknowledge that the software was developed | |
10 | * by the University of California, Berkeley. The name of the | |
11 | * University may not be used to endorse or promote products derived | |
12 | * from this software without specific prior written permission. | |
13 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
14 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
15 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
5ff67f98 DF |
16 | */ |
17 | ||
18 | #ifndef lint | |
19 | char copyright[] = | |
7d5f817b | 20 | "@(#) Copyright (c) 1983 The Regents of the University of California.\n\ |
5ff67f98 | 21 | All rights reserved.\n"; |
fca7493d | 22 | #endif /* not lint */ |
5ff67f98 | 23 | |
4bec325f | 24 | #ifndef lint |
caf9a58c | 25 | static char sccsid[] = "@(#)route.c 5.21 (Berkeley) %G%"; |
fca7493d | 26 | #endif /* not lint */ |
4bec325f | 27 | |
7d5f817b | 28 | #include <paths.h> |
a2280e64 | 29 | #include <sys/param.h> |
4bec325f BJ |
30 | #include <sys/socket.h> |
31 | #include <sys/ioctl.h> | |
5c7868a2 | 32 | #include <sys/file.h> |
63c817da | 33 | #include <sys/mbuf.h> |
9eb5d442 | 34 | #include <sys/kinfo.h> |
94a2d2a7 | 35 | |
4bec325f | 36 | #include <net/route.h> |
31bd2db6 | 37 | #include <net/if_dl.h> |
94a2d2a7 | 38 | #include <netinet/in.h> |
eac76d14 | 39 | #include <netns/ns.h> |
7d5f817b | 40 | #include <netiso/iso.h> |
94a2d2a7 | 41 | |
5c7868a2 | 42 | #include <netdb.h> |
94a2d2a7 | 43 | #include <stdio.h> |
4bec325f BJ |
44 | #include <errno.h> |
45 | #include <ctype.h> | |
46 | ||
31bd2db6 KS |
47 | struct keytab { |
48 | char *kt_cp; | |
49 | int kt_i; | |
50 | } keywords[] = { | |
51 | #include "keywords.h" | |
52 | {0, 0} | |
53 | }; | |
54 | ||
57cacc90 | 55 | struct ortentry route; |
31bd2db6 | 56 | union sockunion { |
7d5f817b KS |
57 | struct sockaddr sa; |
58 | struct sockaddr_in sin; | |
59 | struct sockaddr_ns sns; | |
60 | struct sockaddr_iso siso; | |
31bd2db6 KS |
61 | struct sockaddr_dl sdl; |
62 | } so_dst, so_gate, so_mask, so_genmask, so_ifa, so_ifp, *so_addrs[] = | |
63 | { &so_dst, &so_gate, &so_mask, &so_genmask, &so_ifa, &so_ifp, 0}; | |
64 | typedef union sockunion *sup; | |
65 | int pid, rtm_addrs; | |
4bec325f | 66 | int s; |
31bd2db6 KS |
67 | int forcehost, forcenet, doflush, nflag, af, qflag, Cflag, keyword(); |
68 | int iflag, verbose; | |
b268b889 | 69 | struct sockaddr_in sin = { sizeof(sin), AF_INET }; |
379dcc38 | 70 | struct in_addr inet_makeaddr(); |
31bd2db6 KS |
71 | char *malloc(), *routename(), *netname(); |
72 | extern char *iso_ntoa(), *link_ntoa(); | |
b268b889 MK |
73 | #define kget(p, d) \ |
74 | (lseek(kmem, (off_t)(p), 0), read(kmem, (char *)&(d), sizeof (d))) | |
4bec325f | 75 | |
31bd2db6 KS |
76 | usage(cp) |
77 | char *cp; | |
78 | { | |
79 | fprintf(stderr, | |
80 | "usage: route [ -nqCv ] cmd [[ -<qualifers> ] args ]\n"); | |
81 | if (cp) fprintf(stderr, "(botched keyword: %s)\n", cp); | |
82 | ||
83 | exit(1); | |
84 | } | |
4bec325f BJ |
85 | main(argc, argv) |
86 | int argc; | |
87 | char *argv[]; | |
88 | { | |
89 | ||
b268b889 | 90 | char *argvp; |
4bec325f | 91 | if (argc < 2) |
31bd2db6 | 92 | usage((char *)0); |
4bec325f | 93 | argc--, argv++; |
b3933da8 | 94 | for (; argc > 0 && argv[0][0] == '-'; argc--, argv++) { |
b268b889 | 95 | for (argvp = argv[0]++; *argvp; argvp++) |
b3933da8 | 96 | switch (*argv[0]) { |
b3933da8 | 97 | case 'n': |
a2280e64 | 98 | nflag++; |
b3933da8 | 99 | break; |
ae578a19 KS |
100 | case 'q': |
101 | qflag++; | |
102 | break; | |
5c7868a2 KS |
103 | case 'C': |
104 | Cflag++; /* Use old ioctls */ | |
57cacc90 | 105 | break; |
7d5f817b KS |
106 | case 'v': |
107 | verbose++; | |
b3933da8 MK |
108 | } |
109 | } | |
5c7868a2 KS |
110 | pid = getpid(); |
111 | if (Cflag) | |
57cacc90 | 112 | s = socket(AF_INET, SOCK_RAW, 0); |
5c7868a2 KS |
113 | else |
114 | s = socket(PF_ROUTE, SOCK_RAW, 0); | |
57cacc90 KS |
115 | if (s < 0) { |
116 | perror("route: socket"); | |
117 | exit(1); | |
118 | } | |
31bd2db6 KS |
119 | if (argc > 0) switch (keyword(*argv)) { |
120 | case K_ADD: | |
121 | case K_CHANGE: | |
122 | case K_DELETE: | |
63c817da | 123 | newroute(argc, argv); |
31bd2db6 | 124 | case K_MONITOR: |
5c7868a2 | 125 | monitor(); |
31bd2db6 KS |
126 | case K_FLUSH: |
127 | flushroutes(argc, argv); | |
128 | } | |
129 | usage(*argv); | |
63c817da SL |
130 | } |
131 | ||
132 | /* | |
133 | * Purge all entries in the routing tables not | |
134 | * associated with network interfaces. | |
135 | */ | |
31bd2db6 KS |
136 | flushroutes(argc, argv) |
137 | char *argv[]; | |
b268b889 | 138 | { |
9eb5d442 KS |
139 | int bufsize, needed, seqno, rlen; |
140 | char *buf, *next, *lim; | |
141 | register struct rt_msghdr *rtm; | |
142 | struct { | |
143 | struct rt_msghdr m_rtm; | |
144 | union { | |
145 | char u_saddrs[200]; | |
146 | struct sockaddr u_sa; | |
147 | } m_u; | |
148 | } m; | |
149 | ||
31bd2db6 KS |
150 | if (argc > 1) { |
151 | argv++; | |
152 | if (argc == 1 && **argv == '-') switch (keyword(1 + *argv)) { | |
153 | case K_INET: af = AF_INET; break; | |
154 | case K_XNS: af = AF_NS; break; | |
155 | case K_LINK: af = AF_LINK; break; | |
156 | case K_OSI: af = AF_ISO; break; | |
157 | default: goto bad; | |
158 | } else | |
159 | bad: usage(*argv); | |
160 | } | |
9eb5d442 KS |
161 | if ((needed = getkerninfo(KINFO_RT_DUMP, 0, 0, 0)) < 0) |
162 | { perror("route-getkerninfo-estimate"); exit(1);} | |
163 | if ((buf = malloc(needed)) == 0) | |
164 | { printf("out of space\n");; exit(1);} | |
165 | if ((rlen = getkerninfo(KINFO_RT_DUMP, buf, &needed, 0)) < 0) | |
166 | { perror("actual retrieval of routing table"); exit(1);} | |
167 | lim = buf + rlen; | |
168 | for (next = buf; next < lim; next += rtm->rtm_msglen) { | |
169 | rtm = (struct rt_msghdr *)next; | |
170 | if ((rtm->rtm_flags & (RTF_GATEWAY|RTF_HOST)) == 0) | |
171 | continue; | |
31bd2db6 KS |
172 | if (af) { |
173 | struct sockaddr *sa = (struct sockaddr *)(rtm + 1); | |
174 | if (sa->sa_family != af) | |
175 | continue; | |
176 | } | |
9eb5d442 KS |
177 | rtm->rtm_type = RTM_DELETE; |
178 | rtm->rtm_seq = seqno; | |
179 | if ((rlen = write(s, next, rtm->rtm_msglen)) < 0) { | |
180 | perror("writing to routing socket"); | |
181 | printf("got only %d for rlen\n", rlen); | |
31bd2db6 | 182 | break; |
b268b889 | 183 | } |
9eb5d442 KS |
184 | again: |
185 | if ((rlen = read(s, (char *)&m, sizeof (m))) < 0) { | |
186 | perror("reading from routing socket"); | |
187 | printf("got only %d for rlen\n", rlen); | |
31bd2db6 | 188 | break; |
9eb5d442 KS |
189 | } |
190 | if ((m.m_rtm.rtm_pid != pid) || (m.m_rtm.rtm_seq != seqno)) { | |
191 | printf("Got response for somebody else's request"); | |
192 | goto again; | |
193 | } | |
194 | seqno++; | |
195 | if (qflag) | |
196 | continue; | |
197 | if (verbose) { | |
31bd2db6 | 198 | print_rtmsg(rtm, rlen); |
9eb5d442 KS |
199 | } else { |
200 | struct sockaddr *sa = &m.m_u.u_sa; | |
201 | printf("%-20.20s ", (rtm->rtm_flags & RTF_HOST) ? | |
202 | routename(sa) : netname(sa)); | |
203 | sa = (struct sockaddr *)(sa->sa_len + (char *)sa); | |
204 | printf("%-20.20s ", routename(sa)); | |
205 | printf("done\n"); | |
57cacc90 KS |
206 | } |
207 | } | |
31bd2db6 | 208 | exit(0); |
57cacc90 | 209 | } |
9eb5d442 | 210 | |
63c817da | 211 | char * |
eac76d14 MK |
212 | routename(sa) |
213 | struct sockaddr *sa; | |
63c817da | 214 | { |
a2280e64 | 215 | register char *cp; |
63c817da | 216 | static char line[50]; |
6edb1941 | 217 | struct hostent *hp; |
a2280e64 MK |
218 | static char domain[MAXHOSTNAMELEN + 1]; |
219 | static int first = 1; | |
220 | char *index(); | |
eac76d14 | 221 | char *ns_print(); |
63c817da | 222 | |
a2280e64 MK |
223 | if (first) { |
224 | first = 0; | |
225 | if (gethostname(domain, MAXHOSTNAMELEN) == 0 && | |
226 | (cp = index(domain, '.'))) | |
227 | (void) strcpy(domain, cp + 1); | |
228 | else | |
229 | domain[0] = 0; | |
230 | } | |
eac76d14 MK |
231 | switch (sa->sa_family) { |
232 | ||
233 | case AF_INET: | |
234 | { struct in_addr in; | |
235 | in = ((struct sockaddr_in *)sa)->sin_addr; | |
236 | ||
237 | cp = 0; | |
238 | if (in.s_addr == INADDR_ANY) | |
239 | cp = "default"; | |
240 | if (cp == 0 && !nflag) { | |
241 | hp = gethostbyaddr(&in, sizeof (struct in_addr), | |
242 | AF_INET); | |
243 | if (hp) { | |
244 | if ((cp = index(hp->h_name, '.')) && | |
245 | !strcmp(cp + 1, domain)) | |
246 | *cp = 0; | |
247 | cp = hp->h_name; | |
248 | } | |
a2280e64 | 249 | } |
eac76d14 MK |
250 | if (cp) |
251 | strcpy(line, cp); | |
252 | else { | |
a2280e64 | 253 | #define C(x) ((x) & 0xff) |
eac76d14 | 254 | in.s_addr = ntohl(in.s_addr); |
9bd38ba8 | 255 | (void)sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
256 | C(in.s_addr >> 16), C(in.s_addr >> 8), C(in.s_addr)); |
257 | } | |
258 | break; | |
259 | } | |
260 | ||
261 | case AF_NS: | |
262 | return (ns_print((struct sockaddr_ns *)sa)); | |
263 | ||
31bd2db6 KS |
264 | case AF_LINK: |
265 | return (link_ntoa((struct sockaddr_dl *)sa)); | |
266 | ||
267 | case AF_ISO: | |
268 | (void) sprintf(line, "iso %s", | |
269 | iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); | |
270 | break; | |
271 | ||
eac76d14 MK |
272 | default: |
273 | { u_short *s = (u_short *)sa->sa_data; | |
31bd2db6 KS |
274 | u_short *slim = s + ((sa->sa_len + 1)>>1); |
275 | char *cp = line + sprintf(line, "(%d)", sa->sa_family); | |
276 | int n; | |
eac76d14 | 277 | |
31bd2db6 KS |
278 | while (s < slim) { |
279 | n = sprintf(cp, " %x", *s); | |
280 | s++; cp += n; | |
281 | } | |
eac76d14 MK |
282 | break; |
283 | } | |
a2280e64 MK |
284 | } |
285 | return (line); | |
286 | } | |
287 | ||
288 | /* | |
289 | * Return the name of the network whose address is given. | |
290 | * The address is assumed to be that of a net or subnet, not a host. | |
291 | */ | |
292 | char * | |
eac76d14 MK |
293 | netname(sa) |
294 | struct sockaddr *sa; | |
a2280e64 MK |
295 | { |
296 | char *cp = 0; | |
297 | static char line[50]; | |
298 | struct netent *np = 0; | |
299 | u_long net, mask; | |
e2fe1192 | 300 | register u_long i; |
a2280e64 | 301 | int subnetshift; |
a48a48cc | 302 | char *ns_print(); |
a2280e64 | 303 | |
eac76d14 MK |
304 | switch (sa->sa_family) { |
305 | ||
306 | case AF_INET: | |
307 | { struct in_addr in; | |
308 | in = ((struct sockaddr_in *)sa)->sin_addr; | |
309 | ||
e2fe1192 | 310 | i = in.s_addr = ntohl(in.s_addr); |
eac76d14 MK |
311 | if (in.s_addr == 0) |
312 | cp = "default"; | |
313 | else if (!nflag) { | |
314 | if (IN_CLASSA(i)) { | |
315 | mask = IN_CLASSA_NET; | |
316 | subnetshift = 8; | |
317 | } else if (IN_CLASSB(i)) { | |
318 | mask = IN_CLASSB_NET; | |
319 | subnetshift = 8; | |
320 | } else { | |
321 | mask = IN_CLASSC_NET; | |
322 | subnetshift = 4; | |
323 | } | |
324 | /* | |
325 | * If there are more bits than the standard mask | |
326 | * would suggest, subnets must be in use. | |
327 | * Guess at the subnet mask, assuming reasonable | |
328 | * width subnet fields. | |
329 | */ | |
330 | while (in.s_addr &~ mask) | |
331 | mask = (long)mask >> subnetshift; | |
332 | net = in.s_addr & mask; | |
333 | while ((mask & 1) == 0) | |
334 | mask >>= 1, net >>= 1; | |
335 | np = getnetbyaddr(net, AF_INET); | |
336 | if (np) | |
337 | cp = np->n_name; | |
a2280e64 | 338 | } |
eac76d14 MK |
339 | if (cp) |
340 | strcpy(line, cp); | |
341 | else if ((in.s_addr & 0xffffff) == 0) | |
9bd38ba8 | 342 | (void)sprintf(line, "%u", C(in.s_addr >> 24)); |
eac76d14 | 343 | else if ((in.s_addr & 0xffff) == 0) |
9bd38ba8 | 344 | (void)sprintf(line, "%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
345 | C(in.s_addr >> 16)); |
346 | else if ((in.s_addr & 0xff) == 0) | |
9bd38ba8 | 347 | (void)sprintf(line, "%u.%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
348 | C(in.s_addr >> 16), C(in.s_addr >> 8)); |
349 | else | |
9bd38ba8 | 350 | (void)sprintf(line, "%u.%u.%u.%u", C(in.s_addr >> 24), |
eac76d14 MK |
351 | C(in.s_addr >> 16), C(in.s_addr >> 8), |
352 | C(in.s_addr)); | |
353 | break; | |
354 | } | |
355 | ||
356 | case AF_NS: | |
357 | return (ns_print((struct sockaddr_ns *)sa)); | |
358 | break; | |
359 | ||
31bd2db6 KS |
360 | case AF_LINK: |
361 | return (link_ntoa((struct sockaddr_dl *)sa)); | |
362 | ||
363 | case AF_ISO: | |
364 | (void) sprintf(line, "iso %s", | |
365 | iso_ntoa(&((struct sockaddr_iso *)sa)->siso_addr)); | |
366 | break; | |
367 | ||
eac76d14 MK |
368 | default: |
369 | { u_short *s = (u_short *)sa->sa_data; | |
31bd2db6 KS |
370 | u_short *slim = s + ((sa->sa_len + 1)>>1); |
371 | char *cp = line + sprintf(line, "af %d:", sa->sa_family); | |
372 | int n; | |
eac76d14 | 373 | |
31bd2db6 KS |
374 | while (s < slim) { |
375 | n = sprintf(cp, " %x", *s); | |
376 | s++; cp += n; | |
377 | } | |
eac76d14 MK |
378 | break; |
379 | } | |
63c817da SL |
380 | } |
381 | return (line); | |
4bec325f BJ |
382 | } |
383 | ||
384 | newroute(argc, argv) | |
385 | int argc; | |
31bd2db6 | 386 | register char **argv; |
4bec325f BJ |
387 | { |
388 | struct sockaddr_in *sin; | |
57cacc90 | 389 | char *cmd, *dest, *gateway, *mask; |
31bd2db6 KS |
390 | int ishost, metric = 0, ret, attempts, oerrno, flags = 0, next; |
391 | struct hostent *hp = 0; | |
792b0612 | 392 | extern int errno; |
4bec325f | 393 | |
183ef13b | 394 | cmd = argv[0]; |
31bd2db6 KS |
395 | while (--argc > 0) { |
396 | if (**(++argv)== '-') { | |
397 | switch(keyword(1 + *argv)) { | |
398 | case K_LINK: | |
399 | af = AF_LINK; | |
400 | break; | |
401 | case K_OSI: | |
402 | af = AF_ISO; | |
403 | break; | |
404 | case K_INET: | |
405 | af = AF_INET; | |
406 | break; | |
407 | case K_XNS: | |
408 | af = AF_NS; | |
409 | break; | |
410 | case K_IFACE: | |
411 | iflag++; | |
412 | break; | |
413 | case K_HOST: | |
414 | forcehost++; | |
415 | break; | |
416 | case K_NET: | |
417 | forcenet++; | |
418 | break; | |
419 | case K_NETMASK: | |
420 | argc--; | |
421 | (void) getaddr(RTA_NETMASK, *++argv, 0); | |
422 | break; | |
423 | case K_GENMASK: | |
424 | argc--; | |
425 | (void) getaddr(RTA_GENMASK, *++argv, 0); | |
426 | break; | |
427 | default: | |
428 | usage(1+*argv); | |
429 | } | |
430 | } else { | |
431 | if ((rtm_addrs & RTA_DST) == 0) { | |
432 | dest = *argv; | |
433 | ishost = getaddr(RTA_DST, *argv, &hp); | |
434 | } else if ((rtm_addrs & RTA_GATEWAY) == 0) { | |
435 | gateway = *argv; | |
436 | (void) getaddr(RTA_GATEWAY, *argv, &hp); | |
437 | } else { | |
438 | int ret = atoi(*argv); | |
439 | if (ret == 0) { | |
440 | printf("%s,%s", "old usage of trailing 0", | |
441 | "assuming route to if\n"); | |
442 | iflag = 1; | |
443 | continue; | |
444 | } else if (ret > 0 && ret < 10) { | |
445 | printf("old usage of trailing digit, "); | |
446 | printf("assuming route via gateway\n"); | |
447 | iflag = 0; | |
448 | continue; | |
449 | } | |
450 | (void) getaddr(RTA_NETMASK, *argv, 0); | |
451 | } | |
452 | } | |
453 | } | |
b3933da8 MK |
454 | if (forcehost) |
455 | ishost = 1; | |
456 | if (forcenet) | |
457 | ishost = 0; | |
7d5f817b | 458 | flags = RTF_UP; |
6edb1941 | 459 | if (ishost) |
7d5f817b | 460 | flags |= RTF_HOST; |
57cacc90 | 461 | if (iflag == 0) |
7d5f817b | 462 | flags |= RTF_GATEWAY; |
792b0612 MK |
463 | for (attempts = 1; ; attempts++) { |
464 | errno = 0; | |
31bd2db6 | 465 | if (Cflag && (af == AF_INET || af == AF_NS)) { |
7d5f817b KS |
466 | route.rt_flags = flags; |
467 | route.rt_dst = so_dst.sa; | |
468 | route.rt_gateway = so_gate.sa; | |
57cacc90 KS |
469 | if ((ret = ioctl(s, *cmd == 'a' ? SIOCADDRT : SIOCDELRT, |
470 | (caddr_t)&route)) == 0) | |
471 | break; | |
472 | } else { | |
31bd2db6 | 473 | if ((ret = rtmsg(*cmd, flags)) == 0); |
57cacc90 KS |
474 | break; |
475 | } | |
792b0612 MK |
476 | if (errno != ENETUNREACH && errno != ESRCH) |
477 | break; | |
478 | if (hp && hp->h_addr_list[1]) { | |
479 | hp->h_addr_list++; | |
7d5f817b | 480 | bcopy(hp->h_addr_list[0], (caddr_t)&so_dst.sin.sin_addr, |
792b0612 MK |
481 | hp->h_length); |
482 | } else | |
483 | break; | |
484 | } | |
485 | oerrno = errno; | |
486 | printf("%s %s %s: gateway %s", cmd, ishost? "host" : "net", | |
487 | dest, gateway); | |
488 | if (attempts > 1 && ret == 0) | |
489 | printf(" (%s)", | |
490 | inet_ntoa(((struct sockaddr_in *)&route.rt_gateway)->sin_addr)); | |
491 | if (ret == 0) | |
492 | printf("\n"); | |
493 | else { | |
494 | printf(": "); | |
495 | fflush(stdout); | |
496 | errno = oerrno; | |
31bd2db6 | 497 | error(""); |
792b0612 | 498 | } |
31bd2db6 | 499 | exit(0); |
4bec325f BJ |
500 | } |
501 | ||
502 | error(cmd) | |
503 | char *cmd; | |
504 | { | |
a48a48cc | 505 | extern int errno; |
4bec325f | 506 | |
a48a48cc KB |
507 | switch(errno) { |
508 | case ESRCH: | |
ae578a19 | 509 | printf("not in table\n"); |
a48a48cc KB |
510 | break; |
511 | case EBUSY: | |
ae578a19 | 512 | printf("entry in use\n"); |
a48a48cc KB |
513 | break; |
514 | case ENOBUFS: | |
ae578a19 | 515 | printf("routing table overflow\n"); |
a48a48cc KB |
516 | break; |
517 | default: | |
57cacc90 | 518 | printf("ioctl returns %d\n", errno); |
4bec325f | 519 | perror(cmd); |
a48a48cc | 520 | } |
ae578a19 KS |
521 | fflush(stdout); |
522 | errno = 0; | |
4bec325f BJ |
523 | } |
524 | ||
55e026df MK |
525 | char * |
526 | savestr(s) | |
527 | char *s; | |
528 | { | |
529 | char *sav; | |
530 | ||
531 | sav = malloc(strlen(s) + 1); | |
532 | if (sav == NULL) { | |
533 | fprintf("route: out of memory\n"); | |
534 | exit(1); | |
535 | } | |
536 | strcpy(sav, s); | |
537 | return (sav); | |
538 | } | |
539 | ||
31bd2db6 | 540 | inet_makenetandmask(net, sin) |
5c7868a2 | 541 | u_long net; |
31bd2db6 | 542 | register struct sockaddr_in *sin; |
57cacc90 KS |
543 | { |
544 | u_long addr; | |
5c7868a2 | 545 | u_long mask = 0; |
31bd2db6 | 546 | register char *cp; |
57cacc90 | 547 | |
31bd2db6 | 548 | rtm_addrs |= RTA_NETMASK; |
57cacc90 | 549 | if (net == 0) |
5c7868a2 | 550 | mask = addr = 0; |
57cacc90 KS |
551 | else if (net < 128) { |
552 | addr = net << IN_CLASSA_NSHIFT; | |
5c7868a2 | 553 | mask = IN_CLASSA_NET; |
57cacc90 KS |
554 | } else if (net < 65536) { |
555 | addr = net << IN_CLASSB_NSHIFT; | |
5c7868a2 | 556 | mask = IN_CLASSB_NET; |
57cacc90 KS |
557 | } else if (net < 16777216L) { |
558 | addr = net << IN_CLASSC_NSHIFT; | |
5c7868a2 | 559 | mask = IN_CLASSC_NET; |
57cacc90 KS |
560 | } else { |
561 | addr = net; | |
562 | if ((addr & IN_CLASSA_HOST) == 0) | |
5c7868a2 | 563 | mask = IN_CLASSA_NET; |
57cacc90 | 564 | else if ((addr & IN_CLASSB_HOST) == 0) |
5c7868a2 | 565 | mask = IN_CLASSB_NET; |
57cacc90 | 566 | else if ((addr & IN_CLASSC_HOST) == 0) |
5c7868a2 | 567 | mask = IN_CLASSC_NET; |
57cacc90 | 568 | else |
5c7868a2 KS |
569 | mask = -1; |
570 | } | |
31bd2db6 KS |
571 | sin->sin_addr.s_addr = htonl(addr); |
572 | sin = &so_mask.sin; | |
573 | sin->sin_addr.s_addr = htonl(mask); | |
574 | sin->sin_len = 0; | |
575 | sin->sin_family = 0; | |
576 | cp = (char *)(1 + &(sin->sin_addr)); | |
577 | while (*--cp == 0 && cp > (char *)sin) | |
578 | ; | |
579 | sin->sin_len = 1 + cp - (char *)sin; | |
57cacc90 KS |
580 | } |
581 | ||
6edb1941 MK |
582 | /* |
583 | * Interpret an argument as a network address of some kind, | |
584 | * returning 1 if a host address, 0 if a network address. | |
585 | */ | |
31bd2db6 | 586 | getaddr(which, s, hpp) |
4bec325f | 587 | char *s; |
792b0612 | 588 | struct hostent **hpp; |
4bec325f | 589 | { |
31bd2db6 | 590 | register union sockunion *su; |
b268b889 | 591 | struct ns_addr ns_addr(); |
31bd2db6 | 592 | struct iso_addr iso_addr(); |
accbc2e3 | 593 | struct hostent *hp; |
379dcc38 | 594 | struct netent *np; |
31bd2db6 | 595 | u_long val; |
accbc2e3 | 596 | |
31bd2db6 KS |
597 | if (af == 0) |
598 | af = AF_INET; | |
599 | switch (which) { | |
600 | case RTA_DST: su = so_addrs[0]; break; | |
601 | case RTA_GATEWAY: su = so_addrs[1]; break; | |
602 | case RTA_NETMASK: su = so_addrs[2]; break; | |
603 | case RTA_GENMASK: su = so_addrs[3]; break; | |
604 | default: usage("Internal Error"); /*NOTREACHED*/ | |
605 | } | |
606 | rtm_addrs |= which; | |
607 | su->sa.sa_family = af; | |
7d5f817b | 608 | if (strcmp(s, "default") == 0) { |
31bd2db6 KS |
609 | su->sa.sa_len = 0; |
610 | if (which == RTA_DST) | |
611 | getaddr(RTA_NETMASK, s, 0); | |
612 | return 0; | |
7d5f817b | 613 | } |
31bd2db6 | 614 | if (af == AF_NS) |
b268b889 | 615 | goto do_xns; |
31bd2db6 | 616 | if (af == AF_OSI) |
7d5f817b | 617 | goto do_osi; |
31bd2db6 KS |
618 | if (af == AF_LINK) |
619 | goto do_link; | |
620 | su->sin.sin_len = sizeof(su->sin); | |
621 | if (hpp == 0) hpp = &hp; | |
7d5f817b | 622 | *hpp = 0; |
31bd2db6 KS |
623 | if (forcenet == 0 && (val = inet_addr(s)) != -1) { |
624 | su->sin.sin_addr.s_addr = val; | |
625 | if (inet_lnaof(su->sin.sin_addr) != INADDR_ANY) | |
626 | return (1); | |
627 | else { | |
628 | val = ntohl(val); | |
629 | out: if (which == RTA_DST) | |
630 | inet_makenetandmask(val, &su->sin); | |
631 | return (0); | |
a2280e64 | 632 | } |
792b0612 MK |
633 | } |
634 | val = inet_network(s); | |
635 | if (val != -1) { | |
5c7868a2 | 636 | goto out; |
792b0612 | 637 | } |
379dcc38 SL |
638 | np = getnetbyname(s); |
639 | if (np) { | |
5c7868a2 | 640 | val = np->n_net; |
5c7868a2 | 641 | goto out; |
379dcc38 | 642 | } |
78160c55 MK |
643 | hp = gethostbyname(s); |
644 | if (hp) { | |
792b0612 | 645 | *hpp = hp; |
31bd2db6 KS |
646 | su->sin.sin_family = hp->h_addrtype; |
647 | bcopy(hp->h_addr, &su->sin.sin_addr, hp->h_length); | |
57cacc90 | 648 | return (1); |
78160c55 | 649 | } |
379dcc38 SL |
650 | fprintf(stderr, "%s: bad value\n", s); |
651 | exit(1); | |
b268b889 | 652 | do_xns: |
31bd2db6 | 653 | su->sns.sns_len = sizeof (su->sns); |
7d5f817b KS |
654 | if (val == 0) |
655 | return(0); | |
31bd2db6 | 656 | if (which == RTA_DST) { |
57cacc90 | 657 | extern short ns_bh[3]; |
31bd2db6 KS |
658 | struct sockaddr_ns *sms = &(so_mask.sns); |
659 | bzero((char *)sms, sizeof(*sms)); | |
57cacc90 KS |
660 | sms->sns_family = 0; |
661 | sms->sns_len = 6; | |
662 | sms->sns_addr.x_net = *(union ns_net *)ns_bh; | |
31bd2db6 | 663 | rtm_addrs |= RTA_NETMASK; |
57cacc90 | 664 | } |
31bd2db6 KS |
665 | su->sns.sns_addr = ns_addr(s); |
666 | return (!ns_nullhost(su->sns.sns_addr)); | |
7d5f817b | 667 | do_osi: |
31bd2db6 KS |
668 | su->siso.siso_len = sizeof(su->siso); |
669 | su->siso.siso_addr = iso_addr(s); | |
670 | return (1); | |
671 | do_link: | |
672 | su->sdl.sdl_len = sizeof(su->sdl); | |
673 | link_addr(s, &su->sdl); | |
7d5f817b | 674 | return (1); |
183ef13b | 675 | } |
eac76d14 MK |
676 | |
677 | short ns_nullh[] = {0,0,0}; | |
678 | short ns_bh[] = {-1,-1,-1}; | |
679 | ||
680 | char * | |
681 | ns_print(sns) | |
682 | struct sockaddr_ns *sns; | |
683 | { | |
684 | struct ns_addr work; | |
685 | union { union ns_net net_e; u_long long_e; } net; | |
686 | u_short port; | |
687 | static char mybuf[50], cport[10], chost[25]; | |
688 | char *host = ""; | |
689 | register char *p; register u_char *q; u_char *q_lim; | |
690 | ||
691 | work = sns->sns_addr; | |
692 | port = ntohs(work.x_port); | |
693 | work.x_port = 0; | |
694 | net.net_e = work.x_net; | |
695 | if (ns_nullhost(work) && net.long_e == 0) { | |
696 | if (port ) { | |
9bd38ba8 | 697 | (void)sprintf(mybuf, "*.%xH", port); |
eac76d14 MK |
698 | upHex(mybuf); |
699 | } else | |
9bd38ba8 | 700 | (void)sprintf(mybuf, "*.*"); |
eac76d14 MK |
701 | return (mybuf); |
702 | } | |
703 | ||
704 | if (bcmp(ns_bh, work.x_host.c_host, 6) == 0) { | |
705 | host = "any"; | |
706 | } else if (bcmp(ns_nullh, work.x_host.c_host, 6) == 0) { | |
707 | host = "*"; | |
708 | } else { | |
709 | q = work.x_host.c_host; | |
9bd38ba8 | 710 | (void)sprintf(chost, "%02x%02x%02x%02x%02x%02xH", |
eac76d14 MK |
711 | q[0], q[1], q[2], q[3], q[4], q[5]); |
712 | for (p = chost; *p == '0' && p < chost + 12; p++); | |
713 | host = p; | |
714 | } | |
715 | if (port) | |
9bd38ba8 | 716 | (void)sprintf(cport, ".%xH", htons(port)); |
eac76d14 MK |
717 | else |
718 | *cport = 0; | |
719 | ||
9bd38ba8 | 720 | (void)sprintf(mybuf,"%xH.%s%s", ntohl(net.long_e), host, cport); |
eac76d14 MK |
721 | upHex(mybuf); |
722 | return(mybuf); | |
723 | } | |
724 | ||
725 | upHex(p0) | |
726 | char *p0; | |
727 | { | |
728 | register char *p = p0; | |
729 | for (; *p; p++) switch (*p) { | |
730 | ||
731 | case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': | |
732 | *p += ('A' - 'a'); | |
733 | } | |
734 | } | |
57cacc90 KS |
735 | |
736 | monitor() | |
737 | { | |
738 | int n; | |
739 | char msg[2048]; | |
9eb5d442 | 740 | verbose = 1; |
57cacc90 KS |
741 | for(;;) { |
742 | n = read(s, msg, 2048); | |
743 | printf("got message of size %d\n", n); | |
31bd2db6 | 744 | print_rtmsg((struct rt_msghdr *)msg); |
57cacc90 KS |
745 | } |
746 | } | |
747 | ||
748 | struct { | |
749 | struct rt_msghdr m_rtm; | |
31bd2db6 | 750 | char m_space[512]; |
57cacc90 KS |
751 | } m_rtmsg; |
752 | ||
31bd2db6 | 753 | rtmsg(cmd, flags) |
57cacc90 KS |
754 | { |
755 | static int seq; | |
756 | int len = sizeof(m_rtmsg), rlen; | |
757 | extern int errno; | |
31bd2db6 KS |
758 | register char *cp = m_rtmsg.m_space; |
759 | register int l; | |
57cacc90 KS |
760 | |
761 | errno = 0; | |
762 | bzero((char *)&m_rtmsg, sizeof(m_rtmsg)); | |
763 | if (cmd == 'a') | |
764 | cmd = RTM_ADD; | |
765 | else if (cmd == 'c') | |
766 | cmd = RTM_CHANGE; | |
767 | else | |
768 | cmd = RTM_DELETE; | |
769 | m_rtmsg.m_rtm.rtm_flags = flags; | |
a387100b | 770 | m_rtmsg.m_rtm.rtm_version = RTM_VERSION; |
57cacc90 | 771 | m_rtmsg.m_rtm.rtm_seq = ++seq; |
31bd2db6 KS |
772 | m_rtmsg.m_rtm.rtm_addrs = rtm_addrs; |
773 | ||
774 | #define ROUND(a) (1 + (((a) - 1) | (sizeof(long) - 1))) | |
775 | #define NEXTADDR(w, u) { if (rtm_addrs & (w)) {l = (u).sa.sa_len;\ | |
776 | if(verbose)sodump(&(u),"u");if(l == 0) l = sizeof(int); l = ROUND(l);\ | |
777 | bcopy((char *)&(u), cp, l); cp += l;}} | |
778 | ||
779 | NEXTADDR(RTA_DST, so_dst); | |
780 | NEXTADDR(RTA_GATEWAY, so_gate); | |
781 | NEXTADDR(RTA_NETMASK, so_mask); | |
782 | NEXTADDR(RTA_GENMASK, so_genmask); | |
783 | m_rtmsg.m_rtm.rtm_msglen = cp - (char *)&m_rtmsg; | |
57cacc90 | 784 | m_rtmsg.m_rtm.rtm_type = cmd; |
31bd2db6 KS |
785 | if (verbose) |
786 | print_rtmsg(&m_rtmsg.m_rtm, len); | |
57cacc90 KS |
787 | if ((rlen = write(s, (char *)&m_rtmsg, len)) < 0) { |
788 | perror("writing to routing socket"); | |
789 | printf("got only %d for rlen\n", rlen); | |
7d5f817b | 790 | return (-1); |
57cacc90 KS |
791 | } |
792 | again: | |
793 | if ((rlen = read(s, (char *)&m_rtmsg, len)) < 0) { | |
794 | perror("reading from routing socket"); | |
795 | printf("got only %d for rlen\n", rlen); | |
7d5f817b | 796 | return (-1); |
57cacc90 KS |
797 | } |
798 | if ((m_rtmsg.m_rtm.rtm_pid != pid) || | |
799 | (m_rtmsg.m_rtm.rtm_seq != seq)) { | |
800 | printf("Got response for somebody else's request"); | |
801 | goto again; | |
802 | } | |
803 | if (qflag == 0) | |
804 | print_rtmsg( &m_rtmsg.m_rtm, rlen); | |
805 | if ((m_rtmsg.m_rtm.rtm_flags & RTF_DONE) == 0) { | |
806 | errno = m_rtmsg.m_rtm.rtm_errno; | |
807 | perror("response from routing socket turned down"); | |
808 | return (-1); | |
809 | } | |
810 | return (0); | |
811 | } | |
812 | ||
813 | char *msgtypes[] = { | |
814 | "", | |
815 | "RTM_ADD: Add Route", | |
816 | "RTM_DELETE: Delete Route", | |
817 | "RTM_CHANGE: Change Metrics or flags", | |
818 | "RTM_GET: Report Metrics", | |
819 | "RTM_LOSING: Kernel Suspects Partitioning", | |
820 | "RTM_REDIRECT: Told to use different route", | |
821 | "RTM_MISS: Lookup failed on this address", | |
822 | "RTM_LOCK: fix specified metrics", | |
823 | "RTM_OLDADD: caused by SIOCADDRT", | |
824 | "RTM_OLDDEL: caused by SIOCDELRT", | |
825 | 0, }; | |
826 | ||
827 | char metricnames[] = | |
828 | "\010rttvar\7rtt\6ssthresh\7sendpipe\4recvpipe\3expire\2hopcount\1mtu"; | |
829 | ||
830 | #define ROUNDUP(a) ((char *)(1 + (((((int)a)) - 1) | (sizeof(long) - 1)))) | |
831 | ||
832 | print_rtmsg(rtm, n) | |
833 | register struct rt_msghdr *rtm; | |
834 | { | |
835 | char *cp; | |
836 | register struct sockaddr *sa; | |
a387100b | 837 | int i; |
57cacc90 | 838 | |
7d5f817b KS |
839 | if (verbose == 0) |
840 | return; | |
a387100b | 841 | if (rtm->rtm_version != RTM_VERSION) { |
5c7868a2 | 842 | printf("routing message version %d not understood\n", |
57cacc90 | 843 | rtm->rtm_version); |
5c7868a2 KS |
844 | } else { |
845 | printf("%s\npid: %d, len %d, seq %d, errno %d, flags:", | |
846 | msgtypes[rtm->rtm_type], rtm->rtm_pid, rtm->rtm_msglen, | |
847 | rtm->rtm_seq, rtm->rtm_errno); | |
848 | bprintf(stdout, rtm->rtm_flags, | |
849 | "\1UP\2GATEWAY\3HOST\5DYNAMIC\6MODIFIED\7DONE\010MASK_PRESENT"); | |
a387100b | 850 | printf("\nlocks: "); bprintf(stdout, rtm->rtm_rmx.rmx_locks, metricnames); |
5c7868a2 | 851 | printf(" inits: "); bprintf(stdout, rtm->rtm_inits, metricnames); |
31bd2db6 | 852 | printf("\nsockaddrs: "); |
a387100b KS |
853 | bprintf(stdout, rtm->rtm_addrs, |
854 | "\1DST\2GATEWAY\3NETMASK\4GENMASK\5IFP\6IFA\7AUTHOR"); | |
855 | putchar('\n'); | |
5c7868a2 | 856 | cp = ((char *)(rtm + 1)); |
a387100b KS |
857 | if (rtm->rtm_addrs) |
858 | for (i = 1; i; i <<= 1) | |
859 | if (i & rtm->rtm_addrs) { | |
860 | sa = (struct sockaddr *)cp; | |
861 | printf(" %s", routename(sa)); | |
862 | cp = ROUNDUP(cp + sa->sa_len); | |
863 | } | |
5c7868a2 | 864 | putchar('\n'); |
57cacc90 | 865 | } |
57cacc90 KS |
866 | fflush(stdout); |
867 | } | |
868 | ||
869 | bprintf(fp, b, s) | |
870 | register FILE *fp; | |
871 | register int b; | |
872 | register u_char *s; | |
873 | { | |
874 | register int i; | |
875 | int gotsome = 0; | |
876 | ||
877 | if (b == 0) | |
878 | return; | |
879 | while (i = *s++) { | |
880 | if (b & (1 << (i-1))) { | |
881 | if (gotsome == 0) i = '<'; else i = ','; | |
882 | putc(i, fp); | |
883 | gotsome = 1; | |
884 | for (; (i = *s) > 32; s++) | |
885 | putc(i, fp); | |
886 | } else | |
887 | while (*s > 32) | |
888 | s++; | |
889 | } | |
890 | if (gotsome) | |
891 | putc('>', fp); | |
892 | } | |
31bd2db6 KS |
893 | int |
894 | keyword(cp) | |
895 | char *cp; | |
896 | { | |
897 | register struct keytab *kt = keywords; | |
898 | while (kt->kt_cp && strcmp(kt->kt_cp, cp)) | |
899 | kt++; | |
900 | return kt->kt_i; | |
901 | } | |
902 | ||
903 | sodump(su, which) | |
904 | register union sockunion *su; | |
905 | char *which; | |
906 | { | |
907 | switch (su->sa.sa_family) { | |
908 | case AF_LINK: | |
909 | printf("%s: link %s; ", which, link_ntoa(&su->sdl)); | |
910 | break; | |
911 | case AF_ISO: | |
912 | printf("%s: iso %s; ", which, iso_ntoa(&su->siso.siso_addr)); | |
913 | break; | |
914 | case AF_INET: | |
915 | printf("%s: inet %s; ", which, inet_ntoa(su->sin.sin_addr)); | |
916 | break; | |
917 | case AF_NS: | |
918 | printf("%s: xns %s; ", which, ns_ntoa(&su->sns.sns_addr)); | |
919 | break; | |
920 | } | |
921 | fflush(stdout); | |
922 | } | |
923 | ||
924 | /* | |
925 | * Copyright (c) 1990 Regents of the University of California. | |
926 | * All rights reserved. | |
927 | * | |
928 | * Redistribution and use in source and binary forms are permitted | |
929 | * provided that the above copyright notice and this paragraph are | |
930 | * duplicated in all such forms and that any documentation, | |
931 | * advertising materials, and other materials related to such | |
932 | * distribution and use acknowledge that the software was developed | |
933 | * by the University of California, Berkeley. The name of the | |
934 | * University may not be used to endorse or promote products derived | |
935 | * from this software without specific prior written permission. | |
936 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR | |
937 | * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED | |
938 | * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | |
939 | */ | |
940 | ||
941 | #if defined(LIBC_SCCS) && !defined(lint) | |
942 | static char sccsid[] = "@(#)linkaddr.c 5.1 (Berkeley) 4/1/90"; | |
943 | #endif /* LIBC_SCCS and not lint */ | |
944 | ||
945 | #ifndef AF_LINK | |
946 | #include <sys/param.h> | |
947 | #include <sys/socket.h> | |
948 | #include <net/if_dl.h> | |
949 | #endif | |
950 | /* States*/ | |
951 | #define NAMING 0 | |
952 | #define GOTONE 1 | |
953 | #define GOTTWO 2 | |
954 | #define RESET 3 | |
955 | /* Inputs */ | |
956 | #define DIGIT (4*0) | |
957 | #define END (4*1) | |
958 | #define DELIM (4*2) | |
959 | #define LETTER (4*3) | |
960 | ||
961 | link_addr(addr, sdl) | |
962 | register char *addr; | |
963 | register struct sockaddr_dl *sdl; | |
964 | { | |
965 | register char *cp = sdl->sdl_data; | |
966 | char *cplim = sdl->sdl_len + (char *)sdl; | |
967 | register int byte = 0, state = NAMING, new; | |
968 | ||
969 | bzero((char *)&sdl->sdl_family, sdl->sdl_len - 1); | |
970 | sdl->sdl_family = AF_LINK; | |
971 | do { | |
972 | state &= ~LETTER; | |
973 | if ((*addr >= '0') && (*addr <= '9')) { | |
974 | new = *addr - '0'; | |
975 | } else if ((*addr >= 'a') && (*addr <= 'f')) { | |
976 | new = *addr - 'a' + 10; | |
977 | } else if ((*addr >= 'A') && (*addr <= 'F')) { | |
978 | new = *addr - 'A' + 10; | |
979 | } else if (*addr == 0) { | |
980 | state |= END; | |
981 | } else if (state == NAMING && | |
982 | (((*addr >= 'A') && (*addr <= 'Z')) || | |
983 | ((*addr >= 'a') && (*addr <= 'z')))) | |
984 | state |= LETTER; | |
985 | else | |
986 | state |= DELIM; | |
987 | addr++; | |
988 | switch (state /* | INPUT */) { | |
989 | case NAMING | DIGIT: | |
990 | case NAMING | LETTER: | |
991 | *cp++ = addr[-1]; continue; | |
992 | case NAMING | DELIM: | |
993 | state = RESET; sdl->sdl_nlen = cp - sdl->sdl_data; continue; | |
994 | case GOTTWO | DIGIT: | |
995 | *cp++ = byte; /*FALLTHROUGH*/ | |
996 | case RESET | DIGIT: | |
997 | state = GOTONE; byte = new; continue; | |
998 | case GOTONE | DIGIT: | |
999 | state = GOTTWO; byte = new + (byte << 4); continue; | |
1000 | default: /* | DELIM */ | |
1001 | state = RESET; *cp++ = byte; byte = 0; continue; | |
1002 | case GOTONE | END: | |
1003 | case GOTTWO | END: | |
1004 | *cp++ = byte; /* FALLTHROUGH */ | |
1005 | case RESET | END: | |
1006 | break; | |
1007 | } | |
1008 | break; | |
1009 | } while (cp < cplim); | |
1010 | sdl->sdl_alen = cp - LLADDR(sdl); | |
1011 | new = cp - (char *)sdl; | |
1012 | if (new > sizeof(*sdl)) | |
1013 | sdl->sdl_len = new; | |
1014 | return; | |
1015 | } | |
1016 | static char hexlist[] = "0123456789abcdef"; | |
1017 | ||
1018 | char * | |
1019 | link_ntoa(sdl) | |
1020 | register struct sockaddr_dl *sdl; | |
1021 | { | |
1022 | static char obuf[64]; | |
1023 | register char *out = obuf; | |
1024 | register int i; | |
1025 | register u_char *in = (u_char *)LLADDR(sdl); | |
1026 | u_char *inlim = in + sdl->sdl_nlen; | |
1027 | int firsttime = 1; | |
1028 | ||
1029 | if (sdl->sdl_nlen) { | |
1030 | bcopy(sdl->sdl_data, obuf, sdl->sdl_nlen); | |
1031 | out += sdl->sdl_nlen; | |
1032 | *out++ = ':'; | |
1033 | } | |
1034 | while (in < inlim) { | |
1035 | if (firsttime) firsttime = 0; else *out++ = '.'; | |
1036 | i = *in++; | |
1037 | if (i > 0xf) { | |
1038 | out[1] = hexlist[i & 0xf]; | |
1039 | i >>= 4; | |
1040 | out[0] = hexlist[i]; | |
1041 | out += 2; | |
1042 | } else | |
1043 | *out++ = hexlist[i]; | |
1044 | } | |
1045 | *out = 0; | |
1046 | return(obuf); | |
1047 | } |