1 /* 2 * Copyright (c) 2007-2012 Niels Provos and Nick Mathewson 3 * Copyright (c) 2002-2006 Niels Provos <provos (at) citi.umich.edu> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. The name of the author may not be used to endorse or promote products 15 * derived from this software without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/types.h> 30 31 #include "event2/event-config.h" 32 33 #ifdef _EVENT_HAVE_SYS_TIME_H 34 #include <sys/time.h> 35 #endif 36 37 #include <errno.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #ifdef _EVENT_HAVE_STDARG_H 42 #include <stdarg.h> 43 #endif 44 #ifdef _EVENT_HAVE_UNISTD_H 45 #include <unistd.h> 46 #endif 47 48 #ifdef WIN32 49 #include <winsock2.h> 50 #include <ws2tcpip.h> 51 #endif 52 53 #ifdef _EVENT_HAVE_SYS_SOCKET_H 54 #include <sys/socket.h> 55 #endif 56 #ifdef _EVENT_HAVE_NETINET_IN_H 57 #include <netinet/in.h> 58 #endif 59 #ifdef _EVENT_HAVE_NETINET_IN6_H 60 #include <netinet/in6.h> 61 #endif 62 63 #include "event2/util.h" 64 #include "event2/bufferevent.h" 65 #include "event2/buffer.h" 66 #include "event2/bufferevent_struct.h" 67 #include "event2/bufferevent_compat.h" 68 #include "event2/event.h" 69 #include "log-internal.h" 70 #include "mm-internal.h" 71 #include "bufferevent-internal.h" 72 #include "util-internal.h" 73 #ifdef WIN32 74 #include "iocp-internal.h" 75 #endif 76 77 /* prototypes */ 78 static int be_socket_enable(struct bufferevent *, short); 79 static int be_socket_disable(struct bufferevent *, short); 80 static void be_socket_destruct(struct bufferevent *); 81 static int be_socket_adj_timeouts(struct bufferevent *); 82 static int be_socket_flush(struct bufferevent *, short, enum bufferevent_flush_mode); 83 static int be_socket_ctrl(struct bufferevent *, enum bufferevent_ctrl_op, union bufferevent_ctrl_data *); 84 85 static void be_socket_setfd(struct bufferevent *, evutil_socket_t); 86 87 const struct bufferevent_ops bufferevent_ops_socket = { 88 "socket", 89 evutil_offsetof(struct bufferevent_private, bev), 90 be_socket_enable, 91 be_socket_disable, 92 be_socket_destruct, 93 be_socket_adj_timeouts, 94 be_socket_flush, 95 be_socket_ctrl, 96 }; 97 98 #define be_socket_add(ev, t) \ 99 _bufferevent_add_event((ev), (t)) 100 101 static void 102 bufferevent_socket_outbuf_cb(struct evbuffer *buf, 103 const struct evbuffer_cb_info *cbinfo, 104 void *arg) 105 { 106 struct bufferevent *bufev = arg; 107 struct bufferevent_private *bufev_p = 108 EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); 109 110 if (cbinfo->n_added && 111 (bufev->enabled & EV_WRITE) && 112 !event_pending(&bufev->ev_write, EV_WRITE, NULL) && 113 !bufev_p->write_suspended) { 114 /* Somebody added data to the buffer, and we would like to 115 * write, and we were not writing. So, start writing. */ 116 if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) == -1) { 117 /* Should we log this? */ 118 } 119 } 120 } 121 122 static void 123 bufferevent_readcb(evutil_socket_t fd, short event, void *arg) 124 { 125 struct bufferevent *bufev = arg; 126 struct bufferevent_private *bufev_p = 127 EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); 128 struct evbuffer *input; 129 int res = 0; 130 short what = BEV_EVENT_READING; 131 ev_ssize_t howmuch = -1, readmax=-1; 132 133 _bufferevent_incref_and_lock(bufev); 134 135 if (event == EV_TIMEOUT) { 136 /* Note that we only check for event==EV_TIMEOUT. If 137 * event==EV_TIMEOUT|EV_READ, we can safely ignore the 138 * timeout, since a read has occurred */ 139 what |= BEV_EVENT_TIMEOUT; 140 goto error; 141 } 142 143 input = bufev->input; 144 145 /* 146 * If we have a high watermark configured then we don't want to 147 * read more data than would make us reach the watermark. 148 */ 149 if (bufev->wm_read.high != 0) { 150 howmuch = bufev->wm_read.high - evbuffer_get_length(input); 151 /* we somehow lowered the watermark, stop reading */ 152 if (howmuch <= 0) { 153 bufferevent_wm_suspend_read(bufev); 154 goto done; 155 } 156 } 157 readmax = _bufferevent_get_read_max(bufev_p); 158 if (howmuch < 0 || howmuch > readmax) /* The use of -1 for "unlimited" 159 * uglifies this code. XXXX */ 160 howmuch = readmax; 161 if (bufev_p->read_suspended) 162 goto done; 163 164 evbuffer_unfreeze(input, 0); 165 res = evbuffer_read(input, fd, (int)howmuch); /* XXXX evbuffer_read would do better to take and return ev_ssize_t */ 166 evbuffer_freeze(input, 0); 167 168 if (res == -1) { 169 int err = evutil_socket_geterror(fd); 170 if (EVUTIL_ERR_RW_RETRIABLE(err)) 171 goto reschedule; 172 /* error case */ 173 what |= BEV_EVENT_ERROR; 174 } else if (res == 0) { 175 /* eof case */ 176 what |= BEV_EVENT_EOF; 177 } 178 179 if (res <= 0) 180 goto error; 181 182 _bufferevent_decrement_read_buckets(bufev_p, res); 183 184 /* Invoke the user callback - must always be called last */ 185 if (evbuffer_get_length(input) >= bufev->wm_read.low) 186 _bufferevent_run_readcb(bufev); 187 188 goto done; 189 190 reschedule: 191 goto done; 192 193 error: 194 bufferevent_disable(bufev, EV_READ); 195 _bufferevent_run_eventcb(bufev, what); 196 197 done: 198 _bufferevent_decref_and_unlock(bufev); 199 } 200 201 static void 202 bufferevent_writecb(evutil_socket_t fd, short event, void *arg) 203 { 204 struct bufferevent *bufev = arg; 205 struct bufferevent_private *bufev_p = 206 EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); 207 int res = 0; 208 short what = BEV_EVENT_WRITING; 209 int connected = 0; 210 ev_ssize_t atmost = -1; 211 212 _bufferevent_incref_and_lock(bufev); 213 214 if (event == EV_TIMEOUT) { 215 /* Note that we only check for event==EV_TIMEOUT. If 216 * event==EV_TIMEOUT|EV_WRITE, we can safely ignore the 217 * timeout, since a read has occurred */ 218 what |= BEV_EVENT_TIMEOUT; 219 goto error; 220 } 221 if (bufev_p->connecting) { 222 int c = evutil_socket_finished_connecting(fd); 223 /* we need to fake the error if the connection was refused 224 * immediately - usually connection to localhost on BSD */ 225 if (bufev_p->connection_refused) { 226 bufev_p->connection_refused = 0; 227 c = -1; 228 } 229 230 if (c == 0) 231 goto done; 232 233 bufev_p->connecting = 0; 234 if (c < 0) { 235 event_del(&bufev->ev_write); 236 event_del(&bufev->ev_read); 237 _bufferevent_run_eventcb(bufev, BEV_EVENT_ERROR); 238 goto done; 239 } else { 240 connected = 1; 241 #ifdef WIN32 242 if (BEV_IS_ASYNC(bufev)) { 243 event_del(&bufev->ev_write); 244 bufferevent_async_set_connected(bufev); 245 _bufferevent_run_eventcb(bufev, 246 BEV_EVENT_CONNECTED); 247 goto done; 248 } 249 #endif 250 _bufferevent_run_eventcb(bufev, 251 BEV_EVENT_CONNECTED); 252 if (!(bufev->enabled & EV_WRITE) || 253 bufev_p->write_suspended) { 254 event_del(&bufev->ev_write); 255 goto done; 256 } 257 } 258 } 259 260 atmost = _bufferevent_get_write_max(bufev_p); 261 262 if (bufev_p->write_suspended) 263 goto done; 264 265 if (evbuffer_get_length(bufev->output)) { 266 evbuffer_unfreeze(bufev->output, 1); 267 res = evbuffer_write_atmost(bufev->output, fd, atmost); 268 evbuffer_freeze(bufev->output, 1); 269 if (res == -1) { 270 int err = evutil_socket_geterror(fd); 271 if (EVUTIL_ERR_RW_RETRIABLE(err)) 272 goto reschedule; 273 what |= BEV_EVENT_ERROR; 274 } else if (res == 0) { 275 /* eof case 276 XXXX Actually, a 0 on write doesn't indicate 277 an EOF. An ECONNRESET might be more typical. 278 */ 279 what |= BEV_EVENT_EOF; 280 } 281 if (res <= 0) 282 goto error; 283 284 _bufferevent_decrement_write_buckets(bufev_p, res); 285 } 286 287 if (evbuffer_get_length(bufev->output) == 0) { 288 event_del(&bufev->ev_write); 289 } 290 291 /* 292 * Invoke the user callback if our buffer is drained or below the 293 * low watermark. 294 */ 295 if ((res || !connected) && 296 evbuffer_get_length(bufev->output) <= bufev->wm_write.low) { 297 _bufferevent_run_writecb(bufev); 298 } 299 300 goto done; 301 302 reschedule: 303 if (evbuffer_get_length(bufev->output) == 0) { 304 event_del(&bufev->ev_write); 305 } 306 goto done; 307 308 error: 309 bufferevent_disable(bufev, EV_WRITE); 310 _bufferevent_run_eventcb(bufev, what); 311 312 done: 313 _bufferevent_decref_and_unlock(bufev); 314 } 315 316 struct bufferevent * 317 bufferevent_socket_new(struct event_base *base, evutil_socket_t fd, 318 int options) 319 { 320 struct bufferevent_private *bufev_p; 321 struct bufferevent *bufev; 322 323 #ifdef WIN32 324 if (base && event_base_get_iocp(base)) 325 return bufferevent_async_new(base, fd, options); 326 #endif 327 328 if ((bufev_p = mm_calloc(1, sizeof(struct bufferevent_private)))== NULL) 329 return NULL; 330 331 if (bufferevent_init_common(bufev_p, base, &bufferevent_ops_socket, 332 options) < 0) { 333 mm_free(bufev_p); 334 return NULL; 335 } 336 bufev = &bufev_p->bev; 337 evbuffer_set_flags(bufev->output, EVBUFFER_FLAG_DRAINS_TO_FD); 338 339 event_assign(&bufev->ev_read, bufev->ev_base, fd, 340 EV_READ|EV_PERSIST, bufferevent_readcb, bufev); 341 event_assign(&bufev->ev_write, bufev->ev_base, fd, 342 EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev); 343 344 evbuffer_add_cb(bufev->output, bufferevent_socket_outbuf_cb, bufev); 345 346 evbuffer_freeze(bufev->input, 0); 347 evbuffer_freeze(bufev->output, 1); 348 349 return bufev; 350 } 351 352 int 353 bufferevent_socket_connect(struct bufferevent *bev, 354 struct sockaddr *sa, int socklen) 355 { 356 struct bufferevent_private *bufev_p = 357 EVUTIL_UPCAST(bev, struct bufferevent_private, bev); 358 359 evutil_socket_t fd; 360 int r = 0; 361 int result=-1; 362 int ownfd = 0; 363 364 _bufferevent_incref_and_lock(bev); 365 366 if (!bufev_p) 367 goto done; 368 369 fd = bufferevent_getfd(bev); 370 if (fd < 0) { 371 if (!sa) 372 goto done; 373 fd = socket(sa->sa_family, SOCK_STREAM, 0); 374 if (fd < 0) 375 goto done; 376 if (evutil_make_socket_nonblocking(fd)<0) 377 goto done; 378 ownfd = 1; 379 } 380 if (sa) { 381 #ifdef WIN32 382 if (bufferevent_async_can_connect(bev)) { 383 bufferevent_setfd(bev, fd); 384 r = bufferevent_async_connect(bev, fd, sa, socklen); 385 if (r < 0) 386 goto freesock; 387 bufev_p->connecting = 1; 388 result = 0; 389 goto done; 390 } else 391 #endif 392 r = evutil_socket_connect(&fd, sa, socklen); 393 if (r < 0) 394 goto freesock; 395 } 396 #ifdef WIN32 397 /* ConnectEx() isn't always around, even when IOCP is enabled. 398 * Here, we borrow the socket object's write handler to fall back 399 * on a non-blocking connect() when ConnectEx() is unavailable. */ 400 if (BEV_IS_ASYNC(bev)) { 401 event_assign(&bev->ev_write, bev->ev_base, fd, 402 EV_WRITE|EV_PERSIST, bufferevent_writecb, bev); 403 } 404 #endif 405 bufferevent_setfd(bev, fd); 406 if (r == 0) { 407 if (! be_socket_enable(bev, EV_WRITE)) { 408 bufev_p->connecting = 1; 409 result = 0; 410 goto done; 411 } 412 } else if (r == 1) { 413 /* The connect succeeded already. How very BSD of it. */ 414 result = 0; 415 bufev_p->connecting = 1; 416 event_active(&bev->ev_write, EV_WRITE, 1); 417 } else { 418 /* The connect failed already. How very BSD of it. */ 419 bufev_p->connection_refused = 1; 420 bufev_p->connecting = 1; 421 result = 0; 422 event_active(&bev->ev_write, EV_WRITE, 1); 423 } 424 425 goto done; 426 427 freesock: 428 _bufferevent_run_eventcb(bev, BEV_EVENT_ERROR); 429 if (ownfd) 430 evutil_closesocket(fd); 431 /* do something about the error? */ 432 done: 433 _bufferevent_decref_and_unlock(bev); 434 return result; 435 } 436 437 static void 438 bufferevent_connect_getaddrinfo_cb(int result, struct evutil_addrinfo *ai, 439 void *arg) 440 { 441 struct bufferevent *bev = arg; 442 struct bufferevent_private *bev_p = 443 EVUTIL_UPCAST(bev, struct bufferevent_private, bev); 444 int r; 445 BEV_LOCK(bev); 446 447 bufferevent_unsuspend_write(bev, BEV_SUSPEND_LOOKUP); 448 bufferevent_unsuspend_read(bev, BEV_SUSPEND_LOOKUP); 449 450 if (result != 0) { 451 bev_p->dns_error = result; 452 _bufferevent_run_eventcb(bev, BEV_EVENT_ERROR); 453 _bufferevent_decref_and_unlock(bev); 454 if (ai) 455 evutil_freeaddrinfo(ai); 456 return; 457 } 458 459 /* XXX use the other addrinfos? */ 460 /* XXX use this return value */ 461 r = bufferevent_socket_connect(bev, ai->ai_addr, (int)ai->ai_addrlen); 462 (void)r; 463 _bufferevent_decref_and_unlock(bev); 464 evutil_freeaddrinfo(ai); 465 } 466 467 int 468 bufferevent_socket_connect_hostname(struct bufferevent *bev, 469 struct evdns_base *evdns_base, int family, const char *hostname, int port) 470 { 471 char portbuf[10]; 472 struct evutil_addrinfo hint; 473 int err; 474 struct bufferevent_private *bev_p = 475 EVUTIL_UPCAST(bev, struct bufferevent_private, bev); 476 477 if (family != AF_INET && family != AF_INET6 && family != AF_UNSPEC) 478 return -1; 479 if (port < 1 || port > 65535) 480 return -1; 481 482 BEV_LOCK(bev); 483 bev_p->dns_error = 0; 484 BEV_UNLOCK(bev); 485 486 evutil_snprintf(portbuf, sizeof(portbuf), "%d", port); 487 488 memset(&hint, 0, sizeof(hint)); 489 hint.ai_family = family; 490 hint.ai_protocol = IPPROTO_TCP; 491 hint.ai_socktype = SOCK_STREAM; 492 493 bufferevent_suspend_write(bev, BEV_SUSPEND_LOOKUP); 494 bufferevent_suspend_read(bev, BEV_SUSPEND_LOOKUP); 495 496 bufferevent_incref(bev); 497 err = evutil_getaddrinfo_async(evdns_base, hostname, portbuf, 498 &hint, bufferevent_connect_getaddrinfo_cb, bev); 499 500 if (err == 0) { 501 return 0; 502 } else { 503 bufferevent_unsuspend_write(bev, BEV_SUSPEND_LOOKUP); 504 bufferevent_unsuspend_read(bev, BEV_SUSPEND_LOOKUP); 505 return -1; 506 } 507 } 508 509 int 510 bufferevent_socket_get_dns_error(struct bufferevent *bev) 511 { 512 int rv; 513 struct bufferevent_private *bev_p = 514 EVUTIL_UPCAST(bev, struct bufferevent_private, bev); 515 516 BEV_LOCK(bev); 517 rv = bev_p->dns_error; 518 BEV_UNLOCK(bev); 519 520 return rv; 521 } 522 523 /* 524 * Create a new buffered event object. 525 * 526 * The read callback is invoked whenever we read new data. 527 * The write callback is invoked whenever the output buffer is drained. 528 * The error callback is invoked on a write/read error or on EOF. 529 * 530 * Both read and write callbacks maybe NULL. The error callback is not 531 * allowed to be NULL and have to be provided always. 532 */ 533 534 struct bufferevent * 535 bufferevent_new(evutil_socket_t fd, 536 bufferevent_data_cb readcb, bufferevent_data_cb writecb, 537 bufferevent_event_cb eventcb, void *cbarg) 538 { 539 struct bufferevent *bufev; 540 541 if (!(bufev = bufferevent_socket_new(NULL, fd, 0))) 542 return NULL; 543 544 bufferevent_setcb(bufev, readcb, writecb, eventcb, cbarg); 545 546 return bufev; 547 } 548 549 550 static int 551 be_socket_enable(struct bufferevent *bufev, short event) 552 { 553 if (event & EV_READ) { 554 if (be_socket_add(&bufev->ev_read,&bufev->timeout_read) == -1) 555 return -1; 556 } 557 if (event & EV_WRITE) { 558 if (be_socket_add(&bufev->ev_write,&bufev->timeout_write) == -1) 559 return -1; 560 } 561 return 0; 562 } 563 564 static int 565 be_socket_disable(struct bufferevent *bufev, short event) 566 { 567 struct bufferevent_private *bufev_p = 568 EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); 569 if (event & EV_READ) { 570 if (event_del(&bufev->ev_read) == -1) 571 return -1; 572 } 573 /* Don't actually disable the write if we are trying to connect. */ 574 if ((event & EV_WRITE) && ! bufev_p->connecting) { 575 if (event_del(&bufev->ev_write) == -1) 576 return -1; 577 } 578 return 0; 579 } 580 581 static void 582 be_socket_destruct(struct bufferevent *bufev) 583 { 584 struct bufferevent_private *bufev_p = 585 EVUTIL_UPCAST(bufev, struct bufferevent_private, bev); 586 evutil_socket_t fd; 587 EVUTIL_ASSERT(bufev->be_ops == &bufferevent_ops_socket); 588 589 fd = event_get_fd(&bufev->ev_read); 590 591 event_del(&bufev->ev_read); 592 event_del(&bufev->ev_write); 593 594 if ((bufev_p->options & BEV_OPT_CLOSE_ON_FREE) && fd >= 0) 595 EVUTIL_CLOSESOCKET(fd); 596 } 597 598 static int 599 be_socket_adj_timeouts(struct bufferevent *bufev) 600 { 601 int r = 0; 602 if (event_pending(&bufev->ev_read, EV_READ, NULL)) 603 if (be_socket_add(&bufev->ev_read, &bufev->timeout_read) < 0) 604 r = -1; 605 if (event_pending(&bufev->ev_write, EV_WRITE, NULL)) { 606 if (be_socket_add(&bufev->ev_write, &bufev->timeout_write) < 0) 607 r = -1; 608 } 609 return r; 610 } 611 612 static int 613 be_socket_flush(struct bufferevent *bev, short iotype, 614 enum bufferevent_flush_mode mode) 615 { 616 return 0; 617 } 618 619 620 static void 621 be_socket_setfd(struct bufferevent *bufev, evutil_socket_t fd) 622 { 623 BEV_LOCK(bufev); 624 EVUTIL_ASSERT(bufev->be_ops == &bufferevent_ops_socket); 625 626 event_del(&bufev->ev_read); 627 event_del(&bufev->ev_write); 628 629 event_assign(&bufev->ev_read, bufev->ev_base, fd, 630 EV_READ|EV_PERSIST, bufferevent_readcb, bufev); 631 event_assign(&bufev->ev_write, bufev->ev_base, fd, 632 EV_WRITE|EV_PERSIST, bufferevent_writecb, bufev); 633 634 if (fd >= 0) 635 bufferevent_enable(bufev, bufev->enabled); 636 637 BEV_UNLOCK(bufev); 638 } 639 640 /* XXXX Should non-socket bufferevents support this? */ 641 int 642 bufferevent_priority_set(struct bufferevent *bufev, int priority) 643 { 644 int r = -1; 645 646 BEV_LOCK(bufev); 647 if (bufev->be_ops != &bufferevent_ops_socket) 648 goto done; 649 650 if (event_priority_set(&bufev->ev_read, priority) == -1) 651 goto done; 652 if (event_priority_set(&bufev->ev_write, priority) == -1) 653 goto done; 654 655 r = 0; 656 done: 657 BEV_UNLOCK(bufev); 658 return r; 659 } 660 661 /* XXXX Should non-socket bufferevents support this? */ 662 int 663 bufferevent_base_set(struct event_base *base, struct bufferevent *bufev) 664 { 665 int res = -1; 666 667 BEV_LOCK(bufev); 668 if (bufev->be_ops != &bufferevent_ops_socket) 669 goto done; 670 671 bufev->ev_base = base; 672 673 res = event_base_set(base, &bufev->ev_read); 674 if (res == -1) 675 goto done; 676 677 res = event_base_set(base, &bufev->ev_write); 678 done: 679 BEV_UNLOCK(bufev); 680 return res; 681 } 682 683 static int 684 be_socket_ctrl(struct bufferevent *bev, enum bufferevent_ctrl_op op, 685 union bufferevent_ctrl_data *data) 686 { 687 switch (op) { 688 case BEV_CTRL_SET_FD: 689 be_socket_setfd(bev, data->fd); 690 return 0; 691 case BEV_CTRL_GET_FD: 692 data->fd = event_get_fd(&bev->ev_read); 693 return 0; 694 case BEV_CTRL_GET_UNDERLYING: 695 case BEV_CTRL_CANCEL_ALL: 696 default: 697 return -1; 698 } 699 } 700