| 1 | /* $NetBSD: server.c,v 1.3 2007/03/18 10:00:42 plunky 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 | * server.c |
| 33 | * |
| 34 | * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com> |
| 35 | * All rights reserved. |
| 36 | * |
| 37 | * Redistribution and use in source and binary forms, with or without |
| 38 | * modification, are permitted provided that the following conditions |
| 39 | * are met: |
| 40 | * 1. Redistributions of source code must retain the above copyright |
| 41 | * notice, this list of conditions and the following disclaimer. |
| 42 | * 2. Redistributions in binary form must reproduce the above copyright |
| 43 | * notice, this list of conditions and the following disclaimer in the |
| 44 | * documentation and/or other materials provided with the distribution. |
| 45 | * |
| 46 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 47 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 48 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 49 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE |
| 50 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 51 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 52 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 53 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 54 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 55 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 56 | * SUCH DAMAGE. |
| 57 | * |
| 58 | * $Id: server.c,v 1.1.1.1 2007/11/20 11:56:11 griffin Exp $ |
| 59 | * $FreeBSD: src/usr.sbin/bluetooth/sdpd/server.c,v 1.2 2005/12/06 17:56:36 emax Exp $ |
| 60 | */ |
| 61 | |
| 62 | #include <sys/param.h> |
| 63 | #include <sys/select.h> |
| 64 | #include <sys/stat.h> |
| 65 | #include <sys/queue.h> |
| 66 | #include <sys/ucred.h> |
| 67 | #include <sys/un.h> |
| 68 | #include <sys/uio.h> |
| 69 | #include <netinet/in.h> |
| 70 | #include <arpa/inet.h> |
| 71 | #include <assert.h> |
| 72 | #include <bluetooth.h> |
| 73 | #include <errno.h> |
| 74 | #include <grp.h> |
| 75 | #include <pwd.h> |
| 76 | #include <sdp.h> |
| 77 | #include <stdio.h> |
| 78 | #include <stdlib.h> |
| 79 | #include <string.h> |
| 80 | #include <unistd.h> |
| 81 | #include "log.h" |
| 82 | #include "profile.h" |
| 83 | #include "provider.h" |
| 84 | #include "server.h" |
| 85 | |
| 86 | static void server_accept_client (server_p srv, int32_t fd); |
| 87 | static int32_t server_process_request (server_p srv, int32_t fd); |
| 88 | static int32_t server_send_error_response (server_p srv, int32_t fd, |
| 89 | uint16_t error); |
| 90 | static void server_close_fd (server_p srv, int32_t fd); |
| 91 | #if 0 |
| 92 | static int server_auth_check (server_p srv, struct sockcred *cred); |
| 93 | #endif |
| 94 | static int server_auth_check (server_p srv, struct cmsgcred *cred); |
| 95 | |
| 96 | /* |
| 97 | * Initialize server |
| 98 | */ |
| 99 | |
| 100 | int32_t |
| 101 | server_init(server_p srv, char const *control, char const *sgroup) |
| 102 | { |
| 103 | struct sockaddr_un un; |
| 104 | struct sockaddr_bt l2; |
| 105 | socklen_t size; |
| 106 | int32_t unsock, l2sock; |
| 107 | uint16_t imtu; |
| 108 | int opt; |
| 109 | |
| 110 | assert(srv != NULL); |
| 111 | assert(control != NULL); |
| 112 | |
| 113 | memset(srv, 0, sizeof(struct server)); |
| 114 | srv->sgroup = sgroup; |
| 115 | |
| 116 | /* Open control socket */ |
| 117 | if (unlink(control) < 0 && errno != ENOENT) { |
| 118 | log_crit("Could not unlink(%s). %s (%d)", |
| 119 | control, strerror(errno), errno); |
| 120 | return (-1); |
| 121 | } |
| 122 | |
| 123 | unsock = socket(PF_LOCAL, SOCK_STREAM, 0); |
| 124 | if (unsock < 0) { |
| 125 | log_crit("Could not create control socket. %s (%d)", |
| 126 | strerror(errno), errno); |
| 127 | return (-1); |
| 128 | } |
| 129 | |
| 130 | opt = 1; |
| 131 | #if 0 |
| 132 | if (setsockopt(unsock, 0, LOCAL_CREDS, &opt, sizeof(opt)) < 0) |
| 133 | log_crit("Warning: No credential checks on control socket"); |
| 134 | #endif |
| 135 | memset(&un, 0, sizeof(un)); |
| 136 | un.sun_len = sizeof(un); |
| 137 | un.sun_family = AF_LOCAL; |
| 138 | strlcpy(un.sun_path, control, sizeof(un.sun_path)); |
| 139 | |
| 140 | if (bind(unsock, (struct sockaddr *) &un, sizeof(un)) < 0) { |
| 141 | log_crit("Could not bind control socket. %s (%d)", |
| 142 | strerror(errno), errno); |
| 143 | close(unsock); |
| 144 | return (-1); |
| 145 | } |
| 146 | |
| 147 | if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) { |
| 148 | log_crit("Could not change permissions on control socket. " \ |
| 149 | "%s (%d)", strerror(errno), errno); |
| 150 | close(unsock); |
| 151 | return (-1); |
| 152 | } |
| 153 | |
| 154 | if (listen(unsock, 10) < 0) { |
| 155 | log_crit("Could not listen on control socket. %s (%d)", |
| 156 | strerror(errno), errno); |
| 157 | close(unsock); |
| 158 | return (-1); |
| 159 | } |
| 160 | |
| 161 | /* Open L2CAP socket */ |
| 162 | l2sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); |
| 163 | if (l2sock < 0) { |
| 164 | log_crit("Could not create L2CAP socket. %s (%d)", |
| 165 | strerror(errno), errno); |
| 166 | close(unsock); |
| 167 | return (-1); |
| 168 | } |
| 169 | |
| 170 | size = sizeof(imtu); |
| 171 | if (getsockopt(l2sock, BTPROTO_L2CAP, SO_L2CAP_IMTU, &imtu, &size) < 0) { |
| 172 | log_crit("Could not get L2CAP IMTU. %s (%d)", |
| 173 | strerror(errno), errno); |
| 174 | close(unsock); |
| 175 | close(l2sock); |
| 176 | return (-1); |
| 177 | } |
| 178 | |
| 179 | memset(&l2, 0, sizeof(l2)); |
| 180 | l2.bt_len = sizeof(l2); |
| 181 | l2.bt_family = AF_BLUETOOTH; |
| 182 | l2.bt_psm = L2CAP_PSM_SDP; |
| 183 | bdaddr_copy(&l2.bt_bdaddr, BDADDR_ANY); |
| 184 | |
| 185 | if (bind(l2sock, (struct sockaddr *) &l2, sizeof(l2)) < 0) { |
| 186 | log_crit("Could not bind L2CAP socket. %s (%d)", |
| 187 | strerror(errno), errno); |
| 188 | close(unsock); |
| 189 | close(l2sock); |
| 190 | return (-1); |
| 191 | } |
| 192 | |
| 193 | if (listen(l2sock, 10) < 0) { |
| 194 | log_crit("Could not listen on L2CAP socket. %s (%d)", |
| 195 | strerror(errno), errno); |
| 196 | close(unsock); |
| 197 | close(l2sock); |
| 198 | return (-1); |
| 199 | } |
| 200 | |
| 201 | /* Allocate incoming buffer */ |
| 202 | srv->imtu = (imtu > SDP_LOCAL_MTU)? imtu : SDP_LOCAL_MTU; |
| 203 | srv->req = (uint8_t *) calloc(srv->imtu, sizeof(srv->req[0])); |
| 204 | if (srv->req == NULL) { |
| 205 | log_crit("Could not allocate request buffer"); |
| 206 | close(unsock); |
| 207 | close(l2sock); |
| 208 | return (-1); |
| 209 | } |
| 210 | |
| 211 | /* Allocate memory for descriptor index */ |
| 212 | srv->fdidx = (fd_idx_p) calloc(FD_SETSIZE, sizeof(srv->fdidx[0])); |
| 213 | if (srv->fdidx == NULL) { |
| 214 | log_crit("Could not allocate fd index"); |
| 215 | free(srv->req); |
| 216 | close(unsock); |
| 217 | close(l2sock); |
| 218 | return (-1); |
| 219 | } |
| 220 | |
| 221 | /* Register Service Discovery profile (attach it to control socket) */ |
| 222 | if (provider_register_sd(unsock) < 0) { |
| 223 | log_crit("Could not register Service Discovery profile"); |
| 224 | free(srv->fdidx); |
| 225 | free(srv->req); |
| 226 | close(unsock); |
| 227 | close(l2sock); |
| 228 | return (-1); |
| 229 | } |
| 230 | |
| 231 | /* |
| 232 | * If we got here then everything is fine. Add both control sockets |
| 233 | * to the index. |
| 234 | */ |
| 235 | |
| 236 | FD_ZERO(&srv->fdset); |
| 237 | srv->maxfd = (unsock > l2sock)? unsock : l2sock; |
| 238 | |
| 239 | FD_SET(unsock, &srv->fdset); |
| 240 | srv->fdidx[unsock].valid = 1; |
| 241 | srv->fdidx[unsock].server = 1; |
| 242 | srv->fdidx[unsock].control = 1; |
| 243 | srv->fdidx[unsock].priv = 0; |
| 244 | srv->fdidx[unsock].rsp_cs = 0; |
| 245 | srv->fdidx[unsock].rsp_size = 0; |
| 246 | srv->fdidx[unsock].rsp_limit = 0; |
| 247 | srv->fdidx[unsock].omtu = SDP_LOCAL_MTU; |
| 248 | srv->fdidx[unsock].rsp = NULL; |
| 249 | |
| 250 | FD_SET(l2sock, &srv->fdset); |
| 251 | srv->fdidx[l2sock].valid = 1; |
| 252 | srv->fdidx[l2sock].server = 1; |
| 253 | srv->fdidx[l2sock].control = 0; |
| 254 | srv->fdidx[l2sock].priv = 0; |
| 255 | srv->fdidx[l2sock].rsp_cs = 0; |
| 256 | srv->fdidx[l2sock].rsp_size = 0; |
| 257 | srv->fdidx[l2sock].rsp_limit = 0; |
| 258 | srv->fdidx[l2sock].omtu = 0; /* unknown */ |
| 259 | srv->fdidx[l2sock].rsp = NULL; |
| 260 | |
| 261 | return (0); |
| 262 | } |
| 263 | |
| 264 | /* |
| 265 | * Shutdown server |
| 266 | */ |
| 267 | |
| 268 | void |
| 269 | server_shutdown(server_p srv) |
| 270 | { |
| 271 | int fd; |
| 272 | |
| 273 | assert(srv != NULL); |
| 274 | |
| 275 | for (fd = 0; fd < srv->maxfd + 1; fd ++) |
| 276 | if (srv->fdidx[fd].valid) |
| 277 | server_close_fd(srv, fd); |
| 278 | |
| 279 | free(srv->req); |
| 280 | free(srv->fdidx); |
| 281 | |
| 282 | memset(srv, 0, sizeof(*srv)); |
| 283 | } |
| 284 | |
| 285 | /* |
| 286 | * Do one server iteration |
| 287 | */ |
| 288 | |
| 289 | int32_t |
| 290 | server_do(server_p srv) |
| 291 | { |
| 292 | fd_set fdset; |
| 293 | int32_t n, fd; |
| 294 | |
| 295 | assert(srv != NULL); |
| 296 | |
| 297 | /* Copy cached version of the fd set and call select */ |
| 298 | memcpy(&fdset, &srv->fdset, sizeof(fdset)); |
| 299 | n = select(srv->maxfd + 1, &fdset, NULL, NULL, NULL); |
| 300 | if (n < 0) { |
| 301 | if (errno == EINTR) |
| 302 | return (0); |
| 303 | |
| 304 | log_err("Could not select(%d, %p). %s (%d)", |
| 305 | srv->maxfd + 1, &fdset, strerror(errno), errno); |
| 306 | |
| 307 | return (-1); |
| 308 | } |
| 309 | |
| 310 | /* Process descriptors */ |
| 311 | for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) { |
| 312 | if (!FD_ISSET(fd, &fdset)) |
| 313 | continue; |
| 314 | |
| 315 | assert(srv->fdidx[fd].valid); |
| 316 | n --; |
| 317 | |
| 318 | if (srv->fdidx[fd].server) |
| 319 | server_accept_client(srv, fd); |
| 320 | else if (server_process_request(srv, fd) != 0) |
| 321 | server_close_fd(srv, fd); |
| 322 | } |
| 323 | |
| 324 | return (0); |
| 325 | |
| 326 | } |
| 327 | |
| 328 | /* |
| 329 | * Accept new client connection and register it with index |
| 330 | */ |
| 331 | |
| 332 | static void |
| 333 | server_accept_client(server_p srv, int32_t fd) |
| 334 | { |
| 335 | uint8_t *rsp = NULL; |
| 336 | socklen_t size; |
| 337 | int32_t cfd; |
| 338 | uint16_t omtu; |
| 339 | |
| 340 | do { |
| 341 | cfd = accept(fd, NULL, NULL); |
| 342 | } while (cfd < 0 && errno == EINTR); |
| 343 | |
| 344 | if (cfd < 0) { |
| 345 | log_err("Could not accept connection on %s socket. %s (%d)", |
| 346 | srv->fdidx[fd].control? "control" : "L2CAP", |
| 347 | strerror(errno), errno); |
| 348 | return; |
| 349 | } |
| 350 | |
| 351 | assert(!FD_ISSET(cfd, &srv->fdset)); |
| 352 | assert(!srv->fdidx[cfd].valid); |
| 353 | |
| 354 | if (!srv->fdidx[fd].control) { |
| 355 | /* Get local BD_ADDR */ |
| 356 | size = sizeof(srv->req_sa); |
| 357 | if (getsockname(cfd,(struct sockaddr*)&srv->req_sa, &size) < 0) { |
| 358 | log_err("Could not get local BD_ADDR. %s (%d)", |
| 359 | strerror(errno), errno); |
| 360 | close(cfd); |
| 361 | return; |
| 362 | } |
| 363 | |
| 364 | /* Get outgoing MTU */ |
| 365 | size = sizeof(omtu); |
| 366 | if (getsockopt(cfd, BTPROTO_L2CAP, SO_L2CAP_OMTU, &omtu, &size) < 0) { |
| 367 | log_err("Could not get L2CAP OMTU. %s (%d)", |
| 368 | strerror(errno), errno); |
| 369 | close(cfd); |
| 370 | return; |
| 371 | } |
| 372 | |
| 373 | /* |
| 374 | * The maximum size of the L2CAP packet is 65536 bytes. |
| 375 | * The minimum L2CAP MTU is 43 bytes. That means we need |
| 376 | * 65536 / 43 = ~1524 chunks to transfer maximum packet |
| 377 | * size with minimum MTU. The "rsp_cs" field in fd_idx_t |
| 378 | * is 11 bit wide that gives us upto 2048 chunks. |
| 379 | */ |
| 380 | |
| 381 | if (omtu < L2CAP_MTU_MINIMUM) { |
| 382 | log_err("L2CAP OMTU is too small (%d bytes)", omtu); |
| 383 | close(cfd); |
| 384 | return; |
| 385 | } |
| 386 | } else { |
| 387 | bdaddr_copy(&srv->req_sa.bt_bdaddr, BDADDR_ANY); |
| 388 | omtu = srv->fdidx[fd].omtu; |
| 389 | } |
| 390 | |
| 391 | /* |
| 392 | * Allocate buffer. This is an overkill, but we can not know how |
| 393 | * big our reply is going to be. |
| 394 | */ |
| 395 | |
| 396 | rsp = (uint8_t *) calloc(L2CAP_MTU_MAXIMUM, sizeof(rsp[0])); |
| 397 | if (rsp == NULL) { |
| 398 | log_crit("Could not allocate response buffer"); |
| 399 | close(cfd); |
| 400 | return; |
| 401 | } |
| 402 | |
| 403 | /* Add client descriptor to the index */ |
| 404 | FD_SET(cfd, &srv->fdset); |
| 405 | if (srv->maxfd < cfd) |
| 406 | srv->maxfd = cfd; |
| 407 | srv->fdidx[cfd].valid = 1; |
| 408 | srv->fdidx[cfd].server = 0; |
| 409 | srv->fdidx[cfd].control = srv->fdidx[fd].control; |
| 410 | srv->fdidx[cfd].priv = 0; |
| 411 | srv->fdidx[cfd].rsp_cs = 0; |
| 412 | srv->fdidx[cfd].rsp_size = 0; |
| 413 | srv->fdidx[cfd].rsp_limit = 0; |
| 414 | srv->fdidx[cfd].omtu = omtu; |
| 415 | srv->fdidx[cfd].rsp = rsp; |
| 416 | } |
| 417 | |
| 418 | /* |
| 419 | * Process request from the client |
| 420 | */ |
| 421 | |
| 422 | static int32_t |
| 423 | server_process_request(server_p srv, int32_t fd) |
| 424 | { |
| 425 | uint8_t ctl[128]; |
| 426 | sdp_pdu_p pdu = (sdp_pdu_p) srv->req; |
| 427 | struct msghdr msg; |
| 428 | struct iovec iov; |
| 429 | int32_t len, error; |
| 430 | struct cmsghdr *cmsg; |
| 431 | |
| 432 | assert(srv->imtu > 0); |
| 433 | assert(srv->req != NULL); |
| 434 | assert(FD_ISSET(fd, &srv->fdset)); |
| 435 | assert(srv->fdidx[fd].valid); |
| 436 | assert(!srv->fdidx[fd].server); |
| 437 | assert(srv->fdidx[fd].rsp != NULL); |
| 438 | assert(srv->fdidx[fd].omtu >= L2CAP_MTU_MINIMUM); |
| 439 | |
| 440 | iov.iov_base = srv->req; |
| 441 | iov.iov_len = srv->imtu; |
| 442 | |
| 443 | msg.msg_name = NULL; |
| 444 | msg.msg_namelen = 0; |
| 445 | msg.msg_iov = &iov; |
| 446 | msg.msg_iovlen = 1; |
| 447 | msg.msg_control = ctl; |
| 448 | msg.msg_controllen = sizeof(ctl); |
| 449 | msg.msg_flags = 0; |
| 450 | |
| 451 | do { |
| 452 | len = recvmsg(fd, &msg, 0); |
| 453 | } while (len < 0 && errno == EINTR); |
| 454 | |
| 455 | if (len < 0) { |
| 456 | log_err("Could not receive SDP request from %s socket. %s (%d)", |
| 457 | srv->fdidx[fd].control? "control" : "L2CAP", |
| 458 | strerror(errno), errno); |
| 459 | return (-1); |
| 460 | } |
| 461 | if (len == 0) { |
| 462 | log_info("Client on %s socket has disconnected", |
| 463 | srv->fdidx[fd].control? "control" : "L2CAP"); |
| 464 | return (-1); |
| 465 | } |
| 466 | |
| 467 | #if XXX |
| 468 | if ((cmsg = CMSG_FIRSTHDR(&msg)) != NULL |
| 469 | && cmsg->cmsg_level == SOL_SOCKET |
| 470 | && cmsg->cmsg_type == SCM_CREDS |
| 471 | #if 0 |
| 472 | && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0)) |
| 473 | #endif |
| 474 | ) |
| 475 | srv->fdidx[fd].priv = |
| 476 | server_auth_check(srv, (struct cmsgcred *)CMSG_DATA(cmsg)); |
| 477 | #if 0 |
| 478 | server_auth_check(srv, (struct sockcred *)CMSG_DATA(cmsg)); |
| 479 | #endif |
| 480 | #else |
| 481 | srv->fdidx[fd].priv = 1; |
| 482 | #endif |
| 483 | |
| 484 | if (len >= sizeof(*pdu) |
| 485 | && (sizeof(*pdu) + (pdu->len = ntohs(pdu->len))) == len) { |
| 486 | switch (pdu->pid) { |
| 487 | case SDP_PDU_SERVICE_SEARCH_REQUEST: |
| 488 | error = server_prepare_service_search_response(srv, fd); |
| 489 | break; |
| 490 | |
| 491 | case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: |
| 492 | error = server_prepare_service_attribute_response(srv, fd); |
| 493 | break; |
| 494 | |
| 495 | case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: |
| 496 | error = server_prepare_service_search_attribute_response(srv, fd); |
| 497 | break; |
| 498 | |
| 499 | case SDP_PDU_SERVICE_REGISTER_REQUEST: |
| 500 | error = server_prepare_service_register_response(srv, fd); |
| 501 | break; |
| 502 | |
| 503 | case SDP_PDU_SERVICE_UNREGISTER_REQUEST: |
| 504 | error = server_prepare_service_unregister_response(srv, fd); |
| 505 | break; |
| 506 | |
| 507 | case SDP_PDU_SERVICE_CHANGE_REQUEST: |
| 508 | error = server_prepare_service_change_response(srv, fd); |
| 509 | break; |
| 510 | |
| 511 | default: |
| 512 | error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; |
| 513 | break; |
| 514 | } |
| 515 | } else |
| 516 | error = SDP_ERROR_CODE_INVALID_PDU_SIZE; |
| 517 | |
| 518 | if (error == 0) { |
| 519 | switch (pdu->pid) { |
| 520 | case SDP_PDU_SERVICE_SEARCH_REQUEST: |
| 521 | error = server_send_service_search_response(srv, fd); |
| 522 | break; |
| 523 | |
| 524 | case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: |
| 525 | error = server_send_service_attribute_response(srv, fd); |
| 526 | break; |
| 527 | |
| 528 | case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: |
| 529 | error = server_send_service_search_attribute_response(srv, fd); |
| 530 | break; |
| 531 | |
| 532 | case SDP_PDU_SERVICE_REGISTER_REQUEST: |
| 533 | error = server_send_service_register_response(srv, fd); |
| 534 | break; |
| 535 | |
| 536 | case SDP_PDU_SERVICE_UNREGISTER_REQUEST: |
| 537 | error = server_send_service_unregister_response(srv, fd); |
| 538 | break; |
| 539 | |
| 540 | case SDP_PDU_SERVICE_CHANGE_REQUEST: |
| 541 | error = server_send_service_change_response(srv, fd); |
| 542 | break; |
| 543 | |
| 544 | default: |
| 545 | error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; |
| 546 | break; |
| 547 | } |
| 548 | |
| 549 | if (error != 0) |
| 550 | log_err("Could not send SDP response to %s socket, " \ |
| 551 | "pdu->pid=%d, pdu->tid=%d, error=%d", |
| 552 | srv->fdidx[fd].control? "control" : "L2CAP", |
| 553 | pdu->pid, ntohs(pdu->tid), error); |
| 554 | } else { |
| 555 | log_err("Could not process SDP request from %s socket, " \ |
| 556 | "pdu->pid=%d, pdu->tid=%d, pdu->len=%d, len=%d, " \ |
| 557 | "error=%d", |
| 558 | srv->fdidx[fd].control? "control" : "L2CAP", |
| 559 | pdu->pid, ntohs(pdu->tid), pdu->len, len, error); |
| 560 | |
| 561 | error = server_send_error_response(srv, fd, error); |
| 562 | if (error != 0) |
| 563 | log_err("Could not send SDP error response to %s " \ |
| 564 | "socket, pdu->pid=%d, pdu->tid=%d, error=%d", |
| 565 | srv->fdidx[fd].control? "control" : "L2CAP", |
| 566 | pdu->pid, ntohs(pdu->tid), error); |
| 567 | } |
| 568 | |
| 569 | /* On error forget response (if any) */ |
| 570 | if (error != 0) { |
| 571 | srv->fdidx[fd].rsp_cs = 0; |
| 572 | srv->fdidx[fd].rsp_size = 0; |
| 573 | srv->fdidx[fd].rsp_limit = 0; |
| 574 | } |
| 575 | |
| 576 | return (error); |
| 577 | } |
| 578 | |
| 579 | /* |
| 580 | * Send SDP_Error_Response PDU |
| 581 | */ |
| 582 | |
| 583 | static int32_t |
| 584 | server_send_error_response(server_p srv, int32_t fd, uint16_t error) |
| 585 | { |
| 586 | int32_t size; |
| 587 | |
| 588 | struct { |
| 589 | sdp_pdu_t pdu; |
| 590 | uint16_t error; |
| 591 | } __attribute__ ((packed)) rsp; |
| 592 | |
| 593 | /* Prepare and send SDP error response */ |
| 594 | rsp.pdu.pid = SDP_PDU_ERROR_RESPONSE; |
| 595 | rsp.pdu.tid = ((sdp_pdu_p)(srv->req))->tid; |
| 596 | rsp.pdu.len = htons(sizeof(rsp.error)); |
| 597 | rsp.error = htons(error); |
| 598 | |
| 599 | do { |
| 600 | size = write(fd, &rsp, sizeof(rsp)); |
| 601 | } while (size < 0 && errno == EINTR); |
| 602 | |
| 603 | return ((size < 0)? errno : 0); |
| 604 | } |
| 605 | |
| 606 | /* |
| 607 | * Close descriptor and remove it from index |
| 608 | */ |
| 609 | |
| 610 | static void |
| 611 | server_close_fd(server_p srv, int32_t fd) |
| 612 | { |
| 613 | provider_p provider = NULL, provider_next = NULL; |
| 614 | |
| 615 | assert(FD_ISSET(fd, &srv->fdset)); |
| 616 | assert(srv->fdidx[fd].valid); |
| 617 | |
| 618 | close(fd); |
| 619 | |
| 620 | FD_CLR(fd, &srv->fdset); |
| 621 | if (fd == srv->maxfd) |
| 622 | srv->maxfd --; |
| 623 | |
| 624 | if (srv->fdidx[fd].rsp != NULL) |
| 625 | free(srv->fdidx[fd].rsp); |
| 626 | |
| 627 | memset(&srv->fdidx[fd], 0, sizeof(srv->fdidx[fd])); |
| 628 | |
| 629 | for (provider = provider_get_first(); |
| 630 | provider != NULL; |
| 631 | provider = provider_next) { |
| 632 | provider_next = provider_get_next(provider); |
| 633 | |
| 634 | if (provider->fd == fd) |
| 635 | provider_unregister(provider); |
| 636 | } |
| 637 | } |
| 638 | |
| 639 | static int |
| 640 | /*server_auth_check(server_p srv, struct sockcred *cred)*/ |
| 641 | server_auth_check(server_p srv, struct cmsgcred *cred) |
| 642 | { |
| 643 | struct group *grp; |
| 644 | int n; |
| 645 | |
| 646 | if (cred == NULL) |
| 647 | return 0; |
| 648 | |
| 649 | if (cred->cmcred_uid == 0 || cred->cmcred_euid == 0) |
| 650 | return 1; |
| 651 | |
| 652 | if (srv->sgroup == NULL) |
| 653 | return 0; |
| 654 | |
| 655 | grp = getgrnam(srv->sgroup); |
| 656 | if (grp == NULL) { |
| 657 | log_err("No gid for group '%s'", srv->sgroup); |
| 658 | srv->sgroup = NULL; |
| 659 | return 0; |
| 660 | } |
| 661 | |
| 662 | |
| 663 | if (cred->cmcred_gid == grp->gr_gid) |
| 664 | return 1; |
| 665 | |
| 666 | for (n = 0 ; n < cred->cmcred_ngroups ; n++) { |
| 667 | if (cred->cmcred_groups[n] == grp->gr_gid) |
| 668 | return 1; |
| 669 | } |
| 670 | |
| 671 | return 0; |
| 672 | } |