1 /* $NetBSD: netstat.c,v 1.29 2013/11/24 13:13:12 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 1980, 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 #if 0 35 static char sccsid[] = "@(#)netstat.c 8.1 (Berkeley) 6/6/93"; 36 #endif 37 __RCSID("$NetBSD: netstat.c,v 1.29 2013/11/24 13:13:12 mlelstv Exp $"); 38 #endif /* not lint */ 39 40 /* 41 * netstat 42 */ 43 #include <sys/param.h> 44 #include <sys/socketvar.h> 45 #include <sys/mbuf.h> 46 #include <sys/protosw.h> 47 48 #include <netinet/in.h> 49 50 #include <arpa/inet.h> 51 #include <net/route.h> 52 53 #include <netinet/in_systm.h> 54 #include <netinet/ip.h> 55 #include <netinet/in_pcb.h> 56 #include <netinet/ip_icmp.h> 57 #include <netinet/icmp_var.h> 58 #include <netinet/ip_var.h> 59 #ifdef INET6 60 #include <netinet/ip6.h> 61 #include <netinet6/in6_pcb.h> 62 #endif 63 #include <netinet/tcp.h> 64 #include <netinet/tcpip.h> 65 #include <netinet/tcp_seq.h> 66 #define TCPSTATES 67 #include <netinet/tcp_fsm.h> 68 #include <netinet/tcp_timer.h> 69 #include <netinet/tcp_var.h> 70 #include <netinet/tcp_debug.h> 71 #include <netinet/udp.h> 72 #include <netinet/udp_var.h> 73 74 #include <netdb.h> 75 #include <stdlib.h> 76 #include <string.h> 77 78 #include "systat.h" 79 #include "extern.h" 80 81 static void fetchnetstat4(void *, int); 82 static void enter(struct inpcb *, struct socket *, int, const char *); 83 static const char *inetname(struct in_addr); 84 static void inetprint(struct in_addr *, int, const char *); 85 #ifdef INET6 86 static void fetchnetstat6(void *, int); 87 static void enter6(struct in6pcb *, struct socket *, int, const char *); 88 static const char *inet6name(struct in6_addr *); 89 static void inet6print(struct in6_addr *, int, const char *); 90 #endif 91 92 #define streq(a,b) (strcmp(a,b)==0) 93 94 struct netinfo { 95 struct netinfo *ni_forw, *ni_prev; 96 int ni_family; 97 short ni_line; /* line on screen */ 98 short ni_seen; /* 0 when not present in list */ 99 short ni_flags; 100 #define NIF_LACHG 0x1 /* local address changed */ 101 #define NIF_FACHG 0x2 /* foreign address changed */ 102 short ni_state; /* tcp state */ 103 const char *ni_proto; /* protocol */ 104 struct in_addr ni_laddr; /* local address */ 105 #ifdef INET6 106 struct in6_addr ni_laddr6; /* local address */ 107 #endif 108 long ni_lport; /* local port */ 109 struct in_addr ni_faddr; /* foreign address */ 110 #ifdef INET6 111 struct in6_addr ni_faddr6; /* foreign address */ 112 #endif 113 long ni_fport; /* foreign port */ 114 long ni_rcvcc; /* rcv buffer character count */ 115 long ni_sndcc; /* snd buffer character count */ 116 }; 117 118 static struct { 119 struct netinfo *ni_forw, *ni_prev; 120 } netcb; 121 122 struct netinfo *nhead; 123 124 static int aflag = 0; 125 int nflag = 0; 126 static int lastrow = 1; 127 128 WINDOW * 129 opennetstat(void) 130 { 131 132 sethostent(1); 133 setnetent(1); 134 return (subwin(stdscr, -1, 0, 5, 0)); 135 } 136 137 void 138 closenetstat(WINDOW *w) 139 { 140 struct netinfo *p; 141 142 endhostent(); 143 endnetent(); 144 p = netcb.ni_forw; 145 while (p != nhead) { 146 if (p->ni_line != -1) 147 lastrow--; 148 p->ni_line = -1; 149 p = p->ni_forw; 150 } 151 if (w != NULL) { 152 wclear(w); 153 wrefresh(w); 154 delwin(w); 155 } 156 } 157 158 static struct nlist namelist[] = { 159 #define X_TCBTABLE 0 160 { .n_name = "_tcbtable" }, 161 #define X_UDBTABLE 1 162 { .n_name = "_udbtable" }, 163 { .n_name = NULL }, 164 }; 165 166 int 167 initnetstat(void) 168 { 169 int n; 170 171 n = kvm_nlist(kd, namelist); 172 if (n < 0) { 173 nlisterr(namelist); 174 return(0); 175 } else if (n == sizeof(namelist) / sizeof(namelist[0]) - 1) { 176 error("No symbols in namelist"); 177 return(0); 178 } 179 180 nhead = (struct netinfo *)(void *)&netcb; 181 182 netcb.ni_forw = netcb.ni_prev = nhead; 183 protos = TCP|UDP; 184 return(1); 185 } 186 187 void 188 fetchnetstat(void) 189 { 190 struct netinfo *p; 191 192 if (namelist[X_TCBTABLE].n_value == 0) 193 return; 194 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) 195 p->ni_seen = 0; 196 197 if ((protos & (TCP | UDP)) == 0) { 198 error("No protocols to display"); 199 return; 200 } 201 if ((protos & TCP) && namelist[X_TCBTABLE].n_type) 202 fetchnetstat4(NPTR(X_TCBTABLE), 1); 203 if ((protos & UDP) && namelist[X_UDBTABLE].n_type) 204 fetchnetstat4(NPTR(X_UDBTABLE), 0); 205 #ifdef INET6 206 if ((protos & TCP) && namelist[X_TCBTABLE].n_type) 207 fetchnetstat6(NPTR(X_TCBTABLE), 1); 208 if ((protos & UDP) && namelist[X_UDBTABLE].n_type) 209 fetchnetstat6(NPTR(X_UDBTABLE), 0); 210 #endif 211 } 212 213 static void 214 fetchnetstat4(void *off, int istcp) 215 { 216 struct inpcbtable pcbtable; 217 struct inpcb_hdr **pprev, *next; 218 struct netinfo *p; 219 struct inpcb inpcb, *inpcbp; 220 struct socket sockb; 221 struct tcpcb tcpcb; 222 223 KREAD(off, &pcbtable, sizeof pcbtable); 224 pprev = &((struct inpcbtable *)off)->inpt_queue.tqh_first; 225 next = TAILQ_FIRST(&pcbtable.inpt_queue); 226 while (next != TAILQ_END(&pcbtable.inpt_queue)) { 227 inpcbp = (struct inpcb *)next; 228 KREAD(inpcbp, &inpcb, sizeof (inpcb)); 229 if (inpcb.inp_queue.tqe_prev != pprev) { 230 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) 231 p->ni_seen = 1; 232 error("Kernel state in transition"); 233 return; 234 } 235 pprev = &next->inph_queue.tqe_next; 236 next = inpcb.inp_queue.tqe_next; 237 238 if (inpcb.inp_af != AF_INET) 239 continue; 240 if (!aflag && inet_lnaof(inpcb.inp_laddr) == INADDR_ANY) 241 continue; 242 if (nhosts && !checkhost(&inpcb)) 243 continue; 244 if (nports && !checkport(&inpcb)) 245 continue; 246 KREAD(inpcb.inp_socket, &sockb, sizeof (sockb)); 247 if (istcp) { 248 KREAD(inpcb.inp_ppcb, &tcpcb, sizeof (tcpcb)); 249 enter(&inpcb, &sockb, tcpcb.t_state, "tcp"); 250 } else 251 enter(&inpcb, &sockb, 0, "udp"); 252 } 253 } 254 255 #ifdef INET6 256 static void 257 fetchnetstat6(void *off, int istcp) 258 { 259 struct inpcbtable pcbtable; 260 struct inpcb_hdr **pprev, *next; 261 struct netinfo *p; 262 struct socket sockb; 263 struct tcpcb tcpcb; 264 struct in6pcb in6pcb, *in6pcbp; 265 266 KREAD(off, &pcbtable, sizeof pcbtable); 267 pprev = &((struct inpcbtable *)off)->inpt_queue.tqh_first; 268 next = TAILQ_FIRST(&pcbtable.inpt_queue); 269 while (next != TAILQ_END(&pcbtable.inpt_queue)) { 270 in6pcbp = (struct in6pcb *)next; 271 KREAD(in6pcbp, &in6pcb, sizeof (in6pcb)); 272 if (in6pcb.in6p_queue.tqe_prev != pprev) { 273 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) 274 p->ni_seen = 1; 275 error("Kernel state in transition"); 276 return; 277 } 278 pprev = &next->inph_queue.tqe_next; 279 next = in6pcb.in6p_queue.tqe_next; 280 281 if (in6pcb.in6p_af != AF_INET6) 282 continue; 283 if (!aflag && IN6_IS_ADDR_UNSPECIFIED(&in6pcb.in6p_laddr)) 284 continue; 285 if (nhosts && !checkhost6(&in6pcb)) 286 continue; 287 if (nports && !checkport6(&in6pcb)) 288 continue; 289 KREAD(in6pcb.in6p_socket, &sockb, sizeof (sockb)); 290 if (istcp) { 291 KREAD(in6pcb.in6p_ppcb, &tcpcb, sizeof (tcpcb)); 292 enter6(&in6pcb, &sockb, tcpcb.t_state, "tcp"); 293 } else 294 enter6(&in6pcb, &sockb, 0, "udp"); 295 } 296 } 297 #endif /*INET6*/ 298 299 static void 300 enter(struct inpcb *inp, struct socket *so, int state, const char *proto) 301 { 302 struct netinfo *p; 303 304 /* 305 * Only take exact matches, any sockets with 306 * previously unbound addresses will be deleted 307 * below in the display routine because they 308 * will appear as ``not seen'' in the kernel 309 * data structures. 310 */ 311 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) { 312 if (p->ni_family != AF_INET) 313 continue; 314 if (!streq(proto, p->ni_proto)) 315 continue; 316 if (p->ni_lport != inp->inp_lport || 317 p->ni_laddr.s_addr != inp->inp_laddr.s_addr) 318 continue; 319 if (p->ni_faddr.s_addr == inp->inp_faddr.s_addr && 320 p->ni_fport == inp->inp_fport) 321 break; 322 } 323 if (p == nhead) { 324 if ((p = malloc(sizeof(*p))) == NULL) { 325 error("Out of memory"); 326 return; 327 } 328 p->ni_prev = nhead; 329 p->ni_forw = netcb.ni_forw; 330 netcb.ni_forw->ni_prev = p; 331 netcb.ni_forw = p; 332 p->ni_line = -1; 333 p->ni_laddr = inp->inp_laddr; 334 p->ni_lport = inp->inp_lport; 335 p->ni_faddr = inp->inp_faddr; 336 p->ni_fport = inp->inp_fport; 337 p->ni_proto = proto; 338 p->ni_flags = NIF_LACHG | NIF_FACHG; 339 p->ni_family = AF_INET; 340 } 341 p->ni_rcvcc = so->so_rcv.sb_cc; 342 p->ni_sndcc = so->so_snd.sb_cc; 343 p->ni_state = state; 344 p->ni_seen = 1; 345 } 346 347 #ifdef INET6 348 static void 349 enter6(struct in6pcb *in6p, struct socket *so, int state, const char *proto) 350 { 351 struct netinfo *p; 352 353 /* 354 * Only take exact matches, any sockets with 355 * previously unbound addresses will be deleted 356 * below in the display routine because they 357 * will appear as ``not seen'' in the kernel 358 * data structures. 359 */ 360 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) { 361 if (p->ni_family != AF_INET6) 362 continue; 363 if (!streq(proto, p->ni_proto)) 364 continue; 365 if (p->ni_lport != in6p->in6p_lport || 366 !IN6_ARE_ADDR_EQUAL(&p->ni_laddr6, &in6p->in6p_laddr)) 367 continue; 368 if (IN6_ARE_ADDR_EQUAL(&p->ni_faddr6, &in6p->in6p_faddr) && 369 p->ni_fport == in6p->in6p_fport) 370 break; 371 } 372 if (p == nhead) { 373 if ((p = malloc(sizeof(*p))) == NULL) { 374 error("Out of memory"); 375 return; 376 } 377 p->ni_prev = nhead; 378 p->ni_forw = netcb.ni_forw; 379 netcb.ni_forw->ni_prev = p; 380 netcb.ni_forw = p; 381 p->ni_line = -1; 382 p->ni_laddr6 = in6p->in6p_laddr; 383 p->ni_lport = in6p->in6p_lport; 384 p->ni_faddr6 = in6p->in6p_faddr; 385 p->ni_fport = in6p->in6p_fport; 386 p->ni_proto = proto; 387 p->ni_flags = NIF_LACHG | NIF_FACHG; 388 p->ni_family = AF_INET6; 389 } 390 p->ni_rcvcc = so->so_rcv.sb_cc; 391 p->ni_sndcc = so->so_snd.sb_cc; 392 p->ni_state = state; 393 p->ni_seen = 1; 394 } 395 #endif 396 397 /* column locations */ 398 #define LADDR 0 399 #define FADDR LADDR+23 400 #define PROTO FADDR+23 401 #define RCVCC PROTO+6 402 #define SNDCC RCVCC+7 403 #define STATE SNDCC+7 404 405 void 406 labelnetstat(void) 407 { 408 struct netinfo *p; 409 410 if (namelist[X_TCBTABLE].n_type == 0) 411 return; 412 wmove(wnd, 0, 0); wclrtobot(wnd); 413 mvwaddstr(wnd, 0, LADDR, "Local Address"); 414 mvwaddstr(wnd, 0, FADDR, "Foreign Address"); 415 mvwaddstr(wnd, 0, PROTO, "Proto"); 416 mvwaddstr(wnd, 0, RCVCC, "Recv-Q"); 417 mvwaddstr(wnd, 0, SNDCC, "Send-Q"); 418 mvwaddstr(wnd, 0, STATE, "(state)"); 419 420 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) { 421 if (p->ni_line == -1) 422 continue; 423 p->ni_flags |= NIF_LACHG | NIF_FACHG; 424 } 425 } 426 427 void 428 shownetstat(void) 429 { 430 struct netinfo *p, *q; 431 432 /* 433 * First, delete any connections that have gone 434 * away and adjust the position of connections 435 * below to reflect the deleted line. 436 */ 437 p = netcb.ni_forw; 438 while (p != nhead) { 439 if (p->ni_line == -1 || p->ni_seen) { 440 p = p->ni_forw; 441 continue; 442 } 443 wmove(wnd, p->ni_line, 0); 444 wdeleteln(wnd); 445 for (q = netcb.ni_forw; q != nhead; q = q->ni_forw) 446 if (q != p && q->ni_line > p->ni_line) { 447 q->ni_line--; 448 /* this shouldn't be necessary */ 449 q->ni_flags |= NIF_LACHG | NIF_FACHG; 450 } 451 lastrow--; 452 q = p->ni_forw; 453 p->ni_prev->ni_forw = p->ni_forw; 454 p->ni_forw->ni_prev = p->ni_prev; 455 free(p); 456 p = q; 457 } 458 /* 459 * Update existing connections and add new ones. 460 */ 461 for (p = netcb.ni_forw; p != nhead; p = p->ni_forw) { 462 if (p->ni_line == -1) { 463 /* 464 * Add a new entry if possible. 465 */ 466 if (lastrow > getmaxy(wnd)) 467 continue; 468 p->ni_line = lastrow++; 469 p->ni_flags |= NIF_LACHG | NIF_FACHG; 470 } 471 if (p->ni_flags & NIF_LACHG) { 472 wmove(wnd, p->ni_line, LADDR); 473 switch (p->ni_family) { 474 case AF_INET: 475 inetprint(&p->ni_laddr, p->ni_lport, 476 p->ni_proto); 477 break; 478 #ifdef INET6 479 case AF_INET6: 480 inet6print(&p->ni_laddr6, p->ni_lport, 481 p->ni_proto); 482 break; 483 #endif 484 } 485 p->ni_flags &= ~NIF_LACHG; 486 } 487 if (p->ni_flags & NIF_FACHG) { 488 wmove(wnd, p->ni_line, FADDR); 489 switch (p->ni_family) { 490 case AF_INET: 491 inetprint(&p->ni_faddr, p->ni_fport, 492 p->ni_proto); 493 break; 494 #ifdef INET6 495 case AF_INET6: 496 inet6print(&p->ni_faddr6, p->ni_fport, 497 p->ni_proto); 498 break; 499 #endif 500 } 501 p->ni_flags &= ~NIF_FACHG; 502 } 503 mvwaddstr(wnd, p->ni_line, PROTO, p->ni_proto); 504 #ifdef INET6 505 if (p->ni_family == AF_INET6) 506 waddstr(wnd, "6"); 507 #endif 508 mvwprintw(wnd, p->ni_line, RCVCC, "%6ld", p->ni_rcvcc); 509 mvwprintw(wnd, p->ni_line, SNDCC, "%6ld", p->ni_sndcc); 510 if (streq(p->ni_proto, "tcp")) { 511 if (p->ni_state < 0 || p->ni_state >= TCP_NSTATES) 512 mvwprintw(wnd, p->ni_line, STATE, "%d", 513 p->ni_state); 514 else 515 mvwaddstr(wnd, p->ni_line, STATE, 516 tcpstates[p->ni_state]); 517 } 518 wclrtoeol(wnd); 519 } 520 if (lastrow < getmaxy(wnd)) { 521 wmove(wnd, lastrow, 0); wclrtobot(wnd); 522 wmove(wnd, getmaxy(wnd), 0); wdeleteln(wnd); /* XXX */ 523 } 524 } 525 526 /* 527 * Pretty print an Internet address (net address + port). 528 * If the nflag was specified, use numbers instead of names. 529 */ 530 static void 531 inetprint(struct in_addr *in, int port, const char *proto) 532 { 533 struct servent *sp = 0; 534 char line[80], *cp; 535 536 (void)snprintf(line, sizeof line, "%.*s.", 16, inetname(*in)); 537 cp = strchr(line, '\0'); 538 if (!nflag && port) 539 sp = getservbyport(port, proto); 540 if (sp || port == 0) 541 (void)snprintf(cp, line + sizeof line - cp, "%.8s", 542 sp ? sp->s_name : "*"); 543 else 544 (void)snprintf(cp, line + sizeof line - cp, "%d", 545 ntohs((u_short)port)); 546 /* pad to full column to clear any garbage */ 547 cp = strchr(line, '\0'); 548 while (cp - line < 22) 549 *cp++ = ' '; 550 *cp = '\0'; 551 waddstr(wnd, line); 552 } 553 554 #ifdef INET6 555 static void 556 inet6print(struct in6_addr *in6, int port, const char *proto) 557 { 558 struct servent *sp = 0; 559 char line[80], *cp; 560 561 (void)snprintf(line, sizeof line, "%.*s.", 16, inet6name(in6)); 562 cp = strchr(line, '\0'); 563 if (!nflag && port) 564 sp = getservbyport(port, proto); 565 if (sp || port == 0) 566 (void)snprintf(cp, line + sizeof line - cp, "%.8s", 567 sp ? sp->s_name : "*"); 568 else 569 (void)snprintf(cp, line + sizeof line - cp, "%d", 570 ntohs((u_short)port)); 571 /* pad to full column to clear any garbage */ 572 cp = strchr(line, '\0'); 573 while (cp - line < 22) 574 *cp++ = ' '; 575 *cp = '\0'; 576 waddstr(wnd, line); 577 } 578 #endif 579 580 /* 581 * Construct an Internet address representation. 582 * If the nflag has been supplied, give 583 * numeric value, otherwise try for symbolic name. 584 */ 585 static const char * 586 inetname(struct in_addr in) 587 { 588 char *cp = 0; 589 static char line[50]; 590 struct hostent *hp; 591 struct netent *np; 592 593 if (!nflag && in.s_addr != INADDR_ANY) { 594 int net = inet_netof(in); 595 int lna = inet_lnaof(in); 596 597 if (lna == INADDR_ANY) { 598 np = getnetbyaddr(net, AF_INET); 599 if (np) 600 cp = np->n_name; 601 } 602 if (cp == 0) { 603 hp = gethostbyaddr((char *)&in, sizeof (in), AF_INET); 604 if (hp) 605 cp = hp->h_name; 606 } 607 } 608 if (in.s_addr == INADDR_ANY) 609 strlcpy(line, "*", sizeof(line)); 610 else if (cp) 611 strlcpy(line, cp, sizeof(line)); 612 else { 613 in.s_addr = ntohl(in.s_addr); 614 #define C(x) ((x) & 0xff) 615 (void)snprintf(line, sizeof line, "%u.%u.%u.%u", 616 C(in.s_addr >> 24), C(in.s_addr >> 16), 617 C(in.s_addr >> 8), C(in.s_addr)); 618 #undef C 619 } 620 return (line); 621 } 622 623 #ifdef INET6 624 static const char * 625 inet6name(struct in6_addr *in6) 626 { 627 static char line[NI_MAXHOST]; 628 struct sockaddr_in6 sin6; 629 int flags; 630 631 if (nflag) 632 flags = NI_NUMERICHOST; 633 else 634 flags = 0; 635 if (IN6_IS_ADDR_UNSPECIFIED(in6)) 636 return "*"; 637 memset(&sin6, 0, sizeof(sin6)); 638 sin6.sin6_family = AF_INET6; 639 sin6.sin6_len = sizeof(struct sockaddr_in6); 640 sin6.sin6_addr = *in6; 641 if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, 642 line, sizeof(line), NULL, 0, flags) == 0) 643 return line; 644 return "?"; 645 } 646 #endif 647 648 /* please note: there are also some netstat commands in netcmds.c */ 649 650 void 651 netstat_all(char *args) 652 { 653 aflag = !aflag; 654 fetchnetstat(); 655 shownetstat(); 656 refresh(); 657 } 658 659 void 660 netstat_names(char *args) 661 { 662 663 if (nflag == 0) 664 return; 665 666 nflag = 0; 667 wclear(wnd); 668 labelnetstat(); 669 shownetstat(); 670 refresh(); 671 } 672 673 void 674 netstat_numbers(char *args) 675 { 676 677 if (nflag != 0) 678 return; 679 680 nflag = 1; 681 wclear(wnd); 682 labelnetstat(); 683 shownetstat(); 684 refresh(); 685 } 686