arp.c revision 1.52 1 /* $NetBSD: arp.c,v 1.52 2015/07/29 06:07:35 ozaki-r Exp $ */
2
3 /*
4 * Copyright (c) 1984, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Sun Microsystems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1984, 1993\
38 The Regents of the University of California. All rights reserved.");
39 #endif /* not lint */
40
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)arp.c 8.3 (Berkeley) 4/28/95";
44 #else
45 __RCSID("$NetBSD: arp.c,v 1.52 2015/07/29 06:07:35 ozaki-r Exp $");
46 #endif
47 #endif /* not lint */
48
49 /*
50 * arp - display, set, and delete arp table entries
51 */
52
53 #include <sys/param.h>
54 #include <sys/file.h>
55 #include <sys/socket.h>
56 #include <sys/sysctl.h>
57 #include <sys/ioctl.h>
58
59 #include <net/if.h>
60 #include <net/if_dl.h>
61 #include <net/if_ether.h>
62 #include <net/if_types.h>
63 #include <net/route.h>
64 #include <netinet/in.h>
65 #include <netinet/if_inarp.h>
66 #include <arpa/inet.h>
67
68 #include <err.h>
69 #include <errno.h>
70 #include <netdb.h>
71 #include <nlist.h>
72 #include <paths.h>
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76 #include <unistd.h>
77 #include <ifaddrs.h>
78
79 #include "prog_ops.h"
80
81 static int is_llinfo(const struct sockaddr_dl *, int);
82 static int delete(const char *, const char *);
83 static void dump(uint32_t);
84 static void delete_all(void);
85 static void sdl_print(const struct sockaddr_dl *);
86 static int getifname(u_int16_t, char *, size_t);
87 static int atosdl(const char *s, struct sockaddr_dl *sdl);
88 static int file(const char *);
89 static void get(const char *);
90 static int getinetaddr(const char *, struct in_addr *);
91 static void getsocket(void);
92 static int rtmsg(int);
93 static int set(int, char **);
94 static void usage(void) __dead;
95
96 static pid_t pid;
97 static int aflag, nflag, vflag;
98 static int s = -1;
99 static struct ifaddrs* ifaddrs = NULL;
100 static struct sockaddr_in so_mask = {
101 .sin_len = 8,
102 .sin_addr = {
103 .s_addr = 0xffffffff
104 }
105 };
106 static struct sockaddr_inarp blank_sin = {
107 .sin_len = sizeof(blank_sin),
108 .sin_family = AF_INET
109 };
110 static struct sockaddr_inarp sin_m;
111 static struct sockaddr_dl blank_sdl = {
112 .sdl_len = sizeof(blank_sdl),
113 .sdl_family = AF_LINK
114 };
115 static struct sockaddr_dl sdl_m;
116
117 static int expire_time, flags, export_only, doing_proxy, found_entry;
118 static struct {
119 struct rt_msghdr m_rtm;
120 char m_space[512];
121 } m_rtmsg;
122
123 int
124 main(int argc, char **argv)
125 {
126 int ch;
127 int op = 0;
128
129 setprogname(argv[0]);
130
131 while ((ch = getopt(argc, argv, "andsfv")) != -1)
132 switch((char)ch) {
133 case 'a':
134 aflag = 1;
135 break;
136 case 'd':
137 case 's':
138 case 'f':
139 if (op)
140 usage();
141 op = ch;
142 break;
143 case 'n':
144 nflag = 1;
145 break;
146 case 'v':
147 vflag = 1;
148 break;
149 default:
150 usage();
151 }
152 argc -= optind;
153 argv += optind;
154
155 if (!op && aflag)
156 op = 'a';
157
158 if (prog_init && prog_init() == -1)
159 err(1, "init failed");
160
161 pid = prog_getpid();
162
163 switch((char)op) {
164 case 'a':
165 dump(0);
166 break;
167 case 'd':
168 if (aflag && argc == 0)
169 delete_all();
170 else {
171 if (aflag || argc < 1 || argc > 2)
172 usage();
173 (void)delete(argv[0], argv[1]);
174 }
175 break;
176 case 's':
177 if (argc < 2 || argc > 7)
178 usage();
179 return (set(argc, argv) ? 1 : 0);
180 case 'f':
181 if (argc != 1)
182 usage();
183 return (file(argv[0]));
184 default:
185 if (argc != 1)
186 usage();
187 get(argv[0]);
188 break;
189 }
190 return (0);
191 }
192
193 /*
194 * Process a file to set standard arp entries
195 */
196 static int
197 file(const char *name)
198 {
199 char *line, *argv[5];
200 int i, retval;
201 FILE *fp;
202
203 if (!strcmp(name, "-")) {
204 fp = stdin;
205 } else {
206 fp = fopen(name, "r");
207 if (fp == NULL) {
208 err(1, "Cannot open %s", name);
209 }
210 }
211 retval = 0;
212 for (; (line = fparseln(fp, NULL, NULL, NULL, 0)) != NULL; free(line)) {
213 char **ap, *inputstring;
214
215 inputstring = line;
216 for (ap = argv; ap < &argv[sizeof(argv) / sizeof(argv[0])] &&
217 (*ap = stresep(&inputstring, " \t", '\\')) != NULL;) {
218 if (**ap != '\0')
219 ap++;
220 }
221 i = ap - argv;
222 if (i < 2) {
223 warnx("bad line: %s", line);
224 retval = 1;
225 continue;
226 }
227 if (set(i, argv))
228 retval = 1;
229 }
230 if (fp != stdin)
231 (void)fclose(fp);
232 return retval;
233 }
234
235 static void
236 getsocket(void)
237 {
238 if (s >= 0)
239 return;
240 s = prog_socket(PF_ROUTE, SOCK_RAW, 0);
241 if (s < 0)
242 err(1, "socket");
243 }
244
245 static int
246 getlink(const char *name, struct sockaddr_dl *sdl)
247 {
248 struct ifaddrs *ifap, *ifa;
249
250 if (getifaddrs(&ifap) != 0) {
251 warn("getifaddrs");
252 return 0;
253 }
254
255 for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
256 if (ifa->ifa_addr->sa_family != AF_LINK)
257 continue;
258 if (strcmp(ifa->ifa_name, name) != 0)
259 continue;
260 memcpy(sdl, ifa->ifa_addr, sizeof(*sdl));
261 freeifaddrs(ifap);
262 return 1;
263 }
264 freeifaddrs(ifap);
265 return 0;
266 }
267
268 /*
269 * Set an individual arp entry
270 */
271 static int
272 set(int argc, char **argv)
273 {
274 struct sockaddr_inarp *sina;
275 struct sockaddr_dl *sdl;
276 struct rt_msghdr *rtm;
277 char *host = argv[0], *eaddr;
278 int rval;
279
280 sina = &sin_m;
281 rtm = &(m_rtmsg.m_rtm);
282 eaddr = argv[1];
283
284 getsocket();
285 argc -= 2;
286 argv += 2;
287 sdl_m = blank_sdl; /* struct copy */
288 sin_m = blank_sin; /* struct copy */
289 if (getinetaddr(host, &sina->sin_addr) == -1)
290 return (1);
291 if (atosdl(eaddr, &sdl_m))
292 warnx("invalid link-level address '%s'", eaddr);
293 doing_proxy = flags = export_only = expire_time = 0;
294 for (; argc-- > 0; argv++) {
295 if (strncmp(argv[0], "temp", 4) == 0) {
296 struct timeval timev;
297 (void)gettimeofday(&timev, 0);
298 expire_time = timev.tv_sec + 20 * 60;
299 }
300 else if (strncmp(argv[0], "pub", 3) == 0) {
301 flags |= RTF_ANNOUNCE;
302 doing_proxy = SIN_PROXY;
303 if (argc && strncmp(argv[1], "pro", 3) == 0) {
304 export_only = 1;
305 argc--; argv++;
306 }
307 } else if (strncmp(argv[0], "trail", 5) == 0) {
308 warnx("%s: Sending trailers is no longer supported",
309 host);
310 } else if (strcmp(argv[0], "ifscope") == 0) {
311 if (argc == 0) {
312 warnx("missing interface for ifscope");
313 continue;
314 }
315 argc--;
316 argv++;
317 if (!getlink(argv[0], &sdl_m))
318 warnx("cannot get link address for %s", argv[0]);
319 }
320
321 }
322 if (memcmp(&sdl_m, &blank_sdl, sizeof(blank_sdl)))
323 goto out;
324 tryagain:
325 if (rtmsg(RTM_GET) < 0) {
326 warn("%s", host);
327 return (1);
328 }
329 sina = (struct sockaddr_inarp *)(void *)(rtm + 1);
330 sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(sina->sin_len) +
331 (char *)(void *)sina);
332 if (sina->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
333 if (is_llinfo(sdl, rtm->rtm_flags))
334 goto overwrite;
335 if (doing_proxy == 0) {
336 warnx("set: can only proxy for %s", host);
337 return (1);
338 }
339 if (sin_m.sin_other & SIN_PROXY) {
340 warnx("set: proxy entry exists for non 802 device");
341 return (1);
342 }
343 sin_m.sin_other = SIN_PROXY;
344 export_only = 1;
345 goto tryagain;
346 }
347 overwrite:
348 if (sdl->sdl_family != AF_LINK) {
349 warnx("cannot intuit interface index and type for %s",
350 host);
351 return (1);
352 }
353 sdl_m.sdl_type = sdl->sdl_type;
354 sdl_m.sdl_index = sdl->sdl_index;
355 out:
356 rval = rtmsg(RTM_ADD);
357 if (vflag)
358 (void)printf("%s (%s) added\n", host, eaddr);
359 return (rval);
360 }
361
362 /*
363 * Display an individual arp entry
364 */
365 static void
366 get(const char *host)
367 {
368 struct sockaddr_inarp *sina;
369
370 sina = &sin_m;
371 sin_m = blank_sin; /* struct copy */
372 if (getinetaddr(host, &sina->sin_addr) == -1)
373 exit(1);
374 dump(sina->sin_addr.s_addr);
375 if (found_entry == 0)
376 errx(1, "%s (%s) -- no entry", host, inet_ntoa(sina->sin_addr));
377 }
378
379
380 static int
381 is_llinfo(const struct sockaddr_dl *sdl, int rtflags)
382 {
383 if (sdl->sdl_family != AF_LINK ||
384 (rtflags & (RTF_LLINFO|RTF_GATEWAY)) != RTF_LLINFO)
385 return 0;
386
387 switch (sdl->sdl_type) {
388 case IFT_ETHER:
389 case IFT_FDDI:
390 case IFT_ISO88023:
391 case IFT_ISO88024:
392 case IFT_ISO88025:
393 case IFT_ARCNET:
394 return 1;
395 default:
396 return 0;
397 }
398 }
399
400 /*
401 * Delete an arp entry
402 */
403 int
404 delete(const char *host, const char *info)
405 {
406 struct sockaddr_inarp *sina;
407 struct rt_msghdr *rtm;
408 struct sockaddr_dl *sdl;
409
410 sina = &sin_m;
411 rtm = &m_rtmsg.m_rtm;
412
413 getsocket();
414 sin_m = blank_sin; /* struct copy */
415 if (info && strncmp(info, "pro", 3) == 0)
416 sina->sin_other = SIN_PROXY;
417 if (getinetaddr(host, &sina->sin_addr) == -1)
418 return (1);
419 tryagain:
420 if (rtmsg(RTM_GET) < 0) {
421 warn("%s", host);
422 return (1);
423 }
424 sina = (struct sockaddr_inarp *)(void *)(rtm + 1);
425 sdl = (struct sockaddr_dl *)(void *)(RT_ROUNDUP(sina->sin_len) +
426 (char *)(void *)sina);
427 if (sina->sin_addr.s_addr == sin_m.sin_addr.s_addr &&
428 is_llinfo(sdl, rtm->rtm_flags))
429 goto delete;
430 if (sin_m.sin_other & SIN_PROXY) {
431 warnx("delete: can't locate %s", host);
432 return (1);
433 } else {
434 sin_m.sin_other = SIN_PROXY;
435 goto tryagain;
436 }
437 delete:
438 if (sdl->sdl_family != AF_LINK) {
439 (void)warnx("cannot locate %s", host);
440 return (1);
441 }
442 if (rtmsg(RTM_DELETE))
443 return (1);
444 if (vflag)
445 (void)printf("%s (%s) deleted\n", host,
446 inet_ntoa(sina->sin_addr));
447 return (0);
448 }
449
450 /*
451 * Dump the entire arp table
452 */
453 void
454 dump(uint32_t addr)
455 {
456 int mib[6];
457 size_t needed;
458 char ifname[IFNAMSIZ];
459 char *lim, *buf, *next;
460 const char *host;
461 struct rt_msghdr *rtm;
462 struct sockaddr_inarp *sina;
463 struct sockaddr_dl *sdl;
464 struct hostent *hp;
465
466 mib[0] = CTL_NET;
467 mib[1] = PF_ROUTE;
468 mib[2] = 0;
469 mib[3] = AF_INET;
470 mib[4] = NET_RT_FLAGS;
471 mib[5] = RTF_LLINFO;
472 if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
473 err(1, "route-sysctl-estimate");
474 if (needed == 0)
475 return;
476 if ((buf = malloc(needed)) == NULL)
477 err(1, "malloc");
478 if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
479 err(1, "actual retrieval of routing table");
480 lim = buf + needed;
481 for (next = buf; next < lim; next += rtm->rtm_msglen) {
482 rtm = (struct rt_msghdr *)(void *)next;
483 sina = (struct sockaddr_inarp *)(void *)(rtm + 1);
484 sdl = (struct sockaddr_dl *)(void *)
485 (RT_ROUNDUP(sina->sin_len) + (char *)(void *)sina);
486 if (addr) {
487 if (addr != sina->sin_addr.s_addr)
488 continue;
489 found_entry = 1;
490 }
491 if (nflag == 0)
492 hp = gethostbyaddr((const char *)(void *)
493 &(sina->sin_addr),
494 sizeof sina->sin_addr, AF_INET);
495 else
496 hp = NULL;
497
498 host = hp ? hp->h_name : "?";
499
500 (void)printf("%s (%s) at ", host, inet_ntoa(sina->sin_addr));
501 if (sdl->sdl_alen)
502 sdl_print(sdl);
503 else
504 (void)printf("(incomplete)");
505
506 if (sdl->sdl_index) {
507 if (getifname(sdl->sdl_index, ifname, sizeof(ifname)) == 0)
508 (void)printf(" on %s", ifname);
509 }
510
511 if (rtm->rtm_rmx.rmx_expire == 0)
512 (void)printf(" permanent");
513 if (sina->sin_other & SIN_PROXY)
514 (void)printf(" published (proxy only)");
515 if (rtm->rtm_addrs & RTA_NETMASK) {
516 sina = (struct sockaddr_inarp *)(void *)
517 (RT_ROUNDUP(sdl->sdl_len) + (char *)(void *)sdl);
518 if (sina->sin_addr.s_addr == 0xffffffff)
519 (void)printf(" published");
520 if (sina->sin_len != 8)
521 (void)printf("(weird)");
522 }
523 (void)printf("\n");
524 }
525 free(buf);
526 }
527
528 /*
529 * Delete the entire arp table
530 */
531 void
532 delete_all(void)
533 {
534 int mib[6];
535 size_t needed;
536 char addr[sizeof("000.000.000.000\0")];
537 char *lim, *buf, *next;
538 struct rt_msghdr *rtm;
539 struct sockaddr_inarp *sina;
540
541 mib[0] = CTL_NET;
542 mib[1] = PF_ROUTE;
543 mib[2] = 0;
544 mib[3] = AF_INET;
545 mib[4] = NET_RT_FLAGS;
546 mib[5] = RTF_LLINFO;
547 if (prog_sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
548 err(1, "route-sysctl-estimate");
549 if (needed == 0)
550 return;
551 if ((buf = malloc(needed)) == NULL)
552 err(1, "malloc");
553 if (prog_sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
554 err(1, "actual retrieval of routing table");
555 lim = buf + needed;
556 for (next = buf; next < lim; next += rtm->rtm_msglen) {
557 rtm = (struct rt_msghdr *)(void *)next;
558 sina = (struct sockaddr_inarp *)(void *)(rtm + 1);
559 (void)snprintf(addr, sizeof(addr), "%s",
560 inet_ntoa(sina->sin_addr));
561 (void)delete(addr, NULL);
562 }
563 free(buf);
564 }
565
566 void
567 sdl_print(const struct sockaddr_dl *sdl)
568 {
569 char hbuf[NI_MAXHOST];
570
571 if (getnameinfo((const struct sockaddr *)(const void *)sdl,
572 (socklen_t)sdl->sdl_len,
573 hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
574 (void)printf("<invalid>");
575 else
576 (void)printf("%s", hbuf);
577 }
578
579 static int
580 atosdl(const char *ss, struct sockaddr_dl *sdl)
581 {
582 int i;
583 unsigned long b;
584 char *endp;
585 char *p;
586 char *t, *r;
587
588 p = LLADDR(sdl);
589 endp = ((char *)(void *)sdl) + sdl->sdl_len;
590 i = 0;
591
592 b = strtoul(ss, &t, 16);
593 if (b > 255 || t == ss)
594 return 1;
595
596 *p++ = (char)b;
597 ++i;
598 while ((p < endp) && (*t++ == ':')) {
599 b = strtoul(t, &r, 16);
600 if (b > 255 || r == t)
601 break;
602 *p++ = (char)b;
603 ++i;
604 t = r;
605 }
606 sdl->sdl_alen = i;
607
608 return 0;
609 }
610
611 static void
612 usage(void)
613 {
614 const char *progname;
615
616 progname = getprogname();
617 (void)fprintf(stderr, "Usage: %s [-n] hostname\n", progname);
618 (void)fprintf(stderr, " %s [-nv] -a\n", progname);
619 (void)fprintf(stderr, " %s [-v] -d [-a|hostname [pub [proxy]]]\n",
620 progname);
621 (void)fprintf(stderr, " %s -s hostname ether_addr [temp] [pub [proxy]]\n",
622 progname);
623 (void)fprintf(stderr, " %s -f filename\n", progname);
624 exit(1);
625 }
626
627 static int
628 rtmsg(int cmd)
629 {
630 static int seq;
631 struct rt_msghdr *rtm;
632 char *cp;
633 int l;
634
635 rtm = &m_rtmsg.m_rtm;
636 cp = m_rtmsg.m_space;
637 errno = 0;
638
639 if (cmd == RTM_DELETE)
640 goto doit;
641 (void)memset(&m_rtmsg, 0, sizeof(m_rtmsg));
642 rtm->rtm_flags = flags;
643 rtm->rtm_version = RTM_VERSION;
644
645 switch (cmd) {
646 default:
647 errx(1, "internal wrong cmd");
648 /*NOTREACHED*/
649 case RTM_ADD:
650 rtm->rtm_addrs |= RTA_GATEWAY;
651 rtm->rtm_rmx.rmx_expire = expire_time;
652 rtm->rtm_inits = RTV_EXPIRE;
653 rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
654 sin_m.sin_other = 0;
655 if (doing_proxy) {
656 if (export_only)
657 sin_m.sin_other = SIN_PROXY;
658 else {
659 rtm->rtm_addrs |= RTA_NETMASK;
660 rtm->rtm_flags &= ~RTF_HOST;
661 }
662 }
663 /* FALLTHROUGH */
664 case RTM_GET:
665 rtm->rtm_addrs |= RTA_DST;
666 }
667
668 #define NEXTADDR(w, s) \
669 if (rtm->rtm_addrs & (w)) { \
670 (void)memcpy(cp, &s, \
671 (size_t)((struct sockaddr *)(void *)&s)->sa_len); \
672 RT_ADVANCE(cp, ((struct sockaddr *)(void *)&s)); \
673 }
674
675 NEXTADDR(RTA_DST, sin_m);
676 NEXTADDR(RTA_GATEWAY, sdl_m);
677 NEXTADDR(RTA_NETMASK, so_mask);
678
679 rtm->rtm_msglen = cp - (char *)(void *)&m_rtmsg;
680 doit:
681 l = rtm->rtm_msglen;
682 rtm->rtm_seq = ++seq;
683 rtm->rtm_type = cmd;
684 if (prog_write(s, &m_rtmsg, (size_t)l) < 0) {
685 if (errno != ESRCH || cmd != RTM_DELETE) {
686 warn("writing to routing socket");
687 return (-1);
688 }
689 }
690 do {
691 l = prog_read(s, &m_rtmsg, sizeof(m_rtmsg));
692 } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
693 if (l < 0)
694 warn("read from routing socket");
695 return (0);
696 }
697
698 static int
699 getinetaddr(const char *host, struct in_addr *inap)
700 {
701 struct hostent *hp;
702
703 if (inet_aton(host, inap) == 1)
704 return (0);
705 if ((hp = gethostbyname(host)) == NULL) {
706 warnx("%s: %s", host, hstrerror(h_errno));
707 return (-1);
708 }
709 (void)memcpy(inap, hp->h_addr, sizeof(*inap));
710 return (0);
711 }
712
713 static int
714 getifname(u_int16_t ifindex, char *ifname, size_t l)
715 {
716 int i;
717 struct ifaddrs *addr;
718 const struct sockaddr_dl *sdl = NULL;
719
720 if (ifaddrs == NULL) {
721 i = getifaddrs(&ifaddrs);
722 if (i != 0)
723 err(1, "getifaddrs");
724 }
725
726 for (addr = ifaddrs; addr; addr = addr->ifa_next) {
727 if (addr->ifa_addr == NULL ||
728 addr->ifa_addr->sa_family != AF_LINK)
729 continue;
730
731 sdl = (const struct sockaddr_dl *)(void *)addr->ifa_addr;
732 if (sdl && sdl->sdl_index == ifindex) {
733 (void) strlcpy(ifname, addr->ifa_name, l);
734 return 0;
735 }
736 }
737
738 return -1;
739 }
740