[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