libmodbus icon indicating copy to clipboard operation
libmodbus copied to clipboard

errno not reliable on Windows

Open zii-dmg opened this issue 9 years ago • 4 comments

libmodbus 3.1.4, OS Windows 7, mingw 4.9.2.

Errors in Windows socket functions do not set errno - instead you must call WSAGetLastError() to find what error occurred.

There are no calls to WSAGetLastError() in code except in _connect(). So Windows user can't check errors by errno (some errors can, other can't).

zii-dmg avatar Aug 08 '16 14:08 zii-dmg

Related to #133

stephane avatar Jun 21 '17 05:06 stephane

We could add the following code to the top of modbus-tcp.c:

#if defined(OS_WIN32)
# define WINSOCK_SET_ERRNO() errno = winsock_get_errno()
#else
# define WINSOCK_SET_ERRNO()
#endif

static int winsock_get_errno()
{
	switch (WSAGetLastError()) {
	case WSANOTINITIALISED:
		return WSANOTINITIALISED; // no posix error code available?
	case WSAENETDOWN:
		return ENETDOWN;
	case WSAEACCES:
		return EACCES;
	case WSAEADDRINUSE:
		return EADDRINUSE;
	case WSAEADDRNOTAVAIL:
		return EADDRNOTAVAIL;
	case WSAEFAULT:
		return EFAULT;
	case WSAEINPROGRESS:
		return EINPROGRESS;
	case WSAEINVAL:
		return EINVAL;
	case WSAENOBUFS:
		return ENOBUFS;
	case WSAENOTSOCK:
		return ENOTSOCK;
	}
}

The code maps winsock error codes to its posix equivalent. As an example I added only error codes from the bind() function. There is one special case WSANOTINITIALISED where it is not possible to map the error code. We could simply forward the winsock error code.

Now we need to insert the macro every time a socket function failed.

if (bind(new_s, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
    WINSOCK_SET_ERRNO();
    close(new_s);
    return -1;
}

What do you think? My intention is to let caller side code look the same on Windows and Linux.

timmi-on-rails avatar Oct 05 '18 21:10 timmi-on-rails

call WSAGetLastError and then set errno correspondingly

xiangqianyong avatar Oct 23 '19 03:10 xiangqianyong

timmi-on-rails said:

We could add the following code to the top of modbus-tcp.c:

--- < snip > ---

I would like to second that approach. Currently on Windows, when the client closes the connection in the middle of a indication message, after the recv() call errno is set to EWOULDBLOCK, but WSAGetLastError() returns WSAECONNRESET, which is the "correct" error code in this situation. In order to handle the situation properly, errno needs to get set from WSAGetLastError() as described above by timmi-on-rails .

I am not familiar enough with github and pull/pop/foo-requests, but could provide a patch if someone more knowledgeable jumps in... until then, I just patch my local copy :-/

ralfixx avatar May 06 '21 10:05 ralfixx