The version of WSOCK.VXD that comes with Win95 up to OSR2.5 (I believe) implements Winsock 1.1, whereas Win98 has a Winsock 2.0 version. There is an upgrade to Winsock 2.0 for Windows '95 that includes the latest WSOCK.VXD and also another VxD, WSOCK2.VXD.
This document aims to document WSOCK.VXD's services from the point-of-view of accessing them from C from a DOS program running under Windows. Unfortunately, it is only currently known how to use the Winsock 1.1 version of WSOCK.VXD from a DOS box, because the same techniques seem to fail with the Winsock 2.0 version for no good reason (*). If anyone, knows how to get Winsock 2.0's WSOCK.VXD working, then I'd be glad to know.
* WSOCK.VXD fails when Winsock 2 is present in the following manner: Sockets can be created, but any operations with these sockets fails with the "service unavailable" error.
Winsock 2 services can be used in a DOS box, but not via WSOCK.VXD at present. This involves using another VxD instead. This method is documented in the Coda SOCK.VXD Pseudo-Documentation.
The Winsock header file WINSOCK.H is needed. This can be obtained as part of the "Winsock 2 for Windows '95 SDK", downloadable from the Microsoft web site. This defines many of the address family, socket type and protocol constants needed when calling WSOCK.VXD.
These can be found in (modified form in) several TCP/IP libraries that use WSOCK.VXD, such as libsocket - other libraries that use them can be found from the libsocket links page.
If you are trying to access WSOCK.VXD from a DOS box, you will probably have to use some assembly language and DPMI services. (Note: I'm not sure if you must.) This is a low-level programming after all, so it's pretty hairy. Familiarity with BSD-style sockets would probably help with the concepts of socket networking.
Interrupt 0x2F, Sub-function 0x1684: Get VxD entry point | |
---|---|
In |
|
Out |
|
VXLDR function 0x1: Load VxD | |
---|---|
In | AX = 0x1
DS:DX = Buffer containing VxD's filename |
Out | Carry flag clear on success |
WSOCK.VXD entry parameters | |
---|---|
In | AX = Function number
ES:BX = Buffer containing function data |
Out | AX = Error value |
If the error returned is 0xFFFF, then the services are unavailable, otherwise it is a Winsock error code, i.e. the same as Win32's Winsock error code. 0 indicates success.
Any struct's passed to WSOCK.VXD need to be packed, i.e. the data should be contiguous, arranged as presented in the structures. This is necessary because C compilers will align the data on boundaries to speed access. This introduces padding that will lead to spurious data being communicated to WSOCK.VXD.
With DJGPP, and other GNU C compiler ports, this can be achieved by using the __attribute__(packed) directive after the structure, like so:
struct { int data1; char data2; char data3[3]; } __attribute__((packed));
The version of WSOCK.H available here has a #define to do this automatically for DJGPP, but can easily be modified for other C compilers.
typedef struct _SOCK_LIST {
LPSOCK_INFO Socket; /* the target socket
*/
DWORD EventMask; /* events
the client is interested in */
DWORD Context;
/* user-defined context value (handle?) */
} SOCK_LIST;
Socket should be set to the socket created by function 0x110. EventMask should be set to a combination or any of the FD_* constants, e.g. FD_READ. Context should be set to a unique value, e.g. the current time.
Note: previously I stated that Context should be set to 0. Alfons Hoogervorst suggests this is not the case.
typedef struct _WSIOSTATUS {
DWORD IoStatus; /* completion status
*/
char IoCompleted; /* i/o has completed
*/
char IoCancelled; /* i/o has been cancelled
*/
char IoTimedOut; /* i/o has timed out
*/
char IoSpare1; /* spare (for
dword alignment) */
} WSIOSTATUS;
This is used for making blocking socket calls non-blocking for the client program, i.e. so that calls that block WSOCK.VXD won't block your program.
To use a status block, the ApcRoutine member of any structures should be set to -1 (WSOCK.H's SPECIAL_16BIT_APC constant) and ApcContext should point to the status block. The WSOCK.VXD function call will then return immediately, and the status flags will be set when the blocking call finishes. E.g. IoCompleted will be set to 1 when the I/O completes successfully.
Note: It states in WSOCK.H that this is for 16-bit applications only, but I think it works OK in the 32-bit code in libsocket.
typedef struct _WSNOTIFY {
LIST_ENTRY PerSocketList;/* per-socket list
of notify objects */
LIST_ENTRY GlobalList; /* global
list of all notify objects */
LPSOCK_INFO OwningSocket;/* the socket that
"owns" this object */
DWORD Flags;
/* private notification flags (see below)*/
DWORD EventMask;
/* events the client is interested in */
DWORD Status;
/* the completion status
*/
DWORD OwningThread;
/* either ring 0 thread id or VM handle */
LPVOID ApcRoutine;
/* the user-mode APC to schedule
*/
DWORD ApcContext;
/* a user-supplied context value
*/
} WSNOTIFY;
Function number | Description | BSD Function | Available under Windows for Workgroups | Documented Here |
---|---|---|---|---|
0x100 | Accept | accept() | Yes | Yes |
0x101 | Bind | bind() | Yes | Yes |
0x102 | Close socket | None equivalent [1] | Yes | Yes |
0x103 | Connect | connect() | Yes | Yes |
0x104 | Get peer name | getpeername() | Yes | Yes |
0x105 | Get socket name | getsockname() | Yes | Yes |
0x106 | Get socket options | getsockopt() | Yes | Yes |
0x107 | Socket ioctl | ioctl() | Yes | Yes |
0x108 | Listen | listen() | Yes | Yes |
0x109 | Receive | recv(), recvfrom() | Yes | Yes |
0x10A | Select set-up | select() | Yes | Yes |
0x10B | Select clean-up | Yes | Yes | |
0x10C | Asynchronous select | None equivalent | Yes | No |
0x10D | Send | send(), sendto() | Yes | Yes |
0x10E | Set socket options | setsockopt() | Yes | Yes |
0x10F | Shutdown | shutdown() | Yes | Yes |
0x110 | Create socket | socket() | Yes | Yes |
0x111 | Create | None equivalent | Yes | No |
0x112 | Create multiple | None equivalent | Yes | No |
0x113 | Destroy | None equivalent | Yes | No |
0x114 | Destroy by socket | None equivalent | Yes | No |
0x115 | Destroy by thread | None equivalent | Yes | No |
0x116 | Signal | None equivalent? | Yes | No |
0x117 | Signal all | None equivalent? | Yes | No |
0x118 | Control | None equivalent | Yes | No [2] |
0x119 | Register Postmessage Callback | None equivalent | Yes | No |
0x11A | Asynchronous receive | None equivalent | No | No |
0x11B | Asynchronous send | None equivalent | No |
Key | ||
---|---|---|
Documented | Undocumented | Undocumented and unavailable under WfWg |
<--More documentation & support |
[2] Alfons Hoogervorst has a document about obtaining IP data that discusses a Winsock DLL function WsControl(). This VxD function may be an interface to this, although I have not investigated this. Please see the links section for more details.
In | ListeningSocket should be set to the socket handle returned
by creation via function 0x110.
Address should be set to the buffer for the address of the accepted socket. AddressLength should be set to the length of this buffer. ConnectedSocketHandle should be set to a unique value, e.g. an integer given by time(). ConnectedSocket should be set to NULL. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|---|
Out | On success: Address will contain the address and AddressLength
will contain the length of the copied address data. The socket and handle
of the accepted socket will be given by ConnectedSocket and ConnectedSocketHandle.
On failure: ConnectedSocket is NULL. |
In | Socket should be set to the socket created by function
0x100.
Address should be set to the buffer containing the address to bind to; AddressLength should be set to the length of this buffer. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|---|
Out | On success: The address buffer pointed to by Address will contain the address bound to. |
In | Socket should be filled with the socket created by function 0x110. |
---|
In | Socket should be set to the socket created by function
0x110.
Address should be set to the server address to connect to; AddressLength should be set to the length of the address buffer. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|---|
Out | On success: The buffer pointed to by Address will contain the server address connected to. |
In | Socket should be set to the socket created by function
0x110.
Address should be a pointer to an address buffer; AddressLength should be set to the length of this buffer. |
---|---|
Out | On success: The buffer pointed to by Address will contain the address data. |
In | Socket should be set to the socket created by function
0x110.
Address should be a pointer to a buffer where the address will be stored; AddressLength should be set to the length of this buffer. |
---|---|
Out | On success: The buffer pointed to by Address will contain the socket's address. |
In | Socket should be set to the socket returned on creation by
function 0x110.
OptionLevel sets whether the information should be retrieved at socket level or at a protocol level. It should be set to the Winsock constant SOL_SOCKET or the protocol number respectively. OptionName specifies the type of information required - Winsock's SO_* constants. Note I: Some of the BSD options are not supported: SO_RCVLOWAT, SO_RCVTIMEO, SO_SNDLOWAT, SO_SNDTIMEO, IP_OPTIONS, TCP_MAXSEG. Errors don't seem to be returned for SO_RCVLOWAT, SO_RCVTIMEO, SO_SNDLOWAT, SO_SNDTIMEO. Note II: WSOCK.VXD returned WSAEFAULT errors when I tried to receive the value in IntValue only (i.e. with Value and ValueLength set to 0, NULL). It appears you always have to pass a valid buffer in, even if the value is returned in IntValue (see below). Note III: The integer values used in the struct linger of SO_LINGER are 16-bit integers. Under DJGPP the structure can be written as: struct linger { unsigned short l_onoff; unsigned short l_linger; } |
---|---|
Out |
On success: This function can return either a single integer or a buffer of information, depending on the option queried. If a buffer is returned, Value will point to it and ValueLength will contain its length. Otherwise, IntValue will be used. Boolean or integer options seem to be returned in IntValue. However, it is probably best to check that Value and ValueLength are 0, rather than assuming that booleans are returned in IntValue. |
In | Socket should be set to the socket created by function
0x100.
Command should be set to an ioctl constant - these are listed in the Winsock header file. Valid commands are: FIONBIO for non-blocking I/O; FIONREAD for the number of bytes that can be read atomically from the socket; SIOCATMARK for any unread inline out-of-band data. Param should be set to the parameter value for the command. |
---|---|
Out | On success: I do not have much information for this. For non-blocking sockets, it appears that Param will be set to 1 if the socket becomes/is non-blocking after the call. |
In | Backlog should be set to the length of the queue. Socket should be set to the server socket earlier created and bound. |
---|
In | Socket should be set to the socket created with function
0x110.
Address and AddressLength should be set to 0 if the receive should be from any address at the other end (BSD recv()). If you wish to receive from a specific address (BSD recvfrom()), Address should point to an address buffer, and AddressLength should be set to its length. Buffer should point to the buffer where the data will be stored; BufferLength should contain the buffer's length. Flags should be set with a any or a combination of the MSG_* constants from the Winsock header file Timeout should be set to the time to wait before the call returns if no data has been sent from the other end. Use 0 for an instant timeout, or -1 to wait forever. I believe this time is in seconds. BytesReceived should be set to 0. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|---|
Out | On success: BytesReceived will contain the number of bytes actually received and placed in the buffer. |
In | ReadList should contain the sockets that should be watched
for read readiness; ReadCount should contain the number of sockets
in that list. WriteList should contain the sockets that should
be watched for write readiness; WriteCount should contain the
number of sockets in that list. ExceptList should contain the
sockets that should be watched for an exception(?); ExceptCount
should contain the number of sockets in that list.
Note:ReadList, WriteList and ExceptList should not point at the same structure. This causes general protection faults in some circumstances. The socket lists need to have event masks. See the section on socket lists on how to set these up. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|---|
Out | On success: This function blocks until one of the sockets becomes
available for the I/O queried. This means some sockets may still be blocking,
waiting for the desired I/O to become available - I guess this is why a
clean-up call is needed.
I believe the socket lists will contain the sockets that are actually ready for the desired I/O. I don't know this for certain as libsocket uses a different method. libsocket only uses this call on one socket at a time and uses a status block to check whether the select operation completed successfully. |
In | ReadList should contain the sockets that should be watched
for read readiness; ReadCount should contain the number of sockets
in that list. WriteList should contain the sockets that should
be watched for write readiness; WriteCount should contain the
number of sockets in that list. ExceptList should contain the
sockets that should be watched for an exception(?); ExceptCount
should contain the number of sockets in that list.
Note:ReadList, WriteList and ExceptList should not point at the same structure. This causes general protection faults in some circumstances. Furthermore, I believe that they should not point at the same structures used in the Select Set-Up call either. The socket lists need to have event masks. See the section on socket lists on how to set these up. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|
I think this is an interface for Windows programs, so that they can use select(). I guess the Window parameter is a window handle (HWND).
In | Socket should be set to the socket created with function
0x110.
Address and AddressLength should be set to 0 if the send should be to any address at the other end (BSD send()). If you wish to send to a specific address (BSD sendto()), Address should point to an address buffer, and AddressLength should be set to its length. Buffer should point to the buffer containing the data to be sent; BufferLength should contain the buffer's length. Flags should be set with a any or a combination of the MSG_* constants from the Winsock header file Timeout should be set to the time to wait before the call returns if it can't send the data. Use 0 for an instant timeout, or -1 to wait forever. I believe this time is in seconds. BytesSent should be set to 0. ApcRoutine and ApcContext should be set for the status block or to 0. |
---|---|
Out | On success: BytesSent will contain the number of bytes actually received and placed in the buffer. |
In | Socket should be set to the socket returned on creation by
function 0x110.
OptionLevel sets whether the information should be set at socket level or at a protocol level. It should be set to the Winsock constant SOL_SOCKET or the protocol number respectively. OptionName specifies the option to set - Winsock's SO_* constants. Note I: Some of the BSD options are not supported: SO_ACCEPTCONN,SO_ERROR, SO_RCVLOWAT, SO_RCVTIMEO, SO_SNDLOWAT, SO_SNDTIMEO, SO_TYPE, IP_OPTIONS. Errors don't seem to be returned for these. Although the parameters to this function suggest that you can use IntValue to set integer values, I suspect this is only present to make the parameter block look like the one for Get Socket Options. Using IntValue seems to generate an WSAEFAULT error. So, a buffer should be used - Value should point to it and ValueLength should contain its length. Note II: The integer values used in the struct linger of SO_LINGER are 16-bit integers. Under DJGPP the structure can be written as: struct linger { unsigned short l_onoff; unsigned short l_linger; } The two values can also be passed as the low & high words of IntValue. |
---|
In | Socket should be set to the socket created by function
0x110.
How should be set to 0 to disable further receives, 1 to disable further sends and 2 to disable both further receives and sends. |
---|
In | AddressFamily should be set to the address family, e.g. AF_INET.
SocketType should be set to the socket type, e.g. SOCK_STREAM.
Protocol should be set to the protocol, e.g. IPPROTO_TCP.
NewSocketHandle should be set to a unique value, e.g. an integer
given by time().
NewSocket should be set to NULL. |
---|---|
Out | On success: NewSocket and NewSocketHandle will contain the new socket and handle respectively. |
Creating a socket with this function seems very different to creating a socket using function 0x110 - this call clearly has a lot less information. How do you then set the socket up to use address families, etc.?
I have no more information than this.
I have no more information than this.
From the fact it uses a notify object, I guess that this function is asynchronous. I guess this is used to destroy sockets that have been created.
I have no more information than this.
I have no more information than this.
I have no more information than this.
I have no more information than this.
I have no more information than this.
I have no more information than this, but see footnote 2.
I have no more information than this.
These functions seem straighforward enough to use, but I have no experience of using them. I guess they could be used to implement non-blocking sockets.
I have no more information than this.
As I keep mentioning it, here is the libsocket home page.
Alfons Hoogervorst has compiled some information about finding out various IP details under Windows into ipdata.txt.
I have compiled a similar, but complementary, document called "Where IP Data is Stored for Network Cards under Windows".
A mailing list has been set up to discuss the use of WSOCK.VXD from a DOS box - the dossock mailing list. The scope of the mailing list can be wider than this - discussion of any method of networking from DOS programs is welcome.
The only known way of using Winsock 2 from a DOS box is via SOCK.VXD. I have documented this in the Coda SOCK.VXD Pseudo-Documentation.
Winsock API issues are discussed in the comp.os.ms-windows.programmer.tools.winsock newsgroup. The Winsock Programmers FAQ is the newsgroup's FAQ. These may be useful when programming for WSOCK.VXD, because the Winsock issues may also affect the VxD.
Dan Hedlund, for producing the first library to use WSOCK.VXD (Wsock).
Indrek Mandre, for making a nice BSD-style library from Dan Hedlund's library and then letting me look after it.
Alfons Hoogervorst, for doing lots of excellent low-level stuff, which inspired me.
Version 0.2.0 (2004-07-03) - Update the URL for the dossock mailing list. Update my e-mail address.
Version 0.1.9 - Updated the Get Socket Options and Set Socket Options sections. Corrections to struct linger in these sections.
Version 0.1.8 - Added a description of how WSOCK.VXD fails in the presence of Winsock 2.
Version 0.1.7 - Added a link to the Winsock programmer's newsgroup & FAQ
Version 0.1.6 - Added a link to the dossock mailing list. Fixed a couple of broken links.
Version 0.1.5 - Added a link to the CSOCK.VXD Pseudo-Documentation.
Version 0.1.4 - Update the section on socket ioctls.
Version 0.1.3 - I corrected the links for libsocket. I added new information on Select Start-up, Select Clean-Up, Get Socket Options and Set Socket Options. I also corrected an error in the section on socket lists. I added a link to some info on Windows IP data.
Version 0.1.2 - I uploaded my modified version of WSOCK.H & added links to it. Also added a note about packing data structures.
Version 0.1.1 - The link for ipdata.txt now points to a local link, as the previous link does not work anymore.
Version 0.1 - Original version