2014-12-30 16:36:46 +03:00
/*
* Unix SMB / CIFS implementation .
* Copyright ( C ) Volker Lendecke 2014
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "replace.h"
# include "lib/msghdr.h"
2015-02-14 18:48:54 +03:00
# include "lib/util/iov_buf.h"
2014-12-30 16:36:46 +03:00
# include <sys/socket.h>
2015-09-27 01:54:42 +03:00
# if defined(HAVE_STRUCT_MSGHDR_MSG_CONTROL)
2014-12-30 16:36:46 +03:00
ssize_t msghdr_prep_fds ( struct msghdr * msg , uint8_t * buf , size_t bufsize ,
const int * fds , size_t num_fds )
{
size_t fds_size = sizeof ( int ) * MIN ( num_fds , INT8_MAX ) ;
size_t cmsg_len = CMSG_LEN ( fds_size ) ;
size_t cmsg_space = CMSG_SPACE ( fds_size ) ;
struct cmsghdr * cmsg ;
void * fdptr ;
if ( num_fds = = 0 ) {
if ( msg ! = NULL ) {
msg - > msg_control = NULL ;
msg - > msg_controllen = 0 ;
}
return 0 ;
}
if ( num_fds > INT8_MAX ) {
return - 1 ;
}
2015-01-07 02:56:16 +03:00
if ( ( msg = = NULL ) | | ( cmsg_space > bufsize ) ) {
2014-12-30 16:36:46 +03:00
return cmsg_space ;
}
msg - > msg_control = buf ;
msg - > msg_controllen = cmsg_space ;
cmsg = CMSG_FIRSTHDR ( msg ) ;
cmsg - > cmsg_level = SOL_SOCKET ;
cmsg - > cmsg_type = SCM_RIGHTS ;
cmsg - > cmsg_len = cmsg_len ;
fdptr = CMSG_DATA ( cmsg ) ;
memcpy ( fdptr , fds , fds_size ) ;
msg - > msg_controllen = cmsg - > cmsg_len ;
return cmsg_space ;
}
2015-09-27 01:46:33 +03:00
size_t msghdr_prep_recv_fds ( struct msghdr * msg , uint8_t * buf , size_t bufsize ,
size_t num_fds )
{
size_t ret = CMSG_SPACE ( sizeof ( int ) * num_fds ) ;
if ( bufsize < ret ) {
return ret ;
}
if ( msg ! = NULL ) {
if ( num_fds ! = 0 ) {
msg - > msg_control = buf ;
msg - > msg_controllen = ret ;
} else {
msg - > msg_control = NULL ;
msg - > msg_controllen = 0 ;
}
}
return ret ;
}
size_t msghdr_extract_fds ( struct msghdr * msg , int * fds , size_t fds_size )
{
struct cmsghdr * cmsg ;
size_t num_fds ;
for ( cmsg = CMSG_FIRSTHDR ( msg ) ;
cmsg ! = NULL ;
cmsg = CMSG_NXTHDR ( msg , cmsg ) )
{
if ( ( cmsg - > cmsg_type = = SCM_RIGHTS ) & &
( cmsg - > cmsg_level = = SOL_SOCKET ) ) {
break ;
}
}
if ( cmsg = = NULL ) {
return 0 ;
}
num_fds = ( cmsg - > cmsg_len - CMSG_LEN ( 0 ) ) / sizeof ( int ) ;
if ( ( num_fds ! = 0 ) & & ( fds ! = NULL ) & & ( fds_size > = num_fds ) ) {
memcpy ( fds , CMSG_DATA ( cmsg ) , num_fds * sizeof ( int ) ) ;
}
return num_fds ;
}
2015-09-27 01:54:42 +03:00
# elif defined(HAVE_STRUCT_MSGHDR_MSG_ACCRIGHTS)
ssize_t msghdr_prep_fds ( struct msghdr * msg , uint8_t * buf , size_t bufsize ,
const int * fds , size_t num_fds )
{
size_t needed ;
if ( num_fds > INT8_MAX ) {
return - 1 ;
}
needed = sizeof ( int ) * num_fds ;
if ( ( msg = = NULL ) | | ( needed > bufsize ) ) {
return needed ;
}
memcpy ( buf , fds , needed ) ;
msg - > msg_accrights = ( caddr_t ) buf ;
msg - > msg_accrightslen = needed ;
return needed ;
}
size_t msghdr_prep_recv_fds ( struct msghdr * msg , uint8_t * buf , size_t bufsize ,
size_t num_fds )
{
size_t ret = num_fds * sizeof ( int ) ;
if ( bufsize < ret ) {
return ret ;
}
if ( msg ! = NULL ) {
if ( num_fds ! = 0 ) {
msg - > msg_accrights = ( caddr_t ) buf ;
msg - > msg_accrightslen = ret ;
} else {
msg - > msg_accrights = NULL ;
msg - > msg_accrightslen = 0 ;
}
}
return ret ;
}
size_t msghdr_extract_fds ( struct msghdr * msg , int * fds , size_t fds_size )
{
size_t num_fds = msg - > msg_accrightslen / sizeof ( int ) ;
if ( ( fds ! = 0 ) & & ( num_fds < = fds_size ) ) {
memcpy ( fds , msg - > msg_accrights , msg - > msg_accrightslen ) ;
}
return num_fds ;
}
# else
ssize_t msghdr_prep_fds ( struct msghdr * msg , uint8_t * buf , size_t bufsize ,
const int * fds , size_t num_fds )
{
return - 1 ;
}
size_t msghdr_prep_recv_fds ( struct msghdr * msg , uint8_t * buf , size_t bufsize ,
size_t num_fds )
{
return 0 ;
}
size_t msghdr_extract_fds ( struct msghdr * msg , int * fds , size_t fds_size )
{
return 0 ;
}
# endif
2014-12-30 16:36:46 +03:00
struct msghdr_buf {
struct msghdr msg ;
struct sockaddr_storage addr ;
struct iovec iov ;
uint8_t buf [ ] ;
} ;
ssize_t msghdr_copy ( struct msghdr_buf * msg , size_t msgsize ,
const void * addr , socklen_t addrlen ,
const struct iovec * iov , int iovcnt ,
const int * fds , size_t num_fds )
{
2015-03-04 11:49:18 +03:00
ssize_t fd_len ;
size_t iov_len , needed , bufsize ;
2014-12-30 16:36:46 +03:00
bufsize = ( msgsize > offsetof ( struct msghdr_buf , buf ) ) ?
msgsize - offsetof ( struct msghdr_buf , buf ) : 0 ;
fd_len = msghdr_prep_fds ( & msg - > msg , msg - > buf , bufsize , fds , num_fds ) ;
2015-03-04 11:49:18 +03:00
if ( fd_len = = - 1 ) {
return - 1 ;
}
2014-12-30 16:36:46 +03:00
if ( bufsize > = fd_len ) {
bufsize - = fd_len ;
} else {
bufsize = 0 ;
}
if ( msg ! = NULL ) {
if ( addr ! = NULL ) {
if ( addrlen > sizeof ( struct sockaddr_storage ) ) {
errno = EMSGSIZE ;
return - 1 ;
}
memcpy ( & msg - > addr , addr , addrlen ) ;
msg - > msg . msg_name = & msg - > addr ;
msg - > msg . msg_namelen = addrlen ;
} else {
msg - > msg . msg_name = NULL ;
msg - > msg . msg_namelen = 0 ;
}
msg - > iov . iov_base = msg - > buf + fd_len ;
msg - > iov . iov_len = iov_buf (
iov , iovcnt , msg - > iov . iov_base , bufsize ) ;
iov_len = msg - > iov . iov_len ;
msg - > msg . msg_iov = & msg - > iov ;
msg - > msg . msg_iovlen = 1 ;
} else {
iov_len = iov_buflen ( iov , iovcnt ) ;
}
needed = offsetof ( struct msghdr_buf , buf ) + fd_len ;
if ( needed < fd_len ) {
return - 1 ;
}
needed + = iov_len ;
if ( needed < iov_len ) {
return - 1 ;
}
return needed ;
}
struct msghdr * msghdr_buf_msghdr ( struct msghdr_buf * msg )
{
return & msg - > msg ;
}