2008-08-08 20:30:57 +04:00
/*
Unix SMB / CIFS implementation .
async socket syscalls
Copyright ( C ) Volker Lendecke 2008
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 "includes.h"
2009-01-24 12:00:13 +03:00
# include "lib/talloc/talloc.h"
# include "lib/tevent/tevent.h"
# include "lib/async_req/async_req.h"
# include "lib/async_req/async_sock.h"
2009-02-22 21:49:18 +03:00
# include "lib/util/tevent_unix.h"
2009-01-24 12:00:13 +03:00
# include <fcntl.h>
# ifndef TALLOC_FREE
# define TALLOC_FREE(ctx) do { talloc_free(ctx); ctx=NULL; } while(0)
# endif
2008-08-08 20:30:57 +04:00
2009-02-04 11:07:36 +03:00
/**
* @ brief Map async_req states to unix - style errnos
* @ param [ in ] req The async req to get the state from
* @ param [ out ] err Pointer to take the unix - style errno
*
* @ return true if the async_req is in an error state , false otherwise
*/
bool async_req_is_errno ( struct async_req * req , int * err )
{
enum async_req_state state ;
uint64_t error ;
if ( ! async_req_is_error ( req , & state , & error ) ) {
return false ;
}
switch ( state ) {
case ASYNC_REQ_USER_ERROR :
* err = ( int ) error ;
break ;
case ASYNC_REQ_TIMED_OUT :
2009-02-09 20:36:40 +03:00
# ifdef ETIMEDOUT
2009-02-09 14:11:43 +03:00
* err = ETIMEDOUT ;
# else
2009-02-09 20:36:40 +03:00
* err = EAGAIN ;
2009-02-09 14:11:43 +03:00
# endif
2009-02-04 11:07:36 +03:00
break ;
case ASYNC_REQ_NO_MEMORY :
* err = ENOMEM ;
break ;
default :
* err = EIO ;
break ;
}
return true ;
}
int async_req_simple_recv_errno ( struct async_req * req )
{
int err ;
if ( async_req_is_errno ( req , & err ) ) {
return err ;
}
return 0 ;
}
2009-02-23 10:53:01 +03:00
struct async_send_state {
int fd ;
const void * buf ;
size_t len ;
int flags ;
ssize_t sent ;
} ;
static void async_send_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * private_data ) ;
struct tevent_req * async_send_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
int fd , const void * buf , size_t len ,
int flags )
{
struct tevent_req * result ;
struct async_send_state * state ;
struct tevent_fd * fde ;
result = tevent_req_create ( mem_ctx , & state , struct async_send_state ) ;
if ( result = = NULL ) {
return result ;
}
state - > fd = fd ;
state - > buf = buf ;
state - > len = len ;
state - > flags = flags ;
fde = tevent_add_fd ( ev , state , fd , TEVENT_FD_WRITE , async_send_handler ,
result ) ;
if ( fde = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
return result ;
}
static void async_send_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * private_data )
{
struct tevent_req * req = talloc_get_type_abort (
private_data , struct tevent_req ) ;
struct async_send_state * state = talloc_get_type_abort (
req - > private_state , struct async_send_state ) ;
state - > sent = send ( state - > fd , state - > buf , state - > len , state - > flags ) ;
if ( state - > sent = = - 1 ) {
tevent_req_error ( req , errno ) ;
return ;
}
tevent_req_done ( req ) ;
}
ssize_t async_send_recv ( struct tevent_req * req , int * perrno )
{
struct async_send_state * state = talloc_get_type_abort (
req - > private_state , struct async_send_state ) ;
if ( tevent_req_is_unix_error ( req , perrno ) ) {
return - 1 ;
}
return state - > sent ;
}
2009-02-23 10:56:35 +03:00
struct async_recv_state {
int fd ;
void * buf ;
size_t len ;
int flags ;
ssize_t received ;
} ;
static void async_recv_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * private_data ) ;
struct tevent_req * async_recv_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
int fd , void * buf , size_t len , int flags )
{
struct tevent_req * result ;
struct async_recv_state * state ;
struct tevent_fd * fde ;
result = tevent_req_create ( mem_ctx , & state , struct async_recv_state ) ;
if ( result = = NULL ) {
return result ;
}
state - > fd = fd ;
state - > buf = buf ;
state - > len = len ;
state - > flags = flags ;
fde = tevent_add_fd ( ev , state , fd , TEVENT_FD_READ , async_recv_handler ,
result ) ;
if ( fde = = NULL ) {
TALLOC_FREE ( result ) ;
return NULL ;
}
return result ;
}
static void async_recv_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * private_data )
{
struct tevent_req * req = talloc_get_type_abort (
private_data , struct tevent_req ) ;
struct async_recv_state * state = talloc_get_type_abort (
req - > private_state , struct async_recv_state ) ;
state - > received = recv ( state - > fd , state - > buf , state - > len ,
state - > flags ) ;
if ( state - > received = = - 1 ) {
tevent_req_error ( req , errno ) ;
return ;
}
tevent_req_done ( req ) ;
}
ssize_t async_recv_recv ( struct tevent_req * req , int * perrno )
{
struct async_recv_state * state = talloc_get_type_abort (
req - > private_state , struct async_recv_state ) ;
if ( tevent_req_is_unix_error ( req , perrno ) ) {
return - 1 ;
}
return state - > received ;
}
2009-01-04 02:26:49 +03:00
struct async_connect_state {
int fd ;
int result ;
int sys_errno ;
long old_sockflags ;
} ;
2008-08-08 20:30:57 +04:00
2009-01-24 12:00:13 +03:00
static void async_connect_connected ( struct tevent_context * ev ,
struct tevent_fd * fde , uint16_t flags ,
2009-01-04 02:26:49 +03:00
void * priv ) ;
2008-08-08 20:30:57 +04:00
/**
* @ brief async version of connect ( 2 )
* @ param [ in ] mem_ctx The memory context to hang the result off
* @ param [ in ] ev The event context to work from
* @ param [ in ] fd The socket to recv from
* @ param [ in ] address Where to connect ?
* @ param [ in ] address_len Length of * address
* @ retval The async request
*
* This function sets the socket into non - blocking state to be able to call
* connect in an async state . This will be reset when the request is finished .
*/
2009-02-22 21:49:18 +03:00
struct tevent_req * async_connect_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
int fd , const struct sockaddr * address ,
socklen_t address_len )
2008-08-08 20:30:57 +04:00
{
2009-02-22 21:49:18 +03:00
struct tevent_req * result ;
2009-01-04 02:26:49 +03:00
struct async_connect_state * state ;
2009-01-24 12:00:13 +03:00
struct tevent_fd * fde ;
2008-08-08 20:30:57 +04:00
2009-02-22 21:49:18 +03:00
result = tevent_req_create (
mem_ctx , & state , struct async_connect_state ) ;
if ( result = = NULL ) {
2008-08-08 20:30:57 +04:00
return NULL ;
}
/**
* We have to set the socket to nonblocking for async connect ( 2 ) . Keep
* the old sockflags around .
*/
2009-01-04 02:26:49 +03:00
state - > fd = fd ;
state - > sys_errno = 0 ;
2008-08-08 20:30:57 +04:00
2009-01-24 12:00:13 +03:00
state - > old_sockflags = fcntl ( fd , F_GETFL , 0 ) ;
2009-01-04 02:26:49 +03:00
if ( state - > old_sockflags = = - 1 ) {
goto post_errno ;
2008-08-08 20:30:57 +04:00
}
2009-01-03 21:23:13 +03:00
set_blocking ( fd , false ) ;
2008-08-08 20:30:57 +04:00
2009-01-04 02:26:49 +03:00
state - > result = connect ( fd , address , address_len ) ;
if ( state - > result = = 0 ) {
2009-02-22 21:49:18 +03:00
errno = 0 ;
goto post_errno ;
2008-08-08 20:30:57 +04:00
}
/**
* A number of error messages show that something good is progressing
* and that we have to wait for readability .
*
* If none of them are present , bail out .
*/
if ( ! ( errno = = EINPROGRESS | | errno = = EALREADY | |
# ifdef EISCONN
errno = = EISCONN | |
# endif
errno = = EAGAIN | | errno = = EINTR ) ) {
2009-01-04 02:26:49 +03:00
goto post_errno ;
}
2008-08-08 20:30:57 +04:00
2009-01-24 12:00:13 +03:00
fde = tevent_add_fd ( ev , state , fd , TEVENT_FD_READ | TEVENT_FD_WRITE ,
2009-01-04 02:26:49 +03:00
async_connect_connected , result ) ;
if ( fde = = NULL ) {
2009-02-22 21:49:18 +03:00
errno = ENOMEM ;
goto post_errno ;
2008-08-08 20:30:57 +04:00
}
2009-01-04 02:26:49 +03:00
return result ;
2008-08-08 20:30:57 +04:00
2009-01-04 02:26:49 +03:00
post_errno :
state - > sys_errno = errno ;
2009-01-24 12:00:13 +03:00
fcntl ( fd , F_SETFL , state - > old_sockflags ) ;
2009-02-22 21:49:18 +03:00
if ( state - > sys_errno = = 0 ) {
tevent_req_done ( result ) ;
} else {
tevent_req_error ( result , state - > sys_errno ) ;
2008-08-08 20:30:57 +04:00
}
2009-02-22 21:49:18 +03:00
return tevent_req_post ( result , ev ) ;
2009-01-04 02:26:49 +03:00
}
2008-08-08 20:30:57 +04:00
2009-01-04 02:26:49 +03:00
/**
* fde event handler for connect ( 2 )
* @ param [ in ] ev The event context that sent us here
* @ param [ in ] fde The file descriptor event associated with the connect
* @ param [ in ] flags Indicate read / writeability of the socket
* @ param [ in ] priv private data , " struct async_req * " in this case
*/
2008-08-08 20:30:57 +04:00
2009-01-24 12:00:13 +03:00
static void async_connect_connected ( struct tevent_context * ev ,
struct tevent_fd * fde , uint16_t flags ,
2009-01-04 02:26:49 +03:00
void * priv )
{
2009-02-22 21:49:18 +03:00
struct tevent_req * req = talloc_get_type_abort (
priv , struct tevent_req ) ;
2009-01-04 02:26:49 +03:00
struct async_connect_state * state = talloc_get_type_abort (
2009-02-22 21:49:18 +03:00
req - > private_state , struct async_connect_state ) ;
2009-01-04 02:26:49 +03:00
TALLOC_FREE ( fde ) ;
/*
* Stevens , Network Programming says that if there ' s a
* successful connect , the socket is only writable . Upon an
* error , it ' s both readable and writable .
*/
2009-01-24 12:00:13 +03:00
if ( ( flags & ( TEVENT_FD_READ | TEVENT_FD_WRITE ) )
= = ( TEVENT_FD_READ | TEVENT_FD_WRITE ) ) {
2009-01-04 02:26:49 +03:00
int sockerr ;
socklen_t err_len = sizeof ( sockerr ) ;
if ( getsockopt ( state - > fd , SOL_SOCKET , SO_ERROR ,
( void * ) & sockerr , & err_len ) = = 0 ) {
errno = sockerr ;
}
state - > sys_errno = errno ;
DEBUG ( 10 , ( " connect returned %s \n " , strerror ( errno ) ) ) ;
2009-01-24 12:00:13 +03:00
fcntl ( state - > fd , F_SETFL , state - > old_sockflags ) ;
2009-02-22 21:49:18 +03:00
tevent_req_error ( req , state - > sys_errno ) ;
2009-01-04 02:26:49 +03:00
return ;
}
state - > sys_errno = 0 ;
2009-02-22 21:49:18 +03:00
tevent_req_done ( req ) ;
2008-08-08 20:30:57 +04:00
}
2009-02-22 21:49:18 +03:00
int async_connect_recv ( struct tevent_req * req , int * perrno )
2009-01-04 02:26:49 +03:00
{
struct async_connect_state * state = talloc_get_type_abort (
2009-02-22 21:49:18 +03:00
req - > private_state , struct async_connect_state ) ;
2009-02-04 11:07:36 +03:00
int err ;
2009-01-04 02:26:49 +03:00
2009-01-24 12:00:13 +03:00
fcntl ( state - > fd , F_SETFL , state - > old_sockflags ) ;
2009-01-04 02:26:49 +03:00
2009-02-22 21:49:18 +03:00
if ( tevent_req_is_unix_error ( req , & err ) ) {
2009-02-04 11:07:36 +03:00
* perrno = err ;
return - 1 ;
2009-01-04 02:26:49 +03:00
}
2009-02-22 21:49:18 +03:00
2009-01-04 02:26:49 +03:00
if ( state - > sys_errno = = 0 ) {
2009-02-04 11:07:36 +03:00
return 0 ;
2009-01-04 02:26:49 +03:00
}
2009-02-04 11:07:36 +03:00
* perrno = state - > sys_errno ;
return - 1 ;
2009-01-04 02:26:49 +03:00
}
2009-02-22 22:16:32 +03:00
struct writev_state {
struct tevent_context * ev ;
int fd ;
struct iovec * iov ;
int count ;
size_t total_size ;
} ;
static void writev_handler ( struct tevent_context * ev , struct tevent_fd * fde ,
uint16_t flags , void * private_data ) ;
struct tevent_req * writev_send ( TALLOC_CTX * mem_ctx , struct tevent_context * ev ,
int fd , struct iovec * iov , int count )
{
struct tevent_req * result ;
struct writev_state * state ;
struct tevent_fd * fde ;
result = tevent_req_create ( mem_ctx , & state , struct writev_state ) ;
if ( result = = NULL ) {
return NULL ;
}
state - > ev = ev ;
state - > fd = fd ;
state - > total_size = 0 ;
state - > count = count ;
state - > iov = ( struct iovec * ) talloc_memdup (
state , iov , sizeof ( struct iovec ) * count ) ;
if ( state - > iov = = NULL ) {
goto fail ;
}
fde = tevent_add_fd ( ev , state , fd , TEVENT_FD_WRITE , writev_handler ,
result ) ;
if ( fde = = NULL ) {
goto fail ;
}
return result ;
fail :
TALLOC_FREE ( result ) ;
return NULL ;
}
static void writev_handler ( struct tevent_context * ev , struct tevent_fd * fde ,
uint16_t flags , void * private_data )
{
struct tevent_req * req = talloc_get_type_abort (
private_data , struct tevent_req ) ;
struct writev_state * state = talloc_get_type_abort (
req - > private_state , struct writev_state ) ;
size_t to_write , written ;
int i ;
to_write = 0 ;
for ( i = 0 ; i < state - > count ; i + + ) {
to_write + = state - > iov [ i ] . iov_len ;
}
written = sys_writev ( state - > fd , state - > iov , state - > count ) ;
if ( written = = - 1 ) {
tevent_req_error ( req , errno ) ;
return ;
}
if ( written = = 0 ) {
2009-02-23 01:12:56 +03:00
tevent_req_error ( req , EPIPE ) ;
2009-02-22 22:16:32 +03:00
return ;
}
state - > total_size + = written ;
if ( written = = to_write ) {
tevent_req_done ( req ) ;
return ;
}
/*
* We ' ve written less than we were asked to , drop stuff from
* state - > iov .
*/
while ( written > 0 ) {
if ( written < state - > iov [ 0 ] . iov_len ) {
state - > iov [ 0 ] . iov_base =
( char * ) state - > iov [ 0 ] . iov_base + written ;
state - > iov [ 0 ] . iov_len - = written ;
break ;
}
written = state - > iov [ 0 ] . iov_len ;
state - > iov + = 1 ;
state - > count - = 1 ;
}
}
ssize_t writev_recv ( struct tevent_req * req , int * perrno )
{
struct writev_state * state = talloc_get_type_abort (
req - > private_state , struct writev_state ) ;
if ( tevent_req_is_unix_error ( req , perrno ) ) {
return - 1 ;
}
return state - > total_size ;
}
2009-02-23 01:13:34 +03:00
struct read_packet_state {
int fd ;
uint8_t * buf ;
size_t nread ;
ssize_t ( * more ) ( uint8_t * buf , size_t buflen , void * private_data ) ;
void * private_data ;
} ;
static void read_packet_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * private_data ) ;
struct tevent_req * read_packet_send ( TALLOC_CTX * mem_ctx ,
struct tevent_context * ev ,
int fd , size_t initial ,
ssize_t ( * more ) ( uint8_t * buf ,
size_t buflen ,
void * private_data ) ,
void * private_data )
{
struct tevent_req * result ;
struct read_packet_state * state ;
struct tevent_fd * fde ;
result = tevent_req_create ( mem_ctx , & state , struct read_packet_state ) ;
if ( result = = NULL ) {
return NULL ;
}
state - > fd = fd ;
state - > nread = 0 ;
state - > more = more ;
state - > private_data = private_data ;
state - > buf = talloc_array ( state , uint8_t , initial ) ;
if ( state - > buf = = NULL ) {
goto fail ;
}
fde = tevent_add_fd ( ev , state , fd , TEVENT_FD_READ , read_packet_handler ,
result ) ;
if ( fde = = NULL ) {
goto fail ;
}
return result ;
fail :
TALLOC_FREE ( result ) ;
return NULL ;
}
static void read_packet_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags , void * private_data )
{
struct tevent_req * req = talloc_get_type_abort (
private_data , struct tevent_req ) ;
struct read_packet_state * state = talloc_get_type_abort (
req - > private_state , struct read_packet_state ) ;
size_t total = talloc_get_size ( state - > buf ) ;
ssize_t nread , more ;
uint8_t * tmp ;
nread = read ( state - > fd , state - > buf + state - > nread , total - state - > nread ) ;
if ( nread = = - 1 ) {
tevent_req_error ( req , errno ) ;
return ;
}
if ( nread = = 0 ) {
tevent_req_error ( req , EPIPE ) ;
return ;
}
state - > nread + = nread ;
if ( state - > nread < total ) {
/* Come back later */
return ;
}
/*
* We got what was initially requested . See if " more " asks for - - more .
*/
if ( state - > more = = NULL ) {
/* Nobody to ask, this is a async read_data */
tevent_req_done ( req ) ;
return ;
}
more = state - > more ( state - > buf , total , state - > private_data ) ;
if ( more = = - 1 ) {
/* We got an invalid packet, tell the caller */
tevent_req_error ( req , EIO ) ;
return ;
}
if ( more = = 0 ) {
/* We're done, full packet received */
tevent_req_done ( req ) ;
return ;
}
tmp = TALLOC_REALLOC_ARRAY ( state , state - > buf , uint8_t , total + more ) ;
if ( tevent_req_nomem ( tmp , req ) ) {
return ;
}
state - > buf = tmp ;
}
ssize_t read_packet_recv ( struct tevent_req * req , TALLOC_CTX * mem_ctx ,
uint8_t * * pbuf , int * perrno )
{
struct read_packet_state * state = talloc_get_type_abort (
req - > private_state , struct read_packet_state ) ;
if ( tevent_req_is_unix_error ( req , perrno ) ) {
return - 1 ;
}
* pbuf = talloc_move ( mem_ctx , & state - > buf ) ;
return talloc_get_size ( * pbuf ) ;
}