mirror of
https://github.com/microsoft/WSL.git
synced 2025-12-12 07:44:25 -06:00
Many Microsoft employees have contributed to the Windows Subsystem for Linux, this commit is the result of their work since 2016. The entire history of the Windows Subsystem for Linux can't be shared here, but here's an overview of WSL's history after it moved to it own repository in 2021: Number of commits on the main branch: 2930 Number of contributors: 31 Head over https://github.com/microsoft/WSL/releases for a more detailed history of the features added to WSL since 2021.
624 lines
14 KiB
C
624 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) Microsoft. All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
common.c
|
|
|
|
Abstract:
|
|
|
|
Common socket definitions and helper routines.
|
|
|
|
--*/
|
|
|
|
#include <stdlib.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/epoll.h>
|
|
#include <stdbool.h>
|
|
#include "common.h"
|
|
#include "lxtcommon.h"
|
|
|
|
int LxtSocketEpoll(int Descriptor, int Event, int Timeout)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks whether the given epoll is set in the file descriptor.
|
|
|
|
Arguments:
|
|
|
|
Descriptor - Supplies the descriptor.
|
|
|
|
Event - Supplies an event to check for.
|
|
|
|
Timeout - Supplies the timeout value in milliseconds.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
struct epoll_event EpollEvent;
|
|
int EpollFd;
|
|
int Iterator;
|
|
int NumberDescriptors;
|
|
int Result;
|
|
|
|
EpollFd = -1;
|
|
LxtCheckErrno(EpollFd = epoll_create(1));
|
|
EpollEvent.events = Event;
|
|
EpollEvent.data.fd = Descriptor;
|
|
LxtCheckErrno(epoll_ctl(EpollFd, EPOLL_CTL_ADD, Descriptor, &EpollEvent));
|
|
LxtCheckErrno(NumberDescriptors = epoll_wait(EpollFd, &EpollEvent, 1, Timeout));
|
|
|
|
//
|
|
// If no descriptors were ready within the timeout, that is an error condition
|
|
//
|
|
|
|
if (NumberDescriptors != 1)
|
|
{
|
|
LxtLogInfo("expecting epoll_wait to return 1, but it returned %d", NumberDescriptors);
|
|
|
|
Result = -1;
|
|
errno = EAGAIN;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((EpollEvent.events & Event) == 0)
|
|
{
|
|
LxtLogError("epoll event(%d) is not set. Epoll event(s) set: %d", Event, EpollEvent.events);
|
|
|
|
Result = -1;
|
|
errno = EINVAL;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
Result = 0;
|
|
|
|
ErrorExit:
|
|
if (EpollFd != -1)
|
|
{
|
|
close(EpollFd);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
void* SocketBlockedReaderThread(void* Arg)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will call read on the given fd and block.
|
|
|
|
Arguments:
|
|
|
|
Arg - Supplies the argument for the datagram server to operate.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10] = "123456789";
|
|
ssize_t BytesRead;
|
|
int Fd;
|
|
int Result = LXT_RESULT_FAILURE;
|
|
Fd = *(int*)Arg;
|
|
LxtCheckErrno(BytesRead = recv(Fd, Buffer, sizeof(Buffer), 0));
|
|
if (BytesRead != 0)
|
|
{
|
|
LxtLogError("recv should return 0 bytes read, but it retured %d bytes", BytesRead);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("recv unblocked");
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
pthread_exit((void*)(ssize_t)Result);
|
|
}
|
|
|
|
void* SocketBlockedReaderZeroBufferThread(void* Arg)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will call read on the given fd with a zero-byte receive buffer
|
|
and block.
|
|
|
|
Arguments:
|
|
|
|
Arg - Supplies the argument for the datagram server to operate.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10] = "123456789";
|
|
ssize_t BytesRead;
|
|
int Fd;
|
|
int Result = LXT_RESULT_FAILURE;
|
|
Fd = *(int*)Arg;
|
|
LxtCheckErrno(BytesRead = recv(Fd, Buffer, 0, 0));
|
|
if (BytesRead != 0)
|
|
{
|
|
LxtLogError("recv should return 0 bytes read, but it retured %d bytes", BytesRead);
|
|
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("recv unblocked");
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
pthread_exit((void*)(ssize_t)Result);
|
|
}
|
|
|
|
struct cmsghdr* SocketGetControlMessage(struct msghdr* MessageHeader, struct cmsghdr* StartControlMessage, int Level, int Type)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will return the control information at the given level and
|
|
type.
|
|
|
|
Arguments:
|
|
|
|
MessageHeader - Supplies the message header from which the control
|
|
information has to be extracted.
|
|
|
|
StartControlMessage - Supplies the control message from where to start.
|
|
NULL if the search has to start from the beginning.
|
|
|
|
Level - Supplies the level of the control information.
|
|
|
|
Type - Supplies the type of the control information.
|
|
|
|
Return Value:
|
|
|
|
Control message or NULL if it does not exist.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
struct cmsghdr* ControlMessage;
|
|
|
|
//
|
|
// Use the system macros to extract receive the ip packet control info.
|
|
//
|
|
|
|
ControlMessage = NULL;
|
|
|
|
//
|
|
// If the start control message is provided use that, else get the first
|
|
// control message. This is automatically handled by MY_CMSG_NXTHDR.
|
|
//
|
|
|
|
for (ControlMessage = MY_CMSG_NXTHDR(MessageHeader, StartControlMessage); ControlMessage != NULL;
|
|
ControlMessage = MY_CMSG_NXTHDR(MessageHeader, ControlMessage))
|
|
{
|
|
|
|
if (ControlMessage->cmsg_len < sizeof(struct cmsghdr))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Look for a match.
|
|
//
|
|
|
|
if ((ControlMessage->cmsg_level == Level) && (ControlMessage->cmsg_type == Type))
|
|
{
|
|
|
|
return ControlMessage;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void* SocketBlockedWriterThread(void* Arg)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will call write on the given fd and block.
|
|
|
|
Arguments:
|
|
|
|
Arg - Supplies the argument for the datagram server to operate.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
char Buffer[10] = "123456789";
|
|
ssize_t BytesWritten;
|
|
int Fd;
|
|
int Result = LXT_RESULT_FAILURE;
|
|
Fd = *(int*)Arg;
|
|
LxtCheckErrnoFailure(send(Fd, Buffer, sizeof(Buffer), 0), EPIPE);
|
|
LxtLogInfo("send unblocked");
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
pthread_exit((void*)(ssize_t)Result);
|
|
}
|
|
|
|
int SocketGetSetBooleanSocketOption(int Socket, int OptionLevel, int OptionName, bool SmallerSizeAllowed)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine tests the getsockopt() and setsockopt() API for the
|
|
any of the boolean socket option.
|
|
|
|
Arguments:
|
|
|
|
Socket - Supplies the socket.
|
|
|
|
OptionLevel - Supplies the level at which the option has to be applied.
|
|
|
|
Option - Supplies the option to test.
|
|
|
|
SmallerSizeAllowed - Supplies a boolean indicating whether sizes smaller
|
|
than the size of the option are allowed.
|
|
|
|
Return Value:
|
|
|
|
Returns 0 on success, -1 on failure.
|
|
|
|
--*/
|
|
{
|
|
|
|
int Result;
|
|
int Option;
|
|
socklen_t OptionLength;
|
|
long long int OptionLong;
|
|
|
|
Result = LXT_RESULT_FAILURE;
|
|
|
|
//
|
|
// Validate proper handling of boolean socket options.
|
|
//
|
|
|
|
Option = 1;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
|
|
//
|
|
// Reset the option value to 0.
|
|
//
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 0, "%d");
|
|
|
|
//
|
|
// Since it is a boolean option, any value other than 0 is accepted for
|
|
// enabling the option. Try -ve.
|
|
//
|
|
|
|
Option = -1;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
|
|
//
|
|
// Reset the option value to 0.
|
|
//
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 0, "%d");
|
|
|
|
//
|
|
// Since it is a boolean option, any value other than 0 is accepted for
|
|
// enabling the option. Try > 0.
|
|
//
|
|
|
|
Option = 15;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
|
|
if (SmallerSizeAllowed == false)
|
|
{
|
|
|
|
//
|
|
// Validate that also 1,2 and 3 byte size is not a valid option size for
|
|
// boolean socket option.
|
|
//
|
|
|
|
OptionLength = 1;
|
|
LxtCheckErrnoFailure(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength), EINVAL);
|
|
|
|
OptionLength = 2;
|
|
LxtCheckErrnoFailure(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength), EINVAL);
|
|
|
|
OptionLength = 3;
|
|
LxtCheckErrnoFailure(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength), EINVAL);
|
|
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// Supplying an option size of 1, 2 and 3 are also accepted.
|
|
//
|
|
|
|
Option = 1;
|
|
OptionLength = 1;
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
|
|
//
|
|
// Reset the option value to 0.
|
|
//
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
//
|
|
// Option size of 2.
|
|
//
|
|
|
|
Option = 1;
|
|
OptionLength = 2;
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
|
|
//
|
|
// Reset the option value to 0.
|
|
//
|
|
|
|
Option = 0;
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
//
|
|
// Use option size of 3.
|
|
//
|
|
|
|
Option = 1;
|
|
OptionLength = 3;
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &Option, OptionLength));
|
|
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 1, "%d");
|
|
}
|
|
|
|
//
|
|
// Verify that anything above 4 bytes is ignored truncated.
|
|
//
|
|
|
|
OptionLong = 0x200000000;
|
|
OptionLength = sizeof(OptionLong);
|
|
LxtCheckErrno(setsockopt(Socket, OptionLevel, OptionName, &OptionLong, OptionLength));
|
|
|
|
OptionLength = sizeof(Option);
|
|
LxtCheckErrno(getsockopt(Socket, OptionLevel, OptionName, &Option, &OptionLength));
|
|
|
|
LxtCheckEqual(Option, 0, "%d");
|
|
|
|
ErrorExit:
|
|
return Result;
|
|
}
|
|
|
|
char* SocketGetTypeAsString(int Type)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the string equivalent for the give socket type.
|
|
|
|
Arguments:
|
|
|
|
Type - Supplies the socket type.
|
|
|
|
Return Value:
|
|
|
|
Returns the string equivalent for the type; NULL otherwise.
|
|
|
|
--*/
|
|
{
|
|
|
|
switch (Type)
|
|
{
|
|
case SOCK_STREAM:
|
|
return LXT_SOCKET_STREAM_STRING;
|
|
|
|
case SOCK_DGRAM:
|
|
return LXT_SOCKET_DGRAM_STRING;
|
|
|
|
case SOCK_RAW:
|
|
return LXT_SOCKET_RAW_STRING;
|
|
|
|
case SOCK_SEQPACKET:
|
|
return LXT_SOCKET_SEQPACKET_STRING;
|
|
|
|
case SOCK_PACKET:
|
|
return LXT_SOCKET_PACKET_STRING;
|
|
|
|
default:
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
int SocketStreamClientMsgWaitAll(int ConnectedSocket)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a client helper routine testing MSG_WAITALL flag recv syscall.
|
|
|
|
Arguments:
|
|
|
|
ConnectedSocket - Supplies a socket fd.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
int FullMessageSize;
|
|
char* ReceiveBuffer;
|
|
int Result = LXT_RESULT_FAILURE;
|
|
char* SendBuffer;
|
|
int Size;
|
|
|
|
SendBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;
|
|
FullMessageSize = 2 * strlen(SendBuffer);
|
|
ReceiveBuffer = malloc(FullMessageSize);
|
|
if (ReceiveBuffer == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("Client: 1. send");
|
|
LxtCheckErrno(Size = send(ConnectedSocket, SendBuffer, strlen(SendBuffer), 0));
|
|
|
|
//
|
|
// Sleep long enough that the second send won't be concatenated by WSK to
|
|
// test MSW_WAITALL code path, if the socket is inet socket.
|
|
//
|
|
|
|
sleep(1);
|
|
LxtLogInfo("Client: 2. delayed send");
|
|
LxtCheckErrno(Size = send(ConnectedSocket, SendBuffer, strlen(SendBuffer), 0));
|
|
|
|
memset(ReceiveBuffer, 0, FullMessageSize);
|
|
LxtLogInfo("Client: recv(MSG_WAITALL)");
|
|
LxtCheckErrno(Size = recv(ConnectedSocket, ReceiveBuffer, FullMessageSize, MSG_WAITALL));
|
|
|
|
LxtCheckMemoryEqual(SendBuffer, ReceiveBuffer, FullMessageSize / 2);
|
|
LxtCheckMemoryEqual(SendBuffer, ReceiveBuffer + FullMessageSize / 2, sizeof(SendBuffer));
|
|
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (ReceiveBuffer != NULL)
|
|
{
|
|
free(ReceiveBuffer);
|
|
}
|
|
|
|
return Result;
|
|
}
|
|
|
|
int SocketStreamServerMsgWaitAll(int AcceptedSocket)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is a server helper routine testing MSG_WAITALL flag recv syscall.
|
|
|
|
Arguments:
|
|
|
|
AcceptedSocket - Supplies a socket fd.
|
|
|
|
Return Value:
|
|
|
|
0 on success, -1 on failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
int Index;
|
|
char* ReceiveBuffer;
|
|
int Result = LXT_RESULT_FAILURE;
|
|
int Size;
|
|
int FullMessageSize;
|
|
int Socket = 0;
|
|
|
|
ReceiveBuffer = LXT_SOCKET_DEFAULT_SEND_STRING;
|
|
FullMessageSize = 2 * strlen(ReceiveBuffer);
|
|
ReceiveBuffer = malloc(FullMessageSize);
|
|
if (ReceiveBuffer == NULL)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
LxtLogInfo("Server: recv(MSG_WAITALL)");
|
|
memset(ReceiveBuffer, 0, sizeof(ReceiveBuffer));
|
|
LxtCheckErrno(Size = recv(AcceptedSocket, ReceiveBuffer, FullMessageSize, MSG_WAITALL));
|
|
|
|
LxtLogInfo("Server: write all back");
|
|
LxtCheckErrno(write(AcceptedSocket, ReceiveBuffer, Size));
|
|
Result = LXT_RESULT_SUCCESS;
|
|
|
|
ErrorExit:
|
|
if (ReceiveBuffer != NULL)
|
|
{
|
|
free(ReceiveBuffer);
|
|
}
|
|
|
|
return Result;
|
|
}
|