/* * Initialize server */ bool server_init(server_t *srv, char const *control, char const *sgroup) { assert(srv != NULL); assert(control != NULL); memset(srv, 0, sizeof(*srv)); FD_ZERO(&srv->fdset); srv->sgroup = sgroup; srv->fdmax = -1; srv->fdidx = calloc(FD_SETSIZE, sizeof(fd_idx_t)); if (srv->fdidx == NULL) { log_crit("Failed to allocate fd index"); goto fail; } srv->ctllen = CMSG_SPACE(SOCKCREDSIZE(MAX_GROUPS)); srv->ctlbuf = malloc(srv->ctllen); if (srv->ctlbuf == NULL) { log_crit("Malloc cmsg buffer (len=%zu) failed.", srv->ctllen); goto fail; } srv->imtu = SDP_LOCAL_MTU - sizeof(sdp_pdu_t); srv->ibuf = malloc(srv->imtu); if (srv->ibuf == NULL) { log_crit("Malloc input buffer (imtu=%d) failed.", srv->imtu); goto fail; } srv->omtu = L2CAP_MTU_DEFAULT - sizeof(sdp_pdu_t); srv->obuf = malloc(srv->omtu); if (srv->obuf == NULL) { log_crit("Malloc output buffer (omtu=%d) failed.", srv->omtu); goto fail; } if (db_init(srv) && server_open_control(srv, control) && server_open_l2cap(srv)) return true; fail: server_shutdown(srv); return false; }
value netsys_peek_peer_credentials(value fd) { CAMLparam1(fd); CAMLlocal1(result); int uid; int gid; #ifdef SO_PASSCRED /* Linux */ { int one = 1; struct msghdr msg; struct cmsghdr *cmp; struct ucred *sc; char buf[CMSG_SPACE(sizeof(*sc))]; struct iovec iov; char iovbuf[1]; if (setsockopt(Int_val(fd), SOL_SOCKET, SO_PASSCRED, &one, sizeof(one)) < 0) { uerror("setsockopt", Nothing); }; memset(&msg, 0, sizeof msg); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = buf; msg.msg_controllen = sizeof(buf); iov.iov_base = iovbuf; iov.iov_len = 1; /* Linux requires that at least one byte must be transferred. * So we initialize the iovector for exactly one byte. */ if (recvmsg(Int_val(fd), &msg, MSG_PEEK) < 0) { uerror("recvmsg", Nothing); }; if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { raise_not_found(); }; cmp = CMSG_FIRSTHDR(&msg); if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDENTIALS) { raise_not_found(); }; sc = (struct ucred *) CMSG_DATA(cmp); uid = sc->uid; gid = sc->gid; } #else #ifdef LOCAL_CREDS /* NetBSD */ /* The following code has been copied from libc: rpc/svc_vc.c * TODO: The following code does not work. No idea why. * msg_controllen is always 0. Maybe the socket option must be * set earlier (but that would be very strange). */ { int one = 1; struct msghdr msg; struct cmsghdr *cmp; void *crmsg = NULL; struct sockcred *sc; socklen_t crmsgsize; struct iovec iov; char buf; if (setsockopt(Int_val(fd), SOL_SOCKET, LOCAL_CREDS, &one, sizeof(one)) < 0) { uerror("setsockopt", Nothing); }; memset(&msg, 0, sizeof msg); crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS_MAX)); crmsg = stat_alloc(crmsgsize); memset(crmsg, 0, crmsgsize); msg.msg_control = crmsg; msg.msg_controllen = crmsgsize; msg.msg_iov = &iov; msg.msg_iovlen = 1; iov.iov_base = &buf; iov.iov_len = 1; if (recvmsg(Int_val(fd), &msg, MSG_PEEK) < 0) { stat_free(crmsg); uerror("recvmsg", Nothing); }; if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { stat_free(crmsg); raise_not_found(); }; cmp = CMSG_FIRSTHDR(&msg); if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { stat_free(crmsg); raise_not_found(); }; sc = (struct sockcred *)(void *)CMSG_DATA(cmp); uid = sc->sc_euid; gid = sc->sc_egid; free(crmsg); } #else invalid_argument("peek_peer_credentials"); #endif #endif /* Allocate a pair, and put the result into it: */ result = alloc_tuple(2); Store_field(result, 0, Val_int(uid)); Store_field(result, 1, Val_int(gid)); CAMLreturn(result); }
int lutil_getpeereid( int s, uid_t *euid, gid_t *egid #ifdef LDAP_PF_LOCAL_SENDMSG , struct berval *peerbv #endif ) { #ifdef LDAP_PF_LOCAL #if defined( HAVE_GETPEERUCRED ) ucred_t *uc = NULL; if( getpeerucred( s, &uc ) == 0 ) { *euid = ucred_geteuid( uc ); *egid = ucred_getegid( uc ); ucred_free( uc ); return 0; } #elif defined( SO_PEERCRED ) struct ucred peercred; ber_socklen_t peercredlen = sizeof peercred; if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED, (void *)&peercred, &peercredlen ) == 0 ) && ( peercredlen == sizeof peercred )) { *euid = peercred.uid; *egid = peercred.gid; return 0; } #elif defined( LOCAL_PEERCRED ) struct xucred peercred; ber_socklen_t peercredlen = sizeof peercred; if(( getsockopt( s, LOCAL_PEERCRED, 1, (void *)&peercred, &peercredlen ) == 0 ) && ( peercred.cr_version == XUCRED_VERSION )) { *euid = peercred.cr_uid; *egid = peercred.cr_gid; return 0; } #elif defined( LDAP_PF_LOCAL_SENDMSG ) && defined( MSG_WAITALL ) int err, fd; struct iovec iov; struct msghdr msg = {0}; # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL # ifndef CMSG_SPACE # define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len)) # endif # ifndef CMSG_LEN # define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) # endif struct { struct cmsghdr cm; int fd; } control_st; struct cmsghdr *cmsg; # endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ struct stat st; struct sockaddr_un lname, rname; ber_socklen_t llen, rlen; rlen = sizeof(rname); llen = sizeof(lname); memset( &lname, 0, sizeof( lname )); getsockname(s, (struct sockaddr *)&lname, &llen); iov.iov_base = peerbv->bv_val; iov.iov_len = peerbv->bv_len; msg.msg_iov = &iov; msg.msg_iovlen = 1; peerbv->bv_len = 0; # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL msg.msg_control = &control_st; msg.msg_controllen = sizeof( struct cmsghdr ) + sizeof( int ); /* no padding! */ cmsg = CMSG_FIRSTHDR( &msg ); # else msg.msg_accrights = (char *)&fd; msg.msg_accrightslen = sizeof(fd); # endif /* * AIX returns a bogus file descriptor if recvmsg() is * called with MSG_PEEK (is this a bug?). Hence we need * to receive the Abandon PDU. */ err = recvmsg( s, &msg, MSG_WAITALL ); if( err >= 0 && # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS # else msg.msg_accrightslen == sizeof(int) # endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/ ) { int mode = S_IFIFO|S_ISUID|S_IRWXU; /* We must receive a valid descriptor, it must be a pipe, * it must only be accessible by its owner, and it must * have the name of our socket written on it. */ peerbv->bv_len = err; # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL fd = (*(int *)CMSG_DATA( cmsg )); # endif err = fstat( fd, &st ); if ( err == 0 ) rlen = read(fd, &rname, rlen); close(fd); if( err == 0 && st.st_mode == mode && llen == rlen && !memcmp(&lname, &rname, llen)) { *euid = st.st_uid; *egid = st.st_gid; return 0; } } #elif defined(SOCKCREDSIZE) struct msghdr msg; ber_socklen_t crmsgsize; void *crmsg; struct cmsghdr *cmp; struct sockcred *sc; memset(&msg, 0, sizeof msg); crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); if (crmsgsize == 0) goto sc_err; crmsg = malloc(crmsgsize); if (crmsg == NULL) goto sc_err; memset(crmsg, 0, crmsgsize); msg.msg_control = crmsg; msg.msg_controllen = crmsgsize; if (recvmsg(s, &msg, 0) < 0) { free(crmsg); goto sc_err; } if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { free(crmsg); goto sc_err; } cmp = CMSG_FIRSTHDR(&msg); if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { printf("nocreds\n"); goto sc_err; } sc = (struct sockcred *)(void *)CMSG_DATA(cmp); *euid = sc->sc_euid; *egid = sc->sc_egid; free(crmsg); return 0; sc_err: #endif #endif /* LDAP_PF_LOCAL */ return -1; }
int getpeereid( int s, uid_t *euid, gid_t *egid ) { #ifdef LDAP_PF_LOCAL #if defined( SO_PEERCRED ) struct ucred peercred; socklen_t peercredlen = sizeof peercred; if(( getsockopt( s, SOL_SOCKET, SO_PEERCRED, (void *)&peercred, &peercredlen ) == 0 ) && ( peercredlen == sizeof peercred )) { *euid = peercred.uid; *egid = peercred.gid; return 0; } #elif defined( LOCAL_PEERCRED ) struct xucred peercred; socklen_t peercredlen = sizeof peercred; if(( getsockopt( s, LOCAL_PEERCRED, 1, (void *)&peercred, &peercredlen ) == 0 ) && ( peercred.cr_version == XUCRED_VERSION )) { *euid = peercred.cr_uid; *egid = peercred.cr_gid; return 0; } #elif defined( DO_SENDMSG ) char dummy[8]; int err, fd[2]; struct iovec iov; struct msghdr msg = {0}; # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL # ifndef CMSG_SPACE # define CMSG_SPACE(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + _CMSG_ALIGN(len)) # endif # ifndef CMSG_LEN # define CMSG_LEN(len) (_CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) # endif union { struct cmsghdr cm; unsigned char control[CMSG_SPACE(sizeof(int))]; } control_un; struct cmsghdr *cmsg; # endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL */ struct stat st; msg.msg_name = NULL; msg.msg_namelen = 0; iov.iov_base = dummy; iov.iov_len = sizeof dummy; msg.msg_iov = &iov; msg.msg_iovlen = 1; # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL msg.msg_control = control_un.control; msg.msg_controllen = sizeof( control_un.control ); cmsg = CMSG_FIRSTHDR( &msg ); /* * AIX returns a bogus file descriptor if recvmsg() is * called with MSG_PEEK (is this a bug?). Hence we need * to receive the Abandon PDU. */ if( recvmsg( s, &msg, MSG_WAITALL ) >= 0 && cmsg->cmsg_len == CMSG_LEN( sizeof(int) ) && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS ) # else msg.msg_accrights = (char *)fd; msg.msg_accrightslen = sizeof(fd); if( recvmsg( s, &msg, MSG_PEEK) >= 0 && msg.msg_accrightslen == sizeof(int) ) # endif /* HAVE_STRUCT_MSGHDR_MSG_CONTROL*/ { /* We must receive a valid descriptor, it must be a pipe, * and it must only be accessible by its owner. */ # ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL fd[0] = (*(int *)CMSG_DATA( cmsg )); # endif err = fstat( fd[0], &st ); close(fd[0]); if( err == 0 && S_ISFIFO(st.st_mode) && ((st.st_mode & (S_IRWXG|S_IRWXO)) == 0)) { *euid = st.st_uid; *egid = st.st_gid; return 0; } } #elif defined(SOCKCREDSIZE) struct msghdr msg; socklen_t crmsgsize; void *crmsg; struct cmsghdr *cmp; struct sockcred *sc; memset(&msg, 0, sizeof msg); crmsgsize = CMSG_SPACE(SOCKCREDSIZE(NGROUPS)); if (crmsgsize == 0) goto sc_err; crmsg = malloc(crmsgsize); if (crmsg == NULL) goto sc_err; memset(crmsg, 0, crmsgsize); msg.msg_control = crmsg; msg.msg_controllen = crmsgsize; if (recvmsg(s, &msg, 0) < 0) { free(crmsg); goto sc_err; } if (msg.msg_controllen == 0 || (msg.msg_flags & MSG_CTRUNC) != 0) { free(crmsg); goto sc_err; } cmp = CMSG_FIRSTHDR(&msg); if (cmp->cmsg_level != SOL_SOCKET || cmp->cmsg_type != SCM_CREDS) { printf("nocreds\n"); goto sc_err; } sc = (struct sockcred *)(void *)CMSG_DATA(cmp); *euid = sc->sc_euid; *egid = sc->sc_egid; free(crmsg); return 0; sc_err: #endif #endif /* LDAP_PF_LOCAL */ return -1; }
int get_creds( int sd, char *username, char *hostname) { int nb/*, sync*/; char ctrl[CMSG_SPACE(sizeof(struct ucred))]; size_t size; struct iovec iov[1]; struct msghdr msg; struct cmsghdr *cmptr; ucreds *credentials; struct passwd *cpwd; char dummy; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_control = ctrl; msg.msg_controllen = sizeof(ctrl); msg.msg_flags = 0; #ifdef LOCAL_CREDS nb = 1; if (setsockopt(sd, 0, LOCAL_CREDS, &nb, sizeof(nb)) == -1) return 0; #else #ifdef SO_PASSCRED nb = 1; if (setsockopt(sd, SOL_SOCKET, SO_PASSCRED, &nb, sizeof(nb)) == -1) return 0; #endif dummy = '\0'; do { msg.msg_iov->iov_base = (void *) & dummy; msg.msg_iov->iov_len = sizeof(dummy); nb = recvmsg(sd, &msg, 0); } while (nb == -1 && (errno == EINTR || errno == EAGAIN)); if (nb == -1) return 0; if ((unsigned)msg.msg_controllen < sizeof(struct cmsghdr)) return 0; cmptr = CMSG_FIRSTHDR(&msg); #ifndef __NetBSD__ size = sizeof(ucreds); #else if (cmptr->cmsg_len < SOCKCREDSIZE(0)) return(0); size = SOCKCREDSIZE(((cred *)CMSG_DATA(cmptr))->sc_ngroups); #endif if ((unsigned)cmptr->cmsg_len != CMSG_LEN(size)) return(0); if (cmptr->cmsg_level != SOL_SOCKET) return 0; if (cmptr->cmsg_type != SCM_CREDS) return 0; if (!(credentials = (ucreds *)calloc(1, size))) return 0; *credentials = *(ucreds *)CMSG_DATA(cmptr); cpwd = getpwuid(SPC_PEER_UID(credentials)); if (cpwd) strcpy(username, cpwd->pw_name); strcpy(hostname, server_name); free(credentials); return 0; }
static int32_t server_process_request(server_p srv, int32_t fd) { uint8_t ctl[128]; sdp_pdu_p pdu = (sdp_pdu_p) srv->req; struct msghdr msg; struct iovec iov; int32_t len, error; struct cmsghdr *cmsg; assert(srv->imtu > 0); assert(srv->req != NULL); assert(FD_ISSET(fd, &srv->fdset)); assert(srv->fdidx[fd].valid); assert(!srv->fdidx[fd].server); assert(srv->fdidx[fd].rsp != NULL); assert(srv->fdidx[fd].omtu >= L2CAP_MTU_MINIMUM); iov.iov_base = srv->req; iov.iov_len = srv->imtu; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = ctl; msg.msg_controllen = sizeof(ctl); msg.msg_flags = 0; do { len = recvmsg(fd, &msg, 0); } while (len < 0 && errno == EINTR); if (len < 0) { log_err("Could not receive SDP request from %s socket. %s (%d)", srv->fdidx[fd].control? "control" : "L2CAP", strerror(errno), errno); return (-1); } if (len == 0) { log_info("Client on %s socket has disconnected", srv->fdidx[fd].control? "control" : "L2CAP"); return (-1); } #if XXX if ((cmsg = CMSG_FIRSTHDR(&msg)) != NULL && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS #if 0 && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0)) #endif ) srv->fdidx[fd].priv = server_auth_check(srv, (struct cmsgcred *)CMSG_DATA(cmsg)); #if 0 server_auth_check(srv, (struct sockcred *)CMSG_DATA(cmsg)); #endif #else srv->fdidx[fd].priv = 1; #endif if (len >= sizeof(*pdu) && (sizeof(*pdu) + (pdu->len = ntohs(pdu->len))) == len) { switch (pdu->pid) { case SDP_PDU_SERVICE_SEARCH_REQUEST: error = server_prepare_service_search_response(srv, fd); break; case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: error = server_prepare_service_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: error = server_prepare_service_search_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_REGISTER_REQUEST: error = server_prepare_service_register_response(srv, fd); break; case SDP_PDU_SERVICE_UNREGISTER_REQUEST: error = server_prepare_service_unregister_response(srv, fd); break; case SDP_PDU_SERVICE_CHANGE_REQUEST: error = server_prepare_service_change_response(srv, fd); break; default: error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; break; } } else error = SDP_ERROR_CODE_INVALID_PDU_SIZE; if (error == 0) { switch (pdu->pid) { case SDP_PDU_SERVICE_SEARCH_REQUEST: error = server_send_service_search_response(srv, fd); break; case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: error = server_send_service_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: error = server_send_service_search_attribute_response(srv, fd); break; case SDP_PDU_SERVICE_REGISTER_REQUEST: error = server_send_service_register_response(srv, fd); break; case SDP_PDU_SERVICE_UNREGISTER_REQUEST: error = server_send_service_unregister_response(srv, fd); break; case SDP_PDU_SERVICE_CHANGE_REQUEST: error = server_send_service_change_response(srv, fd); break; default: error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; break; } if (error != 0) log_err("Could not send SDP response to %s socket, " \ "pdu->pid=%d, pdu->tid=%d, error=%d", srv->fdidx[fd].control? "control" : "L2CAP", pdu->pid, ntohs(pdu->tid), error); } else { log_err("Could not process SDP request from %s socket, " \ "pdu->pid=%d, pdu->tid=%d, pdu->len=%d, len=%d, " \ "error=%d", srv->fdidx[fd].control? "control" : "L2CAP", pdu->pid, ntohs(pdu->tid), pdu->len, len, error); error = server_send_error_response(srv, fd, error); if (error != 0) log_err("Could not send SDP error response to %s " \ "socket, pdu->pid=%d, pdu->tid=%d, error=%d", srv->fdidx[fd].control? "control" : "L2CAP", pdu->pid, ntohs(pdu->tid), error); } /* On error forget response (if any) */ if (error != 0) { srv->fdidx[fd].rsp_cs = 0; srv->fdidx[fd].rsp_size = 0; srv->fdidx[fd].rsp_limit = 0; } return (error); }
/* * Process request from the client */ static bool server_process_request(server_t *srv, int fd) { struct msghdr msg; struct iovec iov[2]; struct cmsghdr *cmsg; ssize_t len; uint16_t error; assert(FD_ISSET(fd, &srv->fdset)); assert(srv->fdidx[fd].valid); assert(!srv->fdidx[fd].server); iov[0].iov_base = &srv->pdu; iov[0].iov_len = sizeof(srv->pdu); iov[1].iov_base = srv->ibuf; iov[1].iov_len = srv->imtu; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = __arraycount(iov); msg.msg_control = srv->ctlbuf; msg.msg_controllen = srv->ctllen; msg.msg_flags = 0; do { len = recvmsg(fd, &msg, 0); } while (len == -1 && errno == EINTR); if (len == -1) { log_err("Could not receive SDP request on %s socket. %s (%d)", srv->fdidx[fd].control ? "control" : "L2CAP", strerror(errno), errno); return false; } if (len == 0) { log_info("Client on %s socket has disconnected", srv->fdidx[fd].control ? "control" : "L2CAP"); return false; } if (msg.msg_flags & MSG_TRUNC) log_info("Truncated message on %s socket", srv->fdidx[fd].control ? "control" : "L2CAP"); if ((cmsg = CMSG_FIRSTHDR(&msg)) != NULL && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDS && cmsg->cmsg_len >= CMSG_LEN(SOCKCREDSIZE(0))) srv->fdidx[fd].priv = server_auth_check(srv, CMSG_DATA(cmsg)); srv->pdu.len = be16toh(srv->pdu.len); if ((uint32_t)len < sizeof(srv->pdu) || (uint32_t)len != sizeof(srv->pdu) + srv->pdu.len) { error = SDP_ERROR_CODE_INVALID_PDU_SIZE; } else { switch (srv->pdu.pid) { case SDP_PDU_SERVICE_SEARCH_REQUEST: error = service_search_request(srv, fd); break; case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST: error = service_attribute_request(srv, fd); break; case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST: error = service_search_attribute_request(srv, fd); break; #ifdef SDP_COMPAT case SDP_PDU_SERVICE_REGISTER_REQUEST: error = compat_register_request(srv, fd); break; case SDP_PDU_SERVICE_CHANGE_REQUEST: error = compat_change_request(srv, fd); break; #endif case SDP_PDU_RECORD_INSERT_REQUEST: error = record_insert_request(srv, fd); break; case SDP_PDU_RECORD_UPDATE_REQUEST: error = record_update_request(srv, fd); break; case SDP_PDU_RECORD_REMOVE_REQUEST: error = record_remove_request(srv, fd); break; default: error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX; break; } } if (error != 0) { srv->fdidx[fd].offset = 0; db_unselect(srv, fd); srv->pdu.pid = SDP_PDU_ERROR_RESPONSE; srv->pdu.len = sizeof(error); be16enc(srv->obuf, error); log_debug("sending ErrorResponse (error=0x%04x)", error); } iov[0].iov_base = &srv->pdu; iov[0].iov_len = sizeof(srv->pdu); iov[1].iov_base = srv->obuf; iov[1].iov_len = srv->pdu.len; srv->pdu.len = htobe16(srv->pdu.len); msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_iov = iov; msg.msg_iovlen = __arraycount(iov); msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; do { len = sendmsg(fd, &msg, 0); } while (len == -1 && errno == EINTR); if (len == -1) { log_err("Could not send SDP response on %s socket. %s (%d)", srv->fdidx[fd].control ? "control" : "L2CAP", strerror(errno), errno); return false; } return true; }