Closed Bug 17223 Opened 25 years ago Closed 24 years ago

Support the flags argument to PR_Recv, PR_Send, PR_RecvFrom, and PR_SendTo

Categories

(NSPR :: NSPR, defect, P3)

defect

Tracking

(Not tracked)

RESOLVED FIXED

People

(Reporter: wtc, Assigned: wtc)

Details

Right now the flags argument to PR_Recv, PR_Send,
PR_RecvFrom, and PR_SendTo is not supported and must
be 0.

Some NSPR clients have requested that the MSG_PEEK
flag for recv be supported.  We also want to find
out what other flags can be supported.

I suspect that the reason we decided to not support
the flags argument is that in our NT version, PR_Recv
is implemented by an overlapped ReadFile call, which
doesn't support the MSG_PEEK flag of recv.  It is not
clear whether recv(MSG_PEEK) works on a socket associated
with an I/O completion port.
Status: NEW → ASSIGNED
UNIX98 specifies the following flags:
- recv, recvfrom: MSG_PEEK, MSG_OOB, MSG_WAITALL
- send, sendto: MSG_EOR, MSG_OOB

Winsock specifies the following flags:
- recv, recvfrom: MSG_PEEK, MSG_OOB
- send, sendto: MSG_DONTROUTE, MSG_OOB

So it seems that the flags that are universally
supported by the native socket interfaces are:
- recv, recvfrom: MSG_PEEK, MSG_OOB
- send, sendto: MSG_OOB

The NT-only version of NSPR may have trouble
supporting these flags because PR_Recv and
PR_Send are implemented using the Win32 ReadFile
and WriteFile functions, which don't support the
MSG_PEEK and MSG_OOB flags or their equivalents.
We use ReadFile and WriteFile instead of recv
send because we do overlapped I/O on the sockets.
Winsock2's WSARecv and WSASend support flags and
overlapped I/O.  However, some of our sockets
may be accepted by an AcceptEx call; this kind of
sockets cannot be passed to WSARecv and WSASend.
Moreover, the MSG_PEEK flag of WSARecv is not valid
for overlapped sockets, according to Microsoft
documentation.  So WSARecv and WSASend are out of
the question.
For the records, OpenVMS supports:
  - recv, recvfrom: MSG_PEEK, MSG_OOB
  - send, sendto: MSG_OOB
Status: ASSIGNED → RESOLVED
Closed: 25 years ago
Resolution: --- → WONTFIX
Resolved the bug as WONFIX due to the limitation of
our implementation on NT.
We will look into supporting the PR_MSG_PEEK flag
for PR_Recv in NSPR 4.1.

On Windows NT or other platforms that don't support
the BSD socket MSG_PEEK flag, we will emulate
PR_MSG_PEEK by buffering received data in PRFileDesc.
Status: RESOLVED → REOPENED
Resolution: WONTFIX → ---
Target Milestone: --- → 4.1
Status: REOPENED → ASSIGNED
I added the PR_MSG_PEEK flag (with same value as MSG_PEEK)
for PR_Recv().  On platforms that don't natively support
MSG_PEEK (e.g., Windows NT's overlapped mode sockets and
Mac OS OpenTransport), we will need to emulate it with a
buffer in the NSPR layer.  The tricky part is to manage
the size of this buffer.  Nelson, you said you have an
idea of how to emulate PR_MSG_PEEK.  Could you outline the
design?

This is the design I came up with for the emulation of
PR_MSG_PEEK.

Initially, a PRFileDesc does not have a peek buffer.
The peek buffer is allocated the first time we peek
in the file descriptor.

PR_Recv(sock, buf, amount, PR_MSG_PEEK, timeout) would be implemented
as follows:
    if (peek buffer doesn't exist) {
        allocate peek buffer of size 'amount';
        read data into the peek buffer;
    } else {
        /* peek buffer exists */
        if (peek buffer is empty) {
            if (peek buffer size < amount) {
                reallocate peek buffer of size 'amount';
            }
            read data into peek buffer;
        }
    }
    copy data into output buffer;

PR_Recv(sock, buf, amount, 0, timeout) would be implemented
as follows:
    if (peek buffer exists and contains data) {
        int n = min(amount, #bytes in peek buffer);
        consume n bytes of data in peek buffer
        and copy them to output buffer;
    } else {
        read data into output buffer;
    }

One drawback of this design is that if the peek
buffer already contains data, we will return
those data immediately without attempting
to find out if there are more data in the file
descriptor.  So we may return fewer data than
there actually are, which should be okay because
PR_Recv() is not required to return as many bytes
as requested.

If it is necessary to return as many bytes as
possible, we can call ioctl(FIONREAD) to find
out if the socket has more data, and read the
socket only if the socket has more data. This
is to prevent blocking because by definition
PR_Recv() should return immediately if there
are data available (including data in the peek
buffer).
SSL's general algorithm for PR_Recv is:
if (no data is already available to be read) {
    read in (and decrypt) the next SSL application data record, 
       blocking if necessary
}
Copy out available data, the lesser of the amount available
   and the amount requested.
If the PEEK flag is not set, reduce the amount available by
   the amount just copied out.
return the amount copied out.

Note that application data records are bounded to 32K for SSL2
and 16K for SSL3 and TLS.  So, the maximum amount available from
a single PR_Recv call on an SSL socket will be no more than 32KB.
I implemented the PR_MSG_PEEK flag for PR_Recv() for
BeOS, Mac OS, OS/2, and Windows.  On BeOS, Mac OS, and NT,
PR_MSG_PEEK is emulated with a peek buffer in a NSPR
file descriptor.

If PR_MSG_PEEK is emulated (i.e., the macro
_PR_HAVE_PEEK_BUFFER is defined), PR_Recv, PR_Available,
and PR_Available64 depend on the peek buffer.  The
algorithm is described as follows.

If there is data in the peek buffer, PR_Available and
PR_Available64 return the number of bytes of data in
the peek buffer.  PR_Recv copies the lesser of the amount
available and the amount requested from the peek buffer
to the output buffer.  If PR_MSG_PEEK is not specified,
the bytes copied out are consumed from the peek buffer.

If the peek buffer is not created yet or there is no data
in the peek buffer, PR_Available and PR_Available64 call
ioctl(FIONREAD) on the native socket to obtain the amount
of data available.  If PR_MSG_PEEK is specified, PR_Recv
reads data from the native socket into the output buffer
and then copies the data into the peek buffer.  The size
of the peek buffer does not exceed a maximum defined by
the macro _PR_PEEK_BUFFER_MAX.

On NT, only overlapped mode sockets need to emulate
PR_MSG_PEEK.  So I use the macro
_PR_FD_NEED_EMULATE_MSG_PEEK(fd) to determine
whether the fd needs to emulate PR_MSG_PEEK.  On
platforms where all fd's need to emulate PR_MSG_PEEK
(such as BeOS and Mac OS), _PR_FD_NEED_EMULATE_MSG_PEEK(fd)
is simply defined as 1.
I fixed a mistake that I found when I compiled on Mac.
/cvsroot/mozilla/nsprpub/pr/src/io/prsocket.c, revision 3.42
There is a test case (peek.c) for the PR_MSG_PEEK flag
for PR_Recv.  It probably could be improved.

Marked the bug fixed.
Status: ASSIGNED → RESOLVED
Closed: 25 years ago24 years ago
Resolution: --- → FIXED
You need to log in before you can comment on or make changes to this bug.