[Ace-users] [ace-users] Why doesn't ACE allow 0 length UDP packets (datagrams) to be sent?

Douglas C. Schmidt schmidt at dre.vanderbilt.edu
Fri Mar 7 08:27:04 CST 2008


Hi Andi,

>    ACE VERSION:
>
>        5.6

Thanks for using the PRF.

>    HOST MACHINE and OPERATING SYSTEM:
>
>
>        Windows
>
>
>    TARGET MACHINE and OPERATING SYSTEM, if different from HOST:
>    COMPILER NAME AND VERSION (AND PATCHLEVEL):
>
>
>        Windows
>        Microsoft Visual C++ 8 (VS 2005)
>
>
>    THE $ACE_ROOT/ace/config.h FILE [if you use a link to a platform-
>    specific file, simply state which one]:
>
>	#ifndef ACE_CONFIG_WIN32_H
>	#define ACE_CONFIG_WIN32_H
>	#include /**/ "ace/pre.h"
>
>	// NOTE: Please do not add anything besides #include's here.  Put
>other stuff
>	//       (definitions, etc.) in the included headers
>
>	// We need to ensure that for Borland vcl.h can be included before
>	// windows.h.  So we will not include config-win32-common.h from
>here,
>	// but instead let it be included at the appropriate place in
>	// config-win32-borland.h.
>	#if !defined (__BORLANDC__)
>	#    include "ace/config-win32-common.h"
>	#endif /* !__BORLANDC__ */
>
>	// Include the config-win32-* file specific to the compiler
>	#if defined (__BORLANDC__)
>	#    include "ace/config-win32-borland.h"
>	#elif defined (_MSC_VER)
>	#    include "ace/config-win32-msvc.h"
>	#elif defined (ghs)
>	#    include "ace/config-win32-ghs.h"
>	#elif defined (__MINGW32__)
>	#    include "ace/config-win32-mingw.h"
>	#elif defined (__DMC__)
>	#    include "ace/config-win32-dmc.h"
>	#else
>	#    error Compiler is not supported
>	#endif
>
>	#include /**/ "ace/post.h"
>	#endif /* ACE_CONFIG_WIN32_H */
>
>
>
>    AREA/CLASS/EXAMPLE AFFECTED:
>
>
>        N/A
>
>
>    DOES THE PROBLEM AFFECT:
>
>       Problem prevents applications from sending 0 byte size UDP
>packets.
>
>
>    SYNOPSIS:
>
>       Ace prevents applications from sending UDP packets of size 0
>through the sockets even though the underlying WINSOCK2 commands allow
>sending such packets.
>
>    DESCRIPTION:
>
>       For our application, clients need to be able to send UDP
>datagrams of size 0 to our servers for hole punching attempts. During
>our testing we've found that any attempt of sending 0 bytes through
>the ACE Proactor results in a failure.
>       After some investigation of the source code I have found the
>place where it rejects those packets and I have been able to change it
>so that it allows me to send 0 byte size packets.
>
>       The main question I have now is, what the reason is for denying
>to send those kinds of packets.
>
>       The class where it does the check for the packet size is in
>WIN32_Asynch_IO.cpp in the method
>
>ssize_t
>ACE_WIN32_Asynch_Write_Dgram::send (ACE_Message_Block *message_block,
>                                    size_t &number_of_bytes_sent,
>                                    int flags,
>                                    const ACE_Addr &addr,
>                                    const void *act,
>                                    int priority,
>                                    int signal_number)
>{
>  number_of_bytes_sent = 0;
>
>  size_t bytes_to_write = 0;
>
>  iovec  iov[ACE_IOV_MAX];
>  int    iovcnt = 0;
>
>  for (const ACE_Message_Block* msg = message_block;
>       msg != 0 && iovcnt < ACE_IOV_MAX;
>       msg = msg->cont () , ++iovcnt )
>  {
>    size_t msg_len = msg->length ();
>
>...
>
>    bytes_to_write += msg_len;
>
>    // Make as many iovec as needed to fit all of msg_len.
>    size_t rd_ptr_offset = 0;
>    while (msg_len > 0 && iovcnt < ACE_IOV_MAX)
>      {
>        u_long this_chunk_length;
>        if (msg_len > ULONG_MAX)
>          this_chunk_length = ULONG_MAX;
>        else
>          this_chunk_length = static_cast<u_long> (msg_len);
>        // Collect the data in the iovec.
>        iov[iovcnt].iov_base = msg->rd_ptr () + rd_ptr_offset;
>        iov[iovcnt].iov_len  = this_chunk_length;
>        msg_len -= this_chunk_length;
>        rd_ptr_offset += this_chunk_length;
>
>        // Increment iovec counter if there's more to do.
>        if (msg_len > 0)
>          iovcnt++;
>      }
>...
>  }
>
>  if ( bytes_to_write == 0 )
>      ACE_ERROR_RETURN ((LM_ERROR,
>                         ACE_TEXT
>("ACE_WIN32_Asynch_Write_Dgram::send:")
>                         ACE_TEXT ("Attempt to write 0 bytes\n")),
>                        -1);
>....
>  ssize_t initiate_result = ACE_OS::sendto (result->handle (),
>                                            iov,
>                                            iovcnt,
>                                            number_of_bytes_sent,
>                                            result->flags_,
>                                            (sockaddr *) addr.get_addr
>(),
>                                            addr.get_size(),
>                                            result,
>                                            0);
>....
>}
>
>When sending in a message_block of length 0, then the while loop is
>never executed thus leaving the 'iov' array uninitialized but the
>'iovcnt' is incremented to 1 as part of the for loop.
>The 'if ( bytes_to_write == 0 )' statement will then return since no
>bytes are to be written/sent. If I comment out that if statement, then
>the ACE_OS::sendto will fail because the first entry in the iov array
>is not valid.
>
>If I change this method to the following, then everything works fine
>and it's able to deal with sending 0 bytes.
>
>ssize_t
>ACE_WIN32_Asynch_Write_Dgram::send (ACE_Message_Block *message_block,
>                                    size_t &number_of_bytes_sent,
>                                    int flags,
>                                    const ACE_Addr &addr,
>                                    const void *act,
>                                    int priority,
>                                    int signal_number)
>{
>  number_of_bytes_sent = 0;
>
>  size_t bytes_to_write = 0;
>
>  iovec  iov[ACE_IOV_MAX];
>  int    iovcnt = 0;
>
>  for (const ACE_Message_Block* msg = message_block;
>       msg != 0 && iovcnt < ACE_IOV_MAX;
>       msg = msg->cont () , ++iovcnt )
>  {
>    size_t msg_len = msg->length ();
>
>...
>
>    bytes_to_write += msg_len;
>
>    // Make as many iovec as needed to fit all of msg_len.
>    size_t rd_ptr_offset = 0;
>    do
>    {
>        if (msg_len >= 0 && iovcnt < ACE_IOV_MAX)
>        {
>            u_long this_chunk_length;
>            if (msg_len > ULONG_MAX)
>              this_chunk_length = ULONG_MAX;
>            else
>              this_chunk_length = static_cast<u_long> (msg_len);
>            // Collect the data in the iovec.
>            iov[iovcnt].iov_base = msg->rd_ptr () + rd_ptr_offset;
>            iov[iovcnt].iov_len  = this_chunk_length;
>            msg_len -= this_chunk_length;
>            rd_ptr_offset += this_chunk_length;
>
>            // Increment iovec counter if there's more to do.
>            if (msg_len > 0)
>              iovcnt++;
>        }
>    }while (msg_len > 0 && iovcnt < ACE_IOV_MAX);
>...
>  }
>
>  //if ( bytes_to_write == 0 )
>  //    ACE_ERROR_RETURN ((LM_ERROR,
>  //                       ACE_TEXT
>("ACE_WIN32_Asynch_Write_Dgram::send:")
>  //                       ACE_TEXT ("Attempt to write 0 bytes\n")),
>  //                      -1);
>
>....
>
>  ssize_t initiate_result = ACE_OS::sendto (result->handle (),
>                                            iov,
>                                            iovcnt,
>                                            number_of_bytes_sent,
>                                            result->flags_,
>                                            (sockaddr *) addr.get_addr
>(),
>                                            addr.get_size(),
>                                            result,
>                                            0);
>....
>}
>
>I changed the 'while' loop to a 'do-while' loop and added the
>additional if statement inside it. This way the first entry in the iov
>array will be properly initialized in the case where the message
>length is 0. It still works the same way as before when sending non-
>zero size messages.
>
>Is there anything wrong with changing this method in such a way?

No, I think that's fine.  Could you please send me a patch for
WIN32_Asynch_IO.cpp relative to the version that's at

http://www.dre.vanderbilt.edu/~schmidt/DOC_ROOT/ACE/ace/WIN32_Asynch_IO.cpp

and I'll make sure it gets installed.

Thanks!

        Doug
-- 
Dr. Douglas C. Schmidt                       Professor and Associate Chair
Electrical Engineering and Computer Science  TEL: (615) 343-8197
Vanderbilt University                        WEB: www.dre.vanderbilt.edu/~schmidt
Nashville, TN 37203                          NET: d.schmidt at vanderbilt.edu



More information about the Ace-users mailing list