2020-02-21 04:21:16 +00:00
/*
* $ Id $
*
Copyright ( c ) 2016 - 2020 Chung , Hyung - Hwan . All rights reserved .
Redistribution and use in source and binary forms , with or without
modification , are permitted provided that the following conditions
are met :
1. Redistributions of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions in binary form must reproduce the above copyright
notice , this list of conditions and the following disclaimer in the
documentation and / or other materials provided with the distribution .
THIS SOFTWARE IS PROVIDED BY THE AUTHOR " AS IS " AND ANY EXPRESS OR
IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WAfRRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED .
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE ,
DATA , OR PROFITS ; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*/
# include <mio-dns.h>
# include <mio-sck.h>
# include "mio-prv.h"
# include <netinet/in.h>
struct mio_svc_dns_t
{
MIO_SVC_HEADERS ;
/*MIO_DNS_SVC_HEADERS;*/
} ;
struct mio_svc_dnc_t
{
MIO_SVC_HEADERS ;
/*MIO_DNS_SVC_HEADERS;*/
mio_dev_sck_t * udp_sck ;
mio_dev_sck_t * tcp_sck ;
mio_skad_t serv_addr ;
mio_ntime_t send_tmout ;
mio_ntime_t reply_tmout ; /* default reply timeout */
mio_oow_t reply_tmout_max_tries ;
mio_oow_t seq ;
mio_dns_msg_t * pending_req ;
} ;
struct dnc_sck_xtn_t
{
mio_svc_dnc_t * dnc ;
} ;
typedef struct dnc_sck_xtn_t dnc_sck_xtn_t ;
/* ----------------------------------------------------------------------- */
struct dnc_dns_msg_xtn_t
{
2020-02-28 07:41:25 +00:00
mio_dev_sck_t * dev ;
2020-02-21 04:21:16 +00:00
mio_tmridx_t rtmridx ;
mio_dns_msg_t * prev ;
mio_dns_msg_t * next ;
mio_skad_t servaddr ;
2020-02-27 17:34:27 +00:00
mio_svc_dnc_on_done_t on_done ;
2020-02-21 04:21:16 +00:00
mio_ntime_t wtmout ;
mio_ntime_t rtmout ;
int rmaxtries ; /* maximum number of tries to receive a reply */
int rtries ; /* number of tries made so far */
} ;
typedef struct dnc_dns_msg_xtn_t dnc_dns_msg_xtn_t ;
# if defined(MIO_HAVE_INLINE)
static MIO_INLINE dnc_dns_msg_xtn_t * dnc_dns_msg_getxtn ( mio_dns_msg_t * msg ) { return ( dnc_dns_msg_xtn_t * ) ( ( mio_uint8_t * ) mio_dns_msg_to_pkt ( msg ) + msg - > pktalilen ) ; }
# else
# define dnc_dns_msg_getxtn(msg) ((dnc_dns_msg_xtn_t*)((mio_uint8_t*)mio_dns_msg_to_pkt(msg) + msg->pktalilen))
# endif
static MIO_INLINE void chain_pending_dns_reqmsg ( mio_svc_dnc_t * dnc , mio_dns_msg_t * msg )
{
if ( dnc - > pending_req )
{
dnc_dns_msg_getxtn ( dnc - > pending_req ) - > prev = msg ;
dnc_dns_msg_getxtn ( msg ) - > next = dnc - > pending_req ;
}
dnc - > pending_req = msg ;
}
static MIO_INLINE void unchain_pending_dns_reqmsg ( mio_svc_dnc_t * dnc , mio_dns_msg_t * msg )
{
dnc_dns_msg_xtn_t * msgxtn = dnc_dns_msg_getxtn ( msg ) ;
if ( msgxtn - > next ) dnc_dns_msg_getxtn ( msgxtn - > next ) - > prev = msgxtn - > prev ;
if ( msgxtn - > prev ) dnc_dns_msg_getxtn ( msgxtn - > prev ) - > next = msgxtn - > next ;
else dnc - > pending_req = msgxtn - > next ;
}
static void release_dns_msg ( mio_svc_dnc_t * dnc , mio_dns_msg_t * msg )
{
mio_t * mio = dnc - > mio ;
dnc_dns_msg_xtn_t * msgxtn = dnc_dns_msg_getxtn ( msg ) ;
MIO_DEBUG1 ( mio , " releasing dns msg %d \n " , ( int ) mio_ntoh16 ( mio_dns_msg_to_pkt ( msg ) - > id ) ) ;
if ( msg = = dnc - > pending_req | | msgxtn - > next | | msgxtn - > prev )
{
/* it's chained in the pending request. unchain it */
unchain_pending_dns_reqmsg ( dnc , msg ) ;
}
if ( msgxtn - > rtmridx ! = MIO_TMRIDX_INVALID )
{
mio_deltmrjob ( mio , msgxtn - > rtmridx ) ;
MIO_ASSERT ( mio , msgxtn - > rtmridx = = MIO_TMRIDX_INVALID ) ;
}
2020-02-25 07:09:20 +00:00
/* TODO: add it to the free msg list instead of just freeing it. */
2020-02-21 04:21:16 +00:00
mio_dns_free_msg ( dnc - > mio , msg ) ;
}
2020-02-21 09:42:10 +00:00
/* ----------------------------------------------------------------------- */
static int on_tcp_read ( mio_dev_sck_t * dev , const void * data , mio_iolen_t dlen , const mio_skad_t * srcaddr )
{
2020-02-27 17:34:27 +00:00
mio_t * mio = dev - > mio ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-28 07:41:25 +00:00
mio_dns_msg_t * reqmsg ;
2020-02-27 17:34:27 +00:00
mio_dns_pkt_t * pkt ;
mio_uint16_t id ;
2020-02-25 07:09:20 +00:00
if ( MIO_UNLIKELY ( dlen < = - 1 ) )
{
2020-02-27 17:34:27 +00:00
MIO_DEBUG1 ( mio , " dns tcp read error ....%js \n " , mio_geterrmsg ( mio ) ) ; /* TODO: add source packet */
2020-02-28 07:41:25 +00:00
goto oops ;
2020-02-25 07:09:20 +00:00
}
2020-02-28 07:41:25 +00:00
else if ( MIO_UNLIKELY ( dlen = = 0 ) )
2020-02-25 07:09:20 +00:00
{
2020-02-27 17:34:27 +00:00
MIO_DEBUG0 ( mio , " dns tcp read error ...premature socket hangul \n " ) ; /* TODO: add source packet */
2020-02-28 07:41:25 +00:00
goto oops ;
2020-02-25 07:09:20 +00:00
}
2020-02-27 17:34:27 +00:00
2020-02-28 07:41:25 +00:00
/* TODO: assemble the first two bytes.
* read as many as those two bytes . .
* the following code is wrong . . */
if ( MIO_UNLIKELY ( dlen < MIO_SIZEOF ( * pkt ) + 2 ) )
2020-02-27 17:34:27 +00:00
{
MIO_DEBUG0 ( mio , " dns packet too small from .... \n " ) ; /* TODO: add source packet */
2020-02-28 07:41:25 +00:00
goto oops ; /* mut not be an error. buffer it futher... */
2020-02-27 17:34:27 +00:00
}
2020-02-28 07:41:25 +00:00
pkt = ( mio_dns_pkt_t * ) ( ( mio_uint8_t * ) data + 2 ) ;
2020-02-27 17:34:27 +00:00
if ( ! pkt - > qr )
{
MIO_DEBUG0 ( mio , " dropping dns request received ... \n " ) ; /* TODO: add source info */
2020-02-28 07:41:25 +00:00
return 0 ; /* drop request. nothing to do */
2020-02-27 17:34:27 +00:00
}
2020-02-28 07:41:25 +00:00
id = mio_ntoh16 ( pkt - > id ) ;
2020-02-27 17:34:27 +00:00
2020-02-28 07:41:25 +00:00
MIO_DEBUG1 ( mio , " <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<GOT DATA>>>>>>>>>>>id [%d] >>>>>>>>>>>>>>>>>> \n " , id ) ;
2020-02-27 17:34:27 +00:00
2020-02-28 07:41:25 +00:00
reqmsg = dnc - > pending_req ;
while ( reqmsg )
2020-02-25 07:09:20 +00:00
{
2020-02-28 07:41:25 +00:00
mio_dns_pkt_t * reqpkt = mio_dns_msg_to_pkt ( reqmsg ) ;
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
if ( dev = = ( mio_dev_sck_t * ) reqmsgxtn - > dev & & pkt - > id = = reqpkt - > id )
{
if ( MIO_LIKELY ( reqmsgxtn - > on_done ) ) reqmsgxtn - > on_done ( dnc , reqmsg , MIO_ENOERR , pkt , dlen - 2 ) ;
release_dns_msg ( dnc , reqmsg ) ;
return 0 ;
}
reqmsg = reqmsgxtn - > next ;
2020-02-25 07:09:20 +00:00
}
2020-02-28 07:41:25 +00:00
MIO_DEBUG1 ( mio , " unknown dns response over tcp... %d \n " , pkt - > id ) ; /* TODO: add source info */
2020-02-27 17:34:27 +00:00
return 0 ;
2020-02-28 07:41:25 +00:00
oops :
2020-02-27 17:34:27 +00:00
mio_dev_sck_halt ( dev ) ;
2020-02-21 09:42:10 +00:00
return 0 ;
}
2020-02-27 08:40:39 +00:00
static void on_tcp_reply_timeout ( mio_t * mio , const mio_ntime_t * now , mio_tmrjob_t * job )
{
mio_dns_msg_t * reqmsg = ( mio_dns_msg_t * ) job - > ctx ;
2020-02-28 07:41:25 +00:00
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
mio_dev_sck_t * dev = reqmsgxtn - > dev ;
2020-02-27 08:40:39 +00:00
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-28 07:41:25 +00:00
MIO_ASSERT ( mio , reqmsgxtn - > rtmridx = = MIO_TMRIDX_INVALID ) ;
2020-02-27 08:40:39 +00:00
MIO_ASSERT ( mio , dev = = dnc - > tcp_sck ) ;
MIO_DEBUG0 ( mio , " *** TIMEOUT ==> unable to receive dns response in time over TCP... \n " ) ;
2020-02-28 07:41:25 +00:00
if ( MIO_LIKELY ( reqmsgxtn - > on_done ) ) reqmsgxtn - > on_done ( dnc , reqmsg , MIO_ETMOUT , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , reqmsg ) ;
2020-02-27 08:40:39 +00:00
}
2020-02-21 09:42:10 +00:00
static int on_tcp_write ( mio_dev_sck_t * dev , mio_iolen_t wrlen , void * wrctx , const mio_skad_t * dstaddr )
{
2020-02-27 08:40:39 +00:00
mio_t * mio = dev - > mio ;
mio_dns_msg_t * msg = ( mio_dns_msg_t * ) wrctx ;
dnc_dns_msg_xtn_t * msgxtn = dnc_dns_msg_getxtn ( msg ) ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-27 17:34:27 +00:00
mio_errnum_t status ;
2020-02-27 08:40:39 +00:00
if ( wrlen < = - 1 )
{
2020-02-27 17:34:27 +00:00
/* send failure */
status = mio_geterrnum ( mio ) ;
goto finalize ;
2020-02-27 08:40:39 +00:00
}
else if ( mio_dns_msg_to_pkt ( msg ) - > qr = = 0 & & msgxtn - > rmaxtries > 0 )
{
/* question. schedule to wait for response */
mio_tmrjob_t tmrjob ;
MIO_MEMSET ( & tmrjob , 0 , MIO_SIZEOF ( tmrjob ) ) ;
tmrjob . ctx = msg ;
mio_gettime ( mio , & tmrjob . when ) ;
MIO_ADD_NTIME ( & tmrjob . when , & tmrjob . when , & msgxtn - > rtmout ) ;
tmrjob . handler = on_tcp_reply_timeout ;
tmrjob . idxptr = & msgxtn - > rtmridx ;
msgxtn - > rtmridx = mio_instmrjob ( mio , & tmrjob ) ;
if ( msgxtn - > rtmridx = = MIO_TMRIDX_INVALID )
{
/* call the callback to indicate this operation failure in the middle of transaction */
2020-02-27 17:34:27 +00:00
status = mio_geterrnum ( mio ) ;
MIO_DEBUG0 ( mio , " unable to schedule tcp timeout... \n " ) ;
goto finalize ;
2020-02-27 08:40:39 +00:00
}
}
else
{
2020-02-28 07:41:25 +00:00
/* no error. successfuly sent a message. no reply is expected */
2020-02-27 17:34:27 +00:00
status = MIO_ENOERR ;
goto finalize ;
2020-02-27 08:40:39 +00:00
}
2020-02-25 07:09:20 +00:00
2020-02-21 09:42:10 +00:00
return 0 ;
2020-02-27 17:34:27 +00:00
finalize :
2020-02-28 07:41:25 +00:00
if ( MIO_LIKELY ( msgxtn - > on_done ) ) msgxtn - > on_done ( dnc , msg , status , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , msg ) ;
2020-02-27 17:34:27 +00:00
return 0 ;
2020-02-21 09:42:10 +00:00
}
2020-02-28 07:41:25 +00:00
static void write_dns_msg_over_tcp ( mio_dev_sck_t * dev , mio_dns_msg_t * msg )
2020-02-21 09:42:10 +00:00
{
mio_t * mio = dev - > mio ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-28 07:41:25 +00:00
dnc_dns_msg_xtn_t * msgxtn = dnc_dns_msg_getxtn ( msg ) ;
2020-02-21 09:42:10 +00:00
mio_uint16_t pktlen ;
2020-02-25 07:09:20 +00:00
mio_iovec_t iov [ 2 ] ;
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
pktlen = mio_hton16 ( msg - > pktlen ) ;
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
MIO_ASSERT ( mio , msgxtn - > rtries = = 0 ) ;
msgxtn - > rtries = 1 ; /* this is the first send. however, there will be no retries over tcp */
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
/* TODO: Is it better to create 2 byte space when sending UDP and use it here instead of iov? */
2020-02-25 07:09:20 +00:00
iov [ 0 ] . iov_ptr = & pktlen ;
iov [ 0 ] . iov_len = MIO_SIZEOF ( pktlen ) ;
2020-02-28 07:41:25 +00:00
iov [ 1 ] . iov_ptr = mio_dns_msg_to_pkt ( msg ) ;
iov [ 1 ] . iov_len = msg - > pktlen ;
if ( mio_dev_sck_timedwritev ( dev , iov , MIO_COUNTOF ( iov ) , & msgxtn - > rtmout , msg , MIO_NULL ) < = - 1 )
2020-02-21 09:42:10 +00:00
{
2020-02-28 07:41:25 +00:00
if ( MIO_LIKELY ( msgxtn - > on_done ) ) msgxtn - > on_done ( dnc , msg , mio_geterrnum ( mio ) , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , msg ) ;
}
}
static void on_tcp_connect ( mio_dev_sck_t * dev )
{
mio_t * mio = dev - > mio ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
mio_dns_msg_t * reqmsg ;
MIO_ASSERT ( mio , dev = = dnc - > tcp_sck ) ;
reqmsg = dnc - > pending_req ;
while ( reqmsg )
{
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
mio_dns_msg_t * nextreqmsg = reqmsgxtn - > next ;
if ( reqmsgxtn - > dev = = dev & & reqmsgxtn - > rtries < = 0 ) write_dns_msg_over_tcp ( dev , reqmsg ) ;
reqmsg = nextreqmsg ;
2020-02-21 09:42:10 +00:00
}
}
static void on_tcp_disconnect ( mio_dev_sck_t * dev )
{
mio_t * mio = dev - > mio ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-28 07:41:25 +00:00
mio_dns_msg_t * reqmsg ;
2020-02-21 09:42:10 +00:00
int status ;
/* UNABLE TO CONNECT or CONNECT TIMED OUT */
2020-02-27 08:40:39 +00:00
status = mio_geterrnum ( mio ) ;
2020-02-27 17:34:27 +00:00
if ( status = = MIO_ENOERR )
{
MIO_DEBUG0 ( mio , " TCP DISCONNECTED \n " ) ;
}
else
{
MIO_DEBUG2 ( mio , " TCP UNABLED TO CONNECT %d -> %js \n " , status , mio_errnum_to_errstr ( status ) ) ;
}
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
reqmsg = dnc - > pending_req ;
while ( reqmsg )
{
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
mio_dns_msg_t * nextreqmsg = reqmsgxtn - > next ;
if ( reqmsgxtn - > dev = = dev )
{
if ( MIO_LIKELY ( reqmsgxtn - > on_done ) ) reqmsgxtn - > on_done ( dnc , reqmsg , MIO_ENORSP , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , reqmsg ) ;
}
reqmsg = nextreqmsg ;
}
2020-02-27 08:40:39 +00:00
/* let's forget about the tcp socket */
dnc - > tcp_sck = MIO_NULL ;
2020-02-21 09:42:10 +00:00
}
static int switch_reqmsg_transport_to_tcp ( mio_svc_dnc_t * dnc , mio_dns_msg_t * reqmsg )
{
mio_t * mio = dnc - > mio ;
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
dnc_sck_xtn_t * sckxtn ;
mio_dev_sck_make_t mkinfo ;
mio_dev_sck_connect_t cinfo ;
if ( ! dnc - > tcp_sck )
{
MIO_MEMSET ( & mkinfo , 0 , MIO_SIZEOF ( mkinfo ) ) ;
switch ( mio_skad_family ( & reqmsgxtn - > servaddr ) )
{
case MIO_AF_INET :
mkinfo . type = MIO_DEV_SCK_TCP4 ;
break ;
case MIO_AF_INET6 :
mkinfo . type = MIO_DEV_SCK_TCP6 ;
break ;
default :
mio_seterrnum ( mio , MIO_EINTERN ) ;
return - 1 ;
}
mkinfo . on_write = on_tcp_write ;
mkinfo . on_read = on_tcp_read ;
mkinfo . on_connect = on_tcp_connect ;
mkinfo . on_disconnect = on_tcp_disconnect ;
dnc - > tcp_sck = mio_dev_sck_make ( mio , MIO_SIZEOF ( * sckxtn ) , & mkinfo ) ;
2020-02-25 07:09:20 +00:00
if ( ! dnc - > tcp_sck ) return - 1 ;
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
sckxtn = ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dnc - > tcp_sck ) ;
sckxtn - > dnc = dnc ;
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
MIO_MEMSET ( & cinfo , 0 , MIO_SIZEOF ( cinfo ) ) ;
cinfo . remoteaddr = reqmsgxtn - > servaddr ;
cinfo . connect_tmout = reqmsgxtn - > rtmout ; /* TOOD: create a separate connect timeout or treate rtmout as a whole transaction time and calculate the remaining time from the transaction start, and use it */
2020-02-21 09:42:10 +00:00
2020-02-28 07:41:25 +00:00
if ( mio_dev_sck_connect ( dnc - > tcp_sck , & cinfo ) < = - 1 )
{
mio_dev_sck_kill ( dnc - > tcp_sck ) ;
dnc - > tcp_sck = MIO_NULL ;
return - 1 ; /* the connect request hasn't been honored. */
}
2020-02-27 17:34:27 +00:00
}
2020-02-28 07:41:25 +00:00
2020-02-25 07:09:20 +00:00
/* switch the belonging device to the tcp socket since the connect request has been acknowledged. */
2020-02-27 08:40:39 +00:00
MIO_ASSERT ( mio , reqmsgxtn - > rtmridx = = MIO_TMRIDX_INVALID ) ; /* ensure no timer job scheduled at this moment */
2020-02-28 07:41:25 +00:00
reqmsgxtn - > dev = dnc - > tcp_sck ;
reqmsgxtn - > rtries = 0 ;
2020-02-25 07:09:20 +00:00
2020-02-27 17:34:27 +00:00
printf ( " SWITCHED >>>>>>>>>>>>>>>>> %p %p %p %p %p \n " , reqmsg , reqmsgxtn , reqmsgxtn - > dev , dnc - > udp_sck , dnc - > tcp_sck ) ;
2020-02-28 07:41:25 +00:00
if ( MIO_DEV_SCK_GET_PROGRESS ( dnc - > tcp_sck ) & MIO_DEV_SCK_CONNECTED )
{
write_dns_msg_over_tcp ( reqmsgxtn - > dev , reqmsg ) ;
/* the caller must not use reqmsg from now because write_dns_msg_over_tcp() may release 'reqmsg' */
}
2020-02-25 07:09:20 +00:00
return 0 ;
2020-02-21 09:42:10 +00:00
}
/* ----------------------------------------------------------------------- */
2020-02-21 04:21:16 +00:00
static int on_udp_read ( mio_dev_sck_t * dev , const void * data , mio_iolen_t dlen , const mio_skad_t * srcaddr )
{
mio_t * mio = dev - > mio ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
mio_dns_pkt_t * pkt ;
mio_dns_msg_t * reqmsg ;
mio_uint16_t id ;
2020-02-27 17:34:27 +00:00
if ( MIO_UNLIKELY ( dlen < = - 1 ) )
{
MIO_DEBUG1 ( mio , " dns read error ....%js \n " , mio_geterrmsg ( mio ) ) ; /* TODO: add source packet */
return 0 ;
}
2020-02-21 04:21:16 +00:00
if ( MIO_UNLIKELY ( dlen < MIO_SIZEOF ( * pkt ) ) )
{
MIO_DEBUG0 ( mio , " dns packet too small from .... \n " ) ; /* TODO: add source packet */
return 0 ; /* drop */
}
pkt = ( mio_dns_pkt_t * ) data ;
if ( ! pkt - > qr )
{
MIO_DEBUG0 ( mio , " dropping dns request received ... \n " ) ; /* TODO: add source info */
return 0 ; /* drop request */
}
id = mio_ntoh16 ( pkt - > id ) ;
/* if id doesn't match one of the pending requests sent, drop it */
/* TODO: improve performance of dns response matching*/
reqmsg = dnc - > pending_req ;
while ( reqmsg )
{
mio_dns_pkt_t * reqpkt = mio_dns_msg_to_pkt ( reqmsg ) ;
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
2020-02-28 07:41:25 +00:00
if ( reqmsgxtn - > dev = = dev & & pkt - > id = = reqpkt - > id & & mio_equal_skads ( & reqmsgxtn - > servaddr , srcaddr , 0 ) )
2020-02-21 04:21:16 +00:00
{
2020-02-27 08:40:39 +00:00
if ( reqmsgxtn - > rtmridx ! = MIO_TMRIDX_INVALID )
{
/* unschedule a timer job if any */
mio_deltmrjob ( mio , reqmsgxtn - > rtmridx ) ;
MIO_ASSERT ( mio , reqmsgxtn - > rtmridx = = MIO_TMRIDX_INVALID ) ;
}
2020-02-21 09:42:10 +00:00
////////////////////////
pkt - > tc = 1 ;
////////////////////////
2020-02-25 07:09:20 +00:00
if ( MIO_UNLIKELY ( pkt - > tc ) )
2020-02-21 04:21:16 +00:00
{
/* TODO: add an option for this behavior */
2020-02-28 07:41:25 +00:00
if ( switch_reqmsg_transport_to_tcp ( dnc , reqmsg ) > = 0 ) return 0 ;
2020-02-25 07:09:20 +00:00
/* TODO: add an option to call an error callback with TRUNCATION error code instead of fallback to received UDP truncated message */
2020-02-21 04:21:16 +00:00
}
2020-02-27 17:34:27 +00:00
MIO_DEBUG1 ( mio , " received dns response over udp..id %d \n " , id ) ;
if ( MIO_LIKELY ( reqmsgxtn - > on_done ) ) reqmsgxtn - > on_done ( dnc , reqmsg , MIO_ENOERR , data , dlen ) ;
2020-02-21 04:21:16 +00:00
release_dns_msg ( dnc , reqmsg ) ;
return 0 ;
}
2020-02-27 17:34:27 +00:00
2020-02-21 04:21:16 +00:00
reqmsg = reqmsgxtn - > next ;
}
/* the response id didn't match the ID of pending requests - need to wait longer? */
MIO_DEBUG1 ( mio , " unknown dns response... %d \n " , pkt - > id ) ; /* TODO: add source info */
return 0 ;
}
static void on_udp_reply_timeout ( mio_t * mio , const mio_ntime_t * now , mio_tmrjob_t * job )
{
mio_dns_msg_t * reqmsg = ( mio_dns_msg_t * ) job - > ctx ;
dnc_dns_msg_xtn_t * msgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
2020-02-28 07:41:25 +00:00
mio_dev_sck_t * dev = msgxtn - > dev ;
2020-02-21 04:21:16 +00:00
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-27 17:34:27 +00:00
mio_errnum_t status = MIO_ETMOUT ;
2020-02-21 04:21:16 +00:00
MIO_ASSERT ( mio , msgxtn - > rtmridx = = MIO_TMRIDX_INVALID ) ;
2020-02-21 09:42:10 +00:00
MIO_ASSERT ( mio , dev = = dnc - > udp_sck ) ;
2020-02-21 04:21:16 +00:00
MIO_DEBUG0 ( mio , " *** TIMEOUT ==> unable to receive dns response in time... \n " ) ;
if ( msgxtn - > rtries < msgxtn - > rmaxtries )
{
mio_ntime_t * tmout ;
tmout = MIO_IS_POS_NTIME ( & msgxtn - > wtmout ) ? & msgxtn - > wtmout : MIO_NULL ;
2020-02-27 17:34:27 +00:00
if ( mio_dev_sck_timedwrite ( dev , mio_dns_msg_to_pkt ( reqmsg ) , reqmsg - > pktlen , tmout , reqmsg , & msgxtn - > servaddr ) > = 0 ) return ; /* resent */
2020-02-21 04:21:16 +00:00
2020-02-27 17:34:27 +00:00
/* retry failed */
status = mio_geterrnum ( mio ) ;
2020-02-21 04:21:16 +00:00
}
2020-02-27 17:34:27 +00:00
if ( MIO_LIKELY ( msgxtn - > on_done ) ) msgxtn - > on_done ( dnc , reqmsg , status , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , reqmsg ) ;
2020-02-21 04:21:16 +00:00
}
static int on_udp_write ( mio_dev_sck_t * dev , mio_iolen_t wrlen , void * wrctx , const mio_skad_t * dstaddr )
{
mio_t * mio = dev - > mio ;
mio_dns_msg_t * msg = ( mio_dns_msg_t * ) wrctx ;
dnc_dns_msg_xtn_t * msgxtn = dnc_dns_msg_getxtn ( msg ) ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
2020-02-27 17:34:27 +00:00
mio_errnum_t status ;
2020-02-21 04:21:16 +00:00
MIO_ASSERT ( mio , dev = = ( mio_dev_sck_t * ) msgxtn - > dev ) ;
if ( wrlen < = - 1 )
{
/* write has timed out or an error has occurred */
2020-02-27 17:34:27 +00:00
status = mio_geterrnum ( mio ) ;
goto finalize ;
2020-02-21 04:21:16 +00:00
}
else if ( mio_dns_msg_to_pkt ( msg ) - > qr = = 0 & & msgxtn - > rmaxtries > 0 )
{
/* question. schedule to wait for response */
mio_tmrjob_t tmrjob ;
2020-02-27 17:34:27 +00:00
MIO_DEBUG1 ( mio , " sent dns question %d \n " , ( int ) mio_ntoh16 ( mio_dns_msg_to_pkt ( msg ) - > id ) ) ;
2020-02-21 04:21:16 +00:00
MIO_MEMSET ( & tmrjob , 0 , MIO_SIZEOF ( tmrjob ) ) ;
tmrjob . ctx = msg ;
mio_gettime ( mio , & tmrjob . when ) ;
MIO_ADD_NTIME ( & tmrjob . when , & tmrjob . when , & msgxtn - > rtmout ) ;
tmrjob . handler = on_udp_reply_timeout ;
tmrjob . idxptr = & msgxtn - > rtmridx ;
msgxtn - > rtmridx = mio_instmrjob ( mio , & tmrjob ) ;
if ( msgxtn - > rtmridx = = MIO_TMRIDX_INVALID )
{
/* call the callback to indicate this operation failure in the middle of transaction */
2020-02-27 17:34:27 +00:00
status = mio_geterrnum ( mio ) ;
2020-02-21 04:21:16 +00:00
MIO_DEBUG0 ( mio , " unable to schedule timeout... \n " ) ;
2020-02-27 17:34:27 +00:00
goto finalize ;
2020-02-21 04:21:16 +00:00
}
2020-02-27 17:34:27 +00:00
if ( msgxtn - > rtries = = 0 )
2020-02-21 04:21:16 +00:00
{
2020-02-27 17:34:27 +00:00
/* this is the first wait */
/* TODO: improve performance. hashing by id? */
/* chain it to the peing request list */
chain_pending_dns_reqmsg ( dnc , msg ) ;
2020-02-21 04:21:16 +00:00
}
2020-02-27 17:34:27 +00:00
msgxtn - > rtries + + ;
2020-02-21 04:21:16 +00:00
}
else
{
2020-02-27 17:34:27 +00:00
MIO_DEBUG1 ( mio , " sent dns message %d \n " , ( int ) mio_ntoh16 ( mio_dns_msg_to_pkt ( msg ) - > id ) ) ;
/* sent an answer. however this may be a question if msgxtn->rmaxtries is 0. */
status = MIO_ENOERR ;
goto finalize ;
2020-02-21 04:21:16 +00:00
}
return 0 ;
2020-02-27 17:34:27 +00:00
finalize :
if ( MIO_LIKELY ( msgxtn - > on_done ) ) msgxtn - > on_done ( dnc , msg , status , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , msg ) ;
return 0 ;
2020-02-21 04:21:16 +00:00
}
static void on_udp_connect ( mio_dev_sck_t * dev )
{
}
static void on_udp_disconnect ( mio_dev_sck_t * dev )
{
2020-02-28 07:41:25 +00:00
mio_t * mio = dev - > mio ;
mio_svc_dnc_t * dnc = ( ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dev ) ) - > dnc ;
mio_dns_msg_t * reqmsg ;
reqmsg = dnc - > pending_req ;
while ( reqmsg )
{
dnc_dns_msg_xtn_t * reqmsgxtn = dnc_dns_msg_getxtn ( reqmsg ) ;
mio_dns_msg_t * nextreqmsg = reqmsgxtn - > next ;
if ( reqmsgxtn - > dev = = dev )
{
if ( MIO_LIKELY ( reqmsgxtn - > on_done ) ) reqmsgxtn - > on_done ( dnc , reqmsg , MIO_ENORSP , MIO_NULL , 0 ) ;
release_dns_msg ( dnc , reqmsg ) ;
}
reqmsg = nextreqmsg ;
}
2020-02-21 04:21:16 +00:00
}
mio_svc_dnc_t * mio_svc_dnc_start ( mio_t * mio , const mio_skad_t * serv_addr , const mio_skad_t * bind_addr , const mio_ntime_t * send_tmout , const mio_ntime_t * reply_tmout , mio_oow_t reply_tmout_max_tries )
{
mio_svc_dnc_t * dnc = MIO_NULL ;
mio_dev_sck_make_t mkinfo ;
dnc_sck_xtn_t * xtn ;
dnc = ( mio_svc_dnc_t * ) mio_callocmem ( mio , MIO_SIZEOF ( * dnc ) ) ;
if ( ! dnc ) goto oops ;
dnc - > mio = mio ;
dnc - > stop = mio_svc_dnc_stop ;
dnc - > serv_addr = * serv_addr ;
dnc - > send_tmout = * send_tmout ;
dnc - > reply_tmout = * reply_tmout ;
dnc - > reply_tmout_max_tries = reply_tmout_max_tries ;
MIO_MEMSET ( & mkinfo , 0 , MIO_SIZEOF ( mkinfo ) ) ;
switch ( mio_skad_family ( serv_addr ) )
{
case MIO_AF_INET :
mkinfo . type = MIO_DEV_SCK_UDP4 ;
break ;
case MIO_AF_INET6 :
mkinfo . type = MIO_DEV_SCK_UDP6 ;
break ;
default :
mio_seterrnum ( mio , MIO_EINVAL ) ;
goto oops ;
}
mkinfo . on_write = on_udp_write ;
mkinfo . on_read = on_udp_read ;
mkinfo . on_connect = on_udp_connect ;
mkinfo . on_disconnect = on_udp_disconnect ;
dnc - > udp_sck = mio_dev_sck_make ( mio , MIO_SIZEOF ( * xtn ) , & mkinfo ) ;
if ( ! dnc - > udp_sck ) goto oops ;
xtn = ( dnc_sck_xtn_t * ) mio_dev_sck_getxtn ( dnc - > udp_sck ) ;
xtn - > dnc = dnc ;
if ( bind_addr ) /* TODO: get mio_dev_sck_bind_t? instead of bind_addr? */
{
mio_dev_sck_bind_t bi ;
MIO_MEMSET ( & bi , 0 , MIO_SIZEOF ( bi ) ) ;
bi . localaddr = * bind_addr ;
if ( mio_dev_sck_bind ( dnc - > udp_sck , & bi ) < = - 1 ) goto oops ;
}
MIO_SVC_REGISTER ( mio , ( mio_svc_t * ) dnc ) ;
return dnc ;
oops :
if ( dnc )
{
if ( dnc - > udp_sck ) mio_dev_sck_kill ( dnc - > udp_sck ) ;
mio_freemem ( mio , dnc ) ;
}
return MIO_NULL ;
}
void mio_svc_dnc_stop ( mio_svc_dnc_t * dnc )
{
mio_t * mio = dnc - > mio ;
2020-02-25 07:09:20 +00:00
2020-02-21 04:21:16 +00:00
if ( dnc - > udp_sck ) mio_dev_sck_kill ( dnc - > udp_sck ) ;
2020-02-25 07:09:20 +00:00
if ( dnc - > tcp_sck ) mio_dev_sck_kill ( dnc - > tcp_sck ) ;
2020-02-21 04:21:16 +00:00
while ( dnc - > pending_req ) release_dns_msg ( dnc , dnc - > pending_req ) ;
2020-02-25 07:09:20 +00:00
MIO_SVC_UNREGISTER ( mio , dnc ) ;
2020-02-21 04:21:16 +00:00
mio_freemem ( mio , dnc ) ;
}
2020-02-27 17:34:27 +00:00
mio_dns_msg_t * mio_svc_dnc_sendmsg ( mio_svc_dnc_t * dnc , mio_dns_bhdr_t * bdns , mio_dns_bqr_t * qr , mio_oow_t qr_count , mio_dns_brr_t * rr , mio_oow_t rr_count , mio_dns_bedns_t * edns , mio_svc_dnc_on_done_t on_done , mio_oow_t xtnsize )
2020-02-21 04:21:16 +00:00
{
/* send a request or a response */
mio_dns_msg_t * msg ;
dnc_dns_msg_xtn_t * msgxtn ;
mio_ntime_t * tmout ;
2020-02-25 07:09:20 +00:00
msg = mio_dns_make_msg ( dnc - > mio , bdns , qr , qr_count , rr , rr_count , edns , MIO_SIZEOF ( * msgxtn ) + xtnsize ) ;
2020-02-21 04:21:16 +00:00
if ( ! msg ) return MIO_NULL ;
2020-02-25 07:09:20 +00:00
if ( bdns - > id < 0 )
{
mio_dns_pkt_t * pkt = mio_dns_msg_to_pkt ( msg ) ;
pkt - > id = mio_hton16 ( dnc - > seq ) ;
dnc - > seq + + ;
}
2020-02-21 04:21:16 +00:00
msgxtn = dnc_dns_msg_getxtn ( msg ) ;
2020-02-28 07:41:25 +00:00
msgxtn - > dev = dnc - > udp_sck ;
2020-02-25 07:09:20 +00:00
msgxtn - > rtmridx = MIO_TMRIDX_INVALID ;
2020-02-27 17:34:27 +00:00
msgxtn - > on_done = on_done ;
2020-02-21 04:21:16 +00:00
msgxtn - > wtmout = dnc - > send_tmout ;
msgxtn - > rtmout = dnc - > reply_tmout ;
msgxtn - > rmaxtries = dnc - > reply_tmout_max_tries ;
msgxtn - > rtries = 0 ;
msgxtn - > servaddr = dnc - > serv_addr ;
/* TODO: optionally, override dnc->serv_addr and use the target address passed as a parameter */
tmout = MIO_IS_POS_NTIME ( & msgxtn - > wtmout ) ? & msgxtn - > wtmout : MIO_NULL ;
if ( mio_dev_sck_timedwrite ( dnc - > udp_sck , mio_dns_msg_to_pkt ( msg ) , msg - > pktlen , tmout , msg , & msgxtn - > servaddr ) < = - 1 )
{
release_dns_msg ( dnc , msg ) ;
return MIO_NULL ;
}
return msg ;
}
2020-02-27 17:34:27 +00:00
mio_dns_msg_t * mio_svc_dnc_sendreq ( mio_svc_dnc_t * dnc , mio_dns_bhdr_t * bdns , mio_dns_bqr_t * qr , mio_dns_bedns_t * edns , mio_svc_dnc_on_done_t on_done , mio_oow_t xtnsize )
2020-02-21 04:21:16 +00:00
{
/* send a request without resource records */
if ( bdns - > rcode ! = MIO_DNS_RCODE_NOERROR )
{
mio_seterrnum ( dnc - > mio , MIO_EINVAL ) ;
return MIO_NULL ;
}
2020-02-27 17:34:27 +00:00
return mio_svc_dnc_sendmsg ( dnc , bdns , qr , 1 , MIO_NULL , 0 , edns , on_done , xtnsize ) ;
2020-02-21 04:21:16 +00:00
}
/* ----------------------------------------------------------------------- */
struct dnc_dns_msg_resolve_xtn_t
{
mio_dns_rrt_t qtype ;
int flags ;
mio_svc_dnc_on_resolve_t on_resolve ;
} ;
typedef struct dnc_dns_msg_resolve_xtn_t dnc_dns_msg_resolve_xtn_t ;
# if defined(MIO_HAVE_INLINE)
static MIO_INLINE dnc_dns_msg_resolve_xtn_t * dnc_dns_msg_resolve_getxtn ( mio_dns_msg_t * msg ) { return ( ( dnc_dns_msg_resolve_xtn_t * ) ( ( mio_uint8_t * ) dnc_dns_msg_getxtn ( msg ) + MIO_SIZEOF ( dnc_dns_msg_xtn_t ) ) ) ; }
# else
# define dnc_dns_msg_resolve_getxtn(msg) ((dnc_dns_msg_resolve_xtn_t*)((mio_uint8_t*)dnc_dns_msg_getxtn(msg) + MIO_SIZEOF(dnc_dns_msg_xtn_t)))
# endif
static void on_dnc_resolve ( mio_svc_dnc_t * dnc , mio_dns_msg_t * reqmsg , mio_errnum_t status , const void * data , mio_oow_t dlen )
{
mio_t * mio = mio_svc_dnc_getmio ( dnc ) ;
mio_dns_pkt_info_t * pi = MIO_NULL ;
dnc_dns_msg_resolve_xtn_t * reqmsgxtn = dnc_dns_msg_resolve_getxtn ( reqmsg ) ;
if ( ! ( reqmsgxtn - > flags & MIO_SVC_DNC_RESOLVE_FLAG_BRIEF ) )
{
/* the full reply packet is requested. no transformation is required */
if ( reqmsgxtn - > on_resolve ) reqmsgxtn - > on_resolve ( dnc , reqmsg , status , data , dlen ) ;
return ;
}
if ( data )
{
mio_uint32_t i ;
MIO_ASSERT ( mio , status = = MIO_ENOERR ) ;
pi = mio_dns_make_packet_info ( mio , data , dlen ) ;
if ( ! pi )
{
status = mio_geterrnum ( mio ) ;
2020-02-27 17:34:27 +00:00
goto no_data ;
2020-02-21 04:21:16 +00:00
}
if ( pi - > hdr . rcode ! = MIO_DNS_RCODE_NOERROR )
{
status = MIO_EINVAL ;
2020-02-27 17:34:27 +00:00
goto no_data ;
2020-02-21 04:21:16 +00:00
}
2020-02-27 17:34:27 +00:00
if ( pi - > ancount < 0 ) goto no_data ;
2020-02-21 04:21:16 +00:00
/* in the brief mode, we inspect the answer section only */
if ( reqmsgxtn - > qtype = = MIO_DNS_RRT_Q_ANY )
{
/* return A or AAAA for ANY in the brief mode */
for ( i = 0 ; i < pi - > ancount ; i + + )
{
if ( pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_A | | pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_AAAA )
{
match_found :
if ( reqmsgxtn - > on_resolve ) reqmsgxtn - > on_resolve ( dnc , reqmsg , status , & pi - > rr . an [ i ] , MIO_SIZEOF ( pi - > rr . an [ i ] ) ) ;
goto done ;
}
}
}
for ( i = 0 ; i < pi - > ancount ; i + + )
{
/* it is a bit time taking to retreive the query type from the packet
* bundled in reqmsg as it requires parsing of the packet . let me use
* the query type i stored in the extension space . */
switch ( reqmsgxtn - > qtype )
{
case MIO_DNS_RRT_Q_ANY :
case MIO_DNS_RRT_Q_AFXR : /* AFXR doesn't make sense in the brief mode. just treat it like ANY */
/* no A or AAAA found. so give the first entry in the answer */
goto match_found ;
case MIO_DNS_RRT_Q_MAILA :
/* if you want to get the full RRs, don't use the brief mode. */
if ( pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_MD | | pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_MF ) goto match_found ;
break ;
case MIO_DNS_RRT_Q_MAILB :
/* if you want to get the full RRs, don't use the brief mode. */
if ( pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_MB | | pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_MG | |
pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_MR | | pi - > rr . an [ i ] . rrtype = = MIO_DNS_RRT_MINFO ) goto match_found ;
break ;
default :
if ( pi - > rr . an [ i ] . rrtype = = reqmsgxtn - > qtype ) goto match_found ;
break ;
}
}
2020-02-27 17:34:27 +00:00
goto no_data ;
2020-02-21 04:21:16 +00:00
}
else
{
2020-02-27 17:34:27 +00:00
no_data :
2020-02-21 04:21:16 +00:00
if ( reqmsgxtn - > on_resolve ) reqmsgxtn - > on_resolve ( dnc , reqmsg , status , MIO_NULL , 0 ) ;
}
done :
if ( pi ) mio_dns_free_packet_info ( mio_svc_dnc_getmio ( dnc ) , pi ) ;
}
mio_dns_msg_t * mio_svc_dnc_resolve ( mio_svc_dnc_t * dnc , const mio_bch_t * qname , mio_dns_rrt_t qtype , int flags , mio_svc_dnc_on_resolve_t on_resolve , mio_oow_t xtnsize )
{
static mio_dns_bhdr_t qhdr =
{
- 1 , /* id */
0 , /* qr */
MIO_DNS_OPCODE_QUERY , /* opcode */
0 , /* aa */
0 , /* tc */
1 , /* rd */
0 , /* ra */
0 , /* ad */
0 , /* cd */
MIO_DNS_RCODE_NOERROR /* rcode */
} ;
static mio_dns_bedns_t qedns =
{
4096 , /* uplen */
0 , /* edns version */
0 , /* dnssec ok */
0 , /* number of edns options */
MIO_NULL
} ;
mio_dns_bqr_t qr ;
mio_dns_msg_t * reqmsg ;
dnc_dns_msg_resolve_xtn_t * reqmsgxtn ;
qr . qname = ( mio_bch_t * ) qname ;
qr . qtype = qtype ;
qr . qclass = MIO_DNS_RRC_IN ;
reqmsg = mio_svc_dnc_sendmsg ( dnc , & qhdr , & qr , 1 , MIO_NULL , 0 , & qedns , on_dnc_resolve , MIO_SIZEOF ( * reqmsgxtn ) + xtnsize ) ;
if ( reqmsg )
{
reqmsgxtn = dnc_dns_msg_resolve_getxtn ( reqmsg ) ;
reqmsgxtn - > on_resolve = on_resolve ;
reqmsgxtn - > qtype = qtype ;
reqmsgxtn - > flags = flags ;
}
return reqmsg ;
}