I’m working on a Windows port of a POSIX C++ program.
The problem is that standard POSIX functions like accept() or bind() expect an ‘int’ as the first parameter while its WinSock counterparts use ‘SOCKET’.
When compiled for 32-bit everything is fine, because both are 32bit, but under Win64 SOCKET is 64 bit and int remains 32 bit and it generates a lot of compiler warning like this:warning C4244: '=' : conversion from 'SOCKET' to 'int', possible loss of data
I tried to work around the issue by using a typedef:
#ifdef _WIN32
typedef SOCKET sock_t;
#else
typedef int sock_t;
#endif
and replacing ‘int’s with sock_t at the appropriate places.
This was fine until I reached a part of the code which calls OpenSSL APIs.
As it turned out OpenSSL uses ints for sockets even on Win64. That seemed really strange, so I started searching for an answer, but the only thing I found was an old post on the openssl-dev mailing list which refered to a comment e_os.h:
/*
* Even though sizeof(SOCKET) is 8, it's safe to cast it to int, because
* the value constitutes an index in per-process table of limited size
* and not a real pointer.
*/
So my question is:
is it really safe to cast SOCKET to int?
I’d like to see some kind of documentation which proves that values for SOCKET can't be larger than 2^32.
Thanks in advance!
Ryck
This post seems by the to be repeating the information on kernel objects at msdn:
Kernel object handles are process specific. That is, a process must either create the object or open an existing object to obtain a kernel object handle. The per-process limit on kernel handles is 2^24.
The thread goes on to cite Windows Internals by Russinovich and Solomon as a source for the high bits being zero.
The simple answer to this question is no. Take a look at the description of SOCKET values on MSDN [1]:
Windows Sockets handles have no restrictions, other than that the value INVALID_SOCKET is not a valid socket. Socket handles may take any value in the range 0 to INVALID_SOCKET–1.
So clearly, the API allows all values in the range [0, 2^64 - 1) on 64-bit Windows. And if the API ever returned a value greater than 2^32 - 1, assigning it to an int would result in handle truncation. Also have a look at the description of the return value from the socket() function [2]:
If no error occurs, socket returns a descriptor referencing the new socket.
Notice that it most emphatically does not promise to return a kernel handle. This makes any discussion about the possible values for kernel handles moot.
All that being said, as of this writing, the socket() function really does return a kernel handle (or something indistinguishable from a kernel handle) [3] and kernel handles really are limited to 32-bits [4]. But keep in mind that Microsoft could change any of these things tomorrow without breaking their interface contracts.
However, since a doubtless large number of applications have taken a dependency on these particular implementation details (and more importantly, so has OpenSSL), Microsoft would likely think twice about making any breaking changes. So go ahead and cast a SOCKET to an int. Just keep in mind that this an inherently dangerous, bad practice, and is never justifiable in the name of expediency.
Edit (2018-01-29)
Since this topic still seems to be of some interest, it's worth pointing out that it's quite easy to write portable sockets code in C++11 without resorting to questionable type casts:
using socket_t = decltype(socket(0, 0, 0));
socket_t s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With