Example #1
0
/*
 * 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;
}
Example #2
0
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);
}
Example #3
0
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;
}
Example #4
0
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;
  }
Example #6
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);
}
Example #7
0
/*
 * 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;
}