Home | History | Annotate | Line # | Download | only in sdpd
      1 /*	$NetBSD: server.c,v 1.12 2021/08/08 20:54:49 nia Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2006 Itronix Inc.
      5  * 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. The name of Itronix Inc. may not be used to endorse
     16  *    or promote products derived from this software without specific
     17  *    prior written permission.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
     20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
     23  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     26  * ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 /*-
     32  * Copyright (c) 2009 The NetBSD Foundation, Inc.
     33  * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin (at) yahoo.com>
     34  * All rights reserved.
     35  *
     36  * Redistribution and use in source and binary forms, with or without
     37  * modification, are permitted provided that the following conditions
     38  * are met:
     39  * 1. Redistributions of source code must retain the above copyright
     40  *    notice, this list of conditions and the following disclaimer.
     41  * 2. Redistributions in binary form must reproduce the above copyright
     42  *    notice, this list of conditions and the following disclaimer in the
     43  *    documentation and/or other materials provided with the distribution.
     44  *
     45  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     46  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     48  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     49  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     50  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     51  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     52  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     53  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     54  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     55  * SUCH DAMAGE.
     56  *
     57  * $FreeBSD: src/usr.sbin/bluetooth/sdpd/server.c,v 1.2 2005/12/06 17:56:36 emax Exp $
     58  */
     59 
     60 #include <sys/cdefs.h>
     61 __RCSID("$NetBSD: server.c,v 1.12 2021/08/08 20:54:49 nia Exp $");
     62 
     63 #include <sys/select.h>
     64 #include <sys/stat.h>
     65 #include <sys/ucred.h>
     66 #include <sys/un.h>
     67 
     68 #include <assert.h>
     69 #include <bluetooth.h>
     70 #include <errno.h>
     71 #include <grp.h>
     72 #include <pwd.h>
     73 #include <sdp.h>
     74 #include <stdio.h>
     75 #include <stdlib.h>
     76 #include <string.h>
     77 #include <unistd.h>
     78 
     79 #include "sdpd.h"
     80 
     81 static bool	server_open_control	(server_t *, char const *);
     82 static bool	server_open_l2cap	(server_t *);
     83 static void	server_accept_client	(server_t *, int);
     84 static bool	server_process_request	(server_t *, int);
     85 static void	server_close_fd		(server_t *, int);
     86 static bool	server_auth_check	(server_t *, void *);
     87 
     88 /* number of groups we allocate space for in cmsg */
     89 #define MAX_GROUPS	20
     90 
     91 /*
     92  * Initialize server
     93  */
     94 bool
     95 server_init(server_t *srv, char const *control, char const *sgroup)
     96 {
     97 
     98 	assert(srv != NULL);
     99 	assert(control != NULL);
    100 
    101 	memset(srv, 0, sizeof(*srv));
    102 	FD_ZERO(&srv->fdset);
    103 	srv->sgroup = sgroup;
    104 
    105 	srv->fdmax = -1;
    106 	srv->fdidx = calloc(FD_SETSIZE, sizeof(fd_idx_t));
    107 	if (srv->fdidx == NULL) {
    108 		log_crit("Failed to allocate fd index");
    109 		goto fail;
    110 	}
    111 
    112 	srv->ctllen = CMSG_SPACE(SOCKCREDSIZE(MAX_GROUPS));
    113 	srv->ctlbuf = malloc(srv->ctllen);
    114 	if (srv->ctlbuf == NULL) {
    115 		log_crit("Malloc cmsg buffer (len=%zu) failed.", srv->ctllen);
    116 		goto fail;
    117 	}
    118 
    119 	srv->imtu = SDP_LOCAL_MTU - sizeof(sdp_pdu_t);
    120 	srv->ibuf = malloc(srv->imtu);
    121 	if (srv->ibuf == NULL) {
    122 		log_crit("Malloc input buffer (imtu=%d) failed.", srv->imtu);
    123 		goto fail;
    124 	}
    125 
    126 	srv->omtu = L2CAP_MTU_DEFAULT - sizeof(sdp_pdu_t);
    127 	srv->obuf = malloc(srv->omtu);
    128 	if (srv->obuf == NULL) {
    129 		log_crit("Malloc output buffer (omtu=%d) failed.", srv->omtu);
    130 		goto fail;
    131 	}
    132 
    133 	if (db_init(srv)
    134 	    && server_open_control(srv, control)
    135 	    && server_open_l2cap(srv))
    136 		return true;
    137 
    138 fail:
    139 	server_shutdown(srv);
    140 	return false;
    141 }
    142 
    143 /*
    144  * Open local control socket
    145  */
    146 static bool
    147 server_open_control(server_t *srv, char const *control)
    148 {
    149 	struct sockaddr_un	un;
    150 	int			opt, fd;
    151 
    152 	if (unlink(control) == -1 && errno != ENOENT) {
    153 		log_crit("Could not unlink(%s). %s (%d)",
    154 		    control, strerror(errno), errno);
    155 
    156 		return false;
    157 	}
    158 
    159 	fd = socket(PF_LOCAL, SOCK_STREAM, 0);
    160 	if (fd == -1) {
    161 		log_crit("Could not create control socket. %s (%d)",
    162 		    strerror(errno), errno);
    163 
    164 		return false;
    165 	}
    166 
    167 	opt = 1;
    168 	if (setsockopt(fd, SOL_LOCAL, LOCAL_CREDS, &opt, sizeof(opt)) == -1)
    169 		log_crit("Warning: No credential checks on control socket");
    170 
    171 	memset(&un, 0, sizeof(un));
    172 	un.sun_len = sizeof(un);
    173 	un.sun_family = AF_LOCAL;
    174 	strlcpy(un.sun_path, control, sizeof(un.sun_path));
    175 
    176 	if (bind(fd, (struct sockaddr *) &un, sizeof(un)) == -1) {
    177 		log_crit("Could not bind control socket. %s (%d)",
    178 		    strerror(errno), errno);
    179 
    180 		close(fd);
    181 		return false;
    182 	}
    183 
    184 	if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
    185 		log_crit("Could not set permissions on control socket. %s (%d)",
    186 		    strerror(errno), errno);
    187 
    188 		close(fd);
    189 		return false;
    190 	}
    191 
    192 	if (listen(fd, 5) == -1) {
    193 		log_crit("Could not listen on control socket. %s (%d)",
    194 		    strerror(errno), errno);
    195 
    196 		close(fd);
    197 		return false;
    198 	}
    199 
    200 	/* Add control descriptor to index */
    201 	if (fd > srv->fdmax)
    202 		srv->fdmax = fd;
    203 
    204 	FD_SET(fd, &srv->fdset);
    205 	srv->fdidx[fd].valid = true;
    206 	srv->fdidx[fd].server = true;
    207 	srv->fdidx[fd].control = true;
    208 	srv->fdidx[fd].priv = false;
    209 	return true;
    210 }
    211 
    212 /*
    213  * Open L2CAP server socket
    214  */
    215 static bool
    216 server_open_l2cap(server_t *srv)
    217 {
    218 	struct sockaddr_bt	sa;
    219 	int			fd;
    220 
    221 	fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
    222 	if (fd == -1) {
    223 		log_crit("Could not create L2CAP socket. %s (%d)",
    224 		    strerror(errno), errno);
    225 
    226 		return false;
    227 	}
    228 
    229         if (setsockopt(fd, BTPROTO_L2CAP, SO_L2CAP_IMTU,
    230 	    &srv->imtu, sizeof(srv->imtu)) == -1) {
    231 		log_crit("Could not set L2CAP Incoming MTU. %s (%d)",
    232 		    strerror(errno), errno);
    233 
    234 		close(fd);
    235 		return false;
    236         }
    237 
    238 	memset(&sa, 0, sizeof(sa));
    239 	sa.bt_len = sizeof(sa);
    240 	sa.bt_family = AF_BLUETOOTH;
    241 	sa.bt_psm = L2CAP_PSM_SDP;
    242 	bdaddr_copy(&sa.bt_bdaddr, BDADDR_ANY);
    243 
    244 	if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {
    245 		log_crit("Could not bind L2CAP socket. %s (%d)",
    246 		    strerror(errno), errno);
    247 
    248 		close(fd);
    249 		return false;
    250 	}
    251 
    252 	if (listen(fd, 5) == -1) {
    253 		log_crit("Could not listen on L2CAP socket. %s (%d)",
    254 		    strerror(errno), errno);
    255 
    256 		close(fd);
    257 		return false;
    258 	}
    259 
    260 	/* Add L2CAP descriptor to index */
    261 	if (fd > srv->fdmax)
    262 		srv->fdmax = fd;
    263 
    264 	FD_SET(fd, &srv->fdset);
    265 	srv->fdidx[fd].valid = true;
    266 	srv->fdidx[fd].server = true;
    267 	srv->fdidx[fd].control = false;
    268 	srv->fdidx[fd].priv = false;
    269 	return true;
    270 }
    271 
    272 /*
    273  * Shutdown server
    274  */
    275 void
    276 server_shutdown(server_t *srv)
    277 {
    278 	record_t *r;
    279 	int	fd;
    280 
    281 	assert(srv != NULL);
    282 
    283 	while ((r = LIST_FIRST(&srv->rlist)) != NULL) {
    284 		LIST_REMOVE(r, next);
    285 		free(r);
    286 	}
    287 
    288 	for (fd = 0; fd < srv->fdmax + 1; fd ++) {
    289 		if (srv->fdidx[fd].valid)
    290 			server_close_fd(srv, fd);
    291 	}
    292 
    293 	free(srv->fdidx);
    294 	free(srv->ctlbuf);
    295 	free(srv->ibuf);
    296 	free(srv->obuf);
    297 
    298 	memset(srv, 0, sizeof(*srv));
    299 }
    300 
    301 /*
    302  * Do one server iteration
    303  */
    304 bool
    305 server_do(server_t *srv)
    306 {
    307 	fd_set	fdset;
    308 	int	n, fd;
    309 
    310 	assert(srv != NULL);
    311 
    312 	memcpy(&fdset, &srv->fdset, sizeof(fdset));
    313 	n = select(srv->fdmax + 1, &fdset, NULL, NULL, NULL);
    314 	if (n == -1) {
    315 		if (errno == EINTR)
    316 			return true;
    317 
    318 		log_err("Could not select(%d, %p). %s (%d)",
    319 		    srv->fdmax + 1, &fdset, strerror(errno), errno);
    320 
    321 		return false;
    322 	}
    323 
    324 	for (fd = 0; fd < srv->fdmax + 1 && n > 0; fd++) {
    325 		if (!FD_ISSET(fd, &fdset))
    326 			continue;
    327 
    328 		assert(srv->fdidx[fd].valid);
    329 
    330 		if (srv->fdidx[fd].server)
    331 			server_accept_client(srv, fd);
    332 		else if (!server_process_request(srv, fd))
    333 			server_close_fd(srv, fd);
    334 
    335 		n--;
    336 	}
    337 
    338 	return true;
    339 
    340 }
    341 
    342 /*
    343  * Accept new client connection and register it with index
    344  */
    345 static void
    346 server_accept_client(server_t *srv, int fd)
    347 {
    348 	struct sockaddr_bt	sa;
    349 	socklen_t		len;
    350 	int			cfd;
    351 	uint16_t		omtu;
    352 
    353 	do {
    354 		cfd = accept(fd, NULL, NULL);
    355 	} while (cfd == -1 && errno == EINTR);
    356 
    357 	if (cfd == -1) {
    358 		log_err("Could not accept connection on %s socket. %s (%d)",
    359 		    srv->fdidx[fd].control ? "control" : "L2CAP",
    360 		    strerror(errno), errno);
    361 
    362 		return;
    363 	}
    364 
    365 	if (cfd >= FD_SETSIZE) {
    366 		log_crit("File descriptor too large");
    367 		close(cfd);
    368 		return;
    369 	}
    370 
    371 	assert(!FD_ISSET(cfd, &srv->fdset));
    372 	assert(!srv->fdidx[cfd].valid);
    373 
    374 	memset(&sa, 0, sizeof(sa));
    375 	omtu = srv->omtu;
    376 
    377 	if (!srv->fdidx[fd].control) {
    378 		len = sizeof(sa);
    379 		if (getsockname(cfd, (struct sockaddr *)&sa, &len) == -1)
    380 			log_warning("getsockname failed, using BDADDR_ANY");
    381 
    382 		len = sizeof(omtu);
    383 	        if (getsockopt(cfd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &omtu, &len) == -1)
    384 			log_warning("Could not get L2CAP OMTU, using %d", omtu);
    385 		else
    386 			omtu -= sizeof(sdp_pdu_t);
    387 	}
    388 
    389 	/* Add client descriptor to the index */
    390 	if (cfd > srv->fdmax)
    391 		srv->fdmax = cfd;
    392 
    393 	FD_SET(cfd, &srv->fdset);
    394 	srv->fdidx[cfd].valid = true;
    395 	srv->fdidx[cfd].server = false;
    396 	srv->fdidx[cfd].control = srv->fdidx[fd].control;
    397 	srv->fdidx[cfd].priv = false;
    398 	srv->fdidx[cfd].omtu = (omtu > srv->omtu) ? srv->omtu : omtu;
    399 	srv->fdidx[cfd].offset = 0;
    400 	bdaddr_copy(&srv->fdidx[cfd].bdaddr, &sa.bt_bdaddr);
    401 
    402 	log_debug("new %s client on fd#%d",
    403 	    srv->fdidx[cfd].control ? "control" : "L2CAP", cfd);
    404 }
    405 
    406 /*
    407  * Process request from the client
    408  */
    409 static bool
    410 server_process_request(server_t *srv, int fd)
    411 {
    412 	struct msghdr	msg;
    413 	struct iovec	iov[2];
    414 	struct cmsghdr	*cmsg;
    415 	ssize_t		len;
    416 	uint16_t	error;
    417 
    418 	assert(FD_ISSET(fd, &srv->fdset));
    419 	assert(srv->fdidx[fd].valid);
    420 	assert(!srv->fdidx[fd].server);
    421 
    422 	iov[0].iov_base = &srv->pdu;
    423 	iov[0].iov_len = sizeof(srv->pdu);
    424 	iov[1].iov_base = srv->ibuf;
    425 	iov[1].iov_len = srv->imtu;
    426 
    427 	msg.msg_name = NULL;
    428 	msg.msg_namelen = 0;
    429 	msg.msg_iov = iov;
    430 	msg.msg_iovlen = __arraycount(iov);
    431 	msg.msg_control = srv->ctlbuf;
    432 	msg.msg_controllen = srv->ctllen;
    433 	msg.msg_flags = 0;
    434 
    435 	do {
    436 		len = recvmsg(fd, &msg, 0);
    437 	} while (len == -1 && errno == EINTR);
    438 
    439 	if (len == -1) {
    440 		log_err("Could not receive SDP request on %s socket. %s (%d)",
    441 		    srv->fdidx[fd].control ? "control" : "L2CAP",
    442 		    strerror(errno), errno);
    443 
    444 		return false;
    445 	}
    446 
    447 	if (len == 0) {
    448 		log_info("Client on %s socket has disconnected",
    449 		    srv->fdidx[fd].control ? "control" : "L2CAP");
    450 
    451 		return false;
    452 	}
    453 
    454 	if (msg.msg_flags & MSG_TRUNC)
    455 		log_info("Truncated message on %s socket",
    456 		    srv->fdidx[fd].control ? "control" : "L2CAP");
    457 
    458 	if ((cmsg = CMSG_FIRSTHDR(&msg)) != NULL
    459 	    && cmsg->cmsg_level == SOL_SOCKET
    460 	    && cmsg->cmsg_type == SCM_CREDS
    461 	    && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0)))
    462 		srv->fdidx[fd].priv = server_auth_check(srv, CMSG_DATA(cmsg));
    463 
    464 	srv->pdu.len = be16toh(srv->pdu.len);
    465 
    466 	if ((uint32_t)len < sizeof(srv->pdu)
    467 	    || (uint32_t)len != sizeof(srv->pdu) + srv->pdu.len) {
    468 		error = SDP_ERROR_CODE_INVALID_PDU_SIZE;
    469 	} else {
    470 		switch (srv->pdu.pid) {
    471 		case SDP_PDU_SERVICE_SEARCH_REQUEST:
    472 			error = service_search_request(srv, fd);
    473 			break;
    474 
    475 		case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST:
    476 			error = service_attribute_request(srv, fd);
    477 			break;
    478 
    479 		case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
    480 			error = service_search_attribute_request(srv, fd);
    481 			break;
    482 
    483 #ifdef SDP_COMPAT
    484 		case SDP_PDU_SERVICE_REGISTER_REQUEST:
    485 			error = compat_register_request(srv, fd);
    486 			break;
    487 
    488 		case SDP_PDU_SERVICE_CHANGE_REQUEST:
    489 			error = compat_change_request(srv, fd);
    490 			break;
    491 #endif
    492 
    493 		case SDP_PDU_RECORD_INSERT_REQUEST:
    494 			error = record_insert_request(srv, fd);
    495 			break;
    496 
    497 		case SDP_PDU_RECORD_UPDATE_REQUEST:
    498 			error = record_update_request(srv, fd);
    499 			break;
    500 
    501 		case SDP_PDU_RECORD_REMOVE_REQUEST:
    502 			error = record_remove_request(srv, fd);
    503 			break;
    504 
    505 		default:
    506 			error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
    507 			break;
    508 		}
    509 	}
    510 
    511 	if (error != 0) {
    512 		srv->fdidx[fd].offset = 0;
    513 		db_unselect(srv, fd);
    514 		srv->pdu.pid = SDP_PDU_ERROR_RESPONSE;
    515 		srv->pdu.len = sizeof(error);
    516 		be16enc(srv->obuf, error);
    517 		log_debug("sending ErrorResponse (error=0x%04x)", error);
    518 	}
    519 
    520 	iov[0].iov_base = &srv->pdu;
    521 	iov[0].iov_len = sizeof(srv->pdu);
    522 	iov[1].iov_base = srv->obuf;
    523 	iov[1].iov_len = srv->pdu.len;
    524 
    525 	srv->pdu.len = htobe16(srv->pdu.len);
    526 
    527 	msg.msg_name = NULL;
    528 	msg.msg_namelen = 0;
    529 	msg.msg_iov = iov;
    530 	msg.msg_iovlen = __arraycount(iov);
    531 	msg.msg_control = NULL;
    532 	msg.msg_controllen = 0;
    533 	msg.msg_flags = 0;
    534 
    535 	do {
    536 		len = sendmsg(fd, &msg, 0);
    537 	} while (len == -1 && errno == EINTR);
    538 
    539 	if (len == -1) {
    540 		log_err("Could not send SDP response on %s socket. %s (%d)",
    541 		    srv->fdidx[fd].control ? "control" : "L2CAP",
    542 		    strerror(errno), errno);
    543 
    544 		return false;
    545 	}
    546 
    547 	return true;
    548 }
    549 
    550 /*
    551  * Close descriptor and remove it from index
    552  */
    553 static void
    554 server_close_fd(server_t *srv, int fd)
    555 {
    556 
    557 	assert(FD_ISSET(fd, &srv->fdset));
    558 	assert(srv->fdidx[fd].valid);
    559 
    560 	db_unselect(srv, fd);	/* release selected records */
    561 	db_release(srv, fd);	/* expire owned records */
    562 
    563 	close(fd);
    564 	FD_CLR(fd, &srv->fdset);
    565 	srv->fdidx[fd].valid = false;
    566 
    567 	log_debug("client on fd#%d closed", fd);
    568 
    569 	if (fd == srv->fdmax) {
    570 		while (fd > 0 && !srv->fdidx[fd].valid)
    571 			fd--;
    572 
    573 		srv->fdmax = fd;
    574 	}
    575 }
    576 
    577 /*
    578  * check credentials, return true when permitted to modify service records
    579  */
    580 static bool
    581 server_auth_check(server_t *srv, void *data)
    582 {
    583 	struct sockcred *cred = data;
    584 	struct group *grp;
    585 	int n;
    586 
    587 	if (cred == NULL)
    588 		return false;
    589 
    590 	if (cred->sc_uid == 0 || cred->sc_euid == 0)
    591 		return true;
    592 
    593 	if (srv->sgroup == NULL)
    594 		return false;
    595 
    596 	grp = getgrnam(srv->sgroup);
    597 	if (grp == NULL) {
    598 		log_err("No gid for group '%s'", srv->sgroup);
    599 		srv->sgroup = NULL;
    600 		return false;
    601 	}
    602 
    603 	if (cred->sc_gid == grp->gr_gid || cred->sc_egid == grp->gr_gid)
    604 		return true;
    605 
    606 	if (cred->sc_ngroups > MAX_GROUPS) {
    607 		log_info("Credentials truncated, lost %d groups",
    608 		    MAX_GROUPS - cred->sc_ngroups);
    609 
    610 		cred->sc_ngroups = MAX_GROUPS;
    611 	}
    612 
    613 	for (n = 0 ; n < cred->sc_ngroups ; n++) {
    614 		if (cred->sc_groups[n] == grp->gr_gid)
    615 			return true;
    616 	}
    617 
    618 	return false;
    619 }
    620