Calling accept() causes WSAEFAULT 10014 Bad address - c++

I'm writing a custom TCP server for Windows, using MinGW compiler and winsock2 API.
I have this piece of code:
TCPSocket TCPSocket::accept() {
TCPSocket clSocket;
struct sockaddr_in clAddr;
socklen_t clAddrSize;
clAddrSize = sizeof(clAddr);
clSocket.shared->sockFd = ::accept(shared->sockFd, (struct sockaddr *)&clAddr, &clAddrSize);
if (clSocket.shared->sockFd < 0) {
printf("failed to accept incoming connection (code: %d)\n", WSAGetLastError());
throw SocketException(6, "failed to accept incoming connection");
}
clSocket.shared->buffer = new byte [BUFFER_SIZE];
clSocket.shared->curPos = clSocket.shared->endPos = clSocket.shared->buffer;
return clSocket;
}
However after calling accept() i get
failed to accept incoming connection (code: 10014)
which is according to MSDN:
WSAEFAULT
10014
Bad address.
The system detected an invalid pointer address in attempting to use a pointer argument of a call. This error occurs if an application
passes an invalid pointer value, or if the length of the buffer is too
small. For instance, if the length of an argument, which is a sockaddr
structure, is smaller than the sizeof(sockaddr).
I don't see, how these pointers can be bad, they both directly address a local variable. The clAddrSize is initialized and shared->sockFd is initialized in another function
void TCPSocket::listen(uint16_t port, int backlog) {
struct addrinfo * ainfo;
char portStr[8];
int res;
if (shared->sockFd != -1)
logicError(1, "socket already initialized, need to close first");
snprintf(portStr, sizeof(portStr), "%hu", (ushort)port);
if (getaddrinfo("localhost", portStr, NULL, &ainfo) != 0)
systemError(2, "failed to retrieve info about localhost", false);
shared->sockFd = socket(ainfo->ai_family, SOCK_STREAM, IPPROTO_TCP);
if (shared->sockFd < 0)
systemError(3, "failed to create a TCP socket", false);
res = bind(shared->sockFd, ainfo->ai_addr, ainfo->ai_addrlen);
if (res != 0)
systemError(5, "failed to bind socket to local port", true);
res = ::listen(shared->sockFd, backlog);
if (res != 0)
systemError(6, "failed to set socket to listen state", true);
freeaddrinfo(ainfo);
}
Do you see anything that i overlooked?

Ok, so thanks to CristiFati i found the problem.
The function getaddrinfo("localhost", portStr, NULL, &ainfo) used that way was returning an IPv6 address. While accept was getting sockaddr_in, which is a struct for IPv4 address.
It could be probably solved more ways, for example
using sockaddr_in6 for IPv6 communication
telling getaddrinfo to to search only IPv4 results with 3rd argument
picking up next result in the linked list returned by getaddrinfo
But i chose to manualy init the socket for IPv4 protocol this way:
struct sockaddr_in myAddr;
memset(&myAddr, 0, sizeof(myAddr));
myAddr.sin_family = AF_INET;
myAddr.sin_port = htons((ushort)port);
shared->sockFd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (shared->sockFd < 0)
systemError(3, "failed to create a TCP socket", false);
res = bind(shared->sockFd, (struct sockaddr *)&myAddr, sizeof(myAddr));
if (res != 0)
systemError(5, "failed to bind socket to local port", true);
Since that, everything works.

Related

Why isn't AF_INET working with SOCK_STREAM?

I'm just starting out on gaining a better understanding of socket programming, and I'm trying to build a simple program that can send and receive messages. I've run into an issue with binding a socket to an address to use it. Here is what I have-
#include "stdafx.h"
using namespace std;
int main()
{
bool devbuild = true;
WSADATA mainSdata;
SOCKET sock = INVALID_SOCKET;
sockaddr tobind;
tobind.sa_family = AF_INET;
char stringaddr[] = "192.168.1.1";
inet_pton(AF_INET,stringaddr,&tobind);
//initiating Windows Socket API (WSA)
if (WSAStartup(2.2, &mainSdata) == 0)
{
if (devbuild == true)
{
printf("WSA successfully started...\n");
}
}
else
{
printf("WSA failed to set up, press [ENTER] to exit...\n");
pause();
return 1;
}
//instantiating the socket
sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, NULL);
if (sock != INVALID_SOCKET)
{
if (devbuild == true)
{
printf("Socket successfully created...\n");
}
}
else
{
printf("Socket failed to set up, press [ENTER] to exit...\n");
pause();
return 2;
}
//binding the socket
if (bind(sock, &tobind, sizeof(tobind)) == 0)
{
if (devbuild == true)
{
printf("Socket successfully bound...\n");
}
}
else
{
printf("Socket failed to bind, press [ENTER] to exit...\n");
printf("Last WSA error was: %d", WSAGetLastError());
pause();
return 3;
}
pause();
return 0;
}
I'm getting a return of 3, with WSA error code 10047
10047 - WSAEAFNOSUPPORT
Address family not supported by protocol family.
An address incompatible with the requested protocol was used. All sockets are created with an associated address family (that is, AF_INET for Internet Protocols) and a generic protocol type (that is, SOCK_STREAM). This error is returned if an incorrect protocol is explicitly requested in the socket call, or if an address of the wrong family is used for a socket, for example, in sendto.
This doesn't make sense, because I am only using SOCK_STREAM and AF_INET, which support one another.
I believe one problem (possibly not the only problem, but this is what jumps out at me) is in this line:
inet_pton(AF_INET,stringaddr,&tobind);
The problem is that you are passing &tobind as the final argument, and tobind is a sockaddr, but inet_pton() expects its third argument to point to a struct in_addr instead when using AF_INET (the fact that inet_pton() takes a void-pointer rather than a typed pointer for its third argument makes this kind of mistake really easy to make).
So what you should be doing instead is (note added error checking also):
if (inet_pton(AF_INET,stringaddr,&tobind.sin_addr) != 1)
printf("inet_pton() failed!\n");
Also, you need to make tobind be of type struct sockaddr_in rather than just a sockaddr, and also you need to zero out the struct before using it:
struct sockaddr_in tobind;
memset(&tobind, 0, sizeof(tobind)); // make sure the uninitialized fields are all zero
tobind.sa_family = AF_INET;
[...]

Socket programming in Linux by C++

I am developing a C++ app in openSUSE 12.3 and one of it's part is responsible to send data to a device via Socket (in LAN). I am using this code
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *printer;
portno = 9100;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) error("ERROR opening socket\n");
printer = gethostbyname("100.0.69.23");
if(printer == NULL) error("No such device on 100.0.69.23\n");
//set bit set to zero
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *) printer->h_addr, (char *) &serv_addr.sin_addr.s_addr, printer- >h_length);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0)
{error("ERROR connecting");
return;
}
n = write(sockfd, data, datalenght);
if(n < 0) error("ERROR sending command to printer");
n = read(sockfd, buffer, 200);
I think the code is correct but the connect function returns -1 and seems that could not connect to the device (printer) . This code was written in openSUSE 11 and was working OK and I could send/receive data to device but when I copy/paste it to new system (openSUSE 12.3) it gives me failure in connecting. I ping result on the specific IP which is in use show that device is reachable via LAN
I think you should consider the possibility that hostent returned by gethostbyname function might have AF_INET6 address family (in which case it will be IPv6 instead of IPv4 address).
http://linux.die.net/man/3/gethostbyname
So you can either use GNU extension function gethostbyname2 function that will allow you to specify address family.
printer = gethostbyname2("100.0.69.23", AF_INET);
Or instead you can use getaddrinfo function, as gethostbyname function is said to be obsolete, by the documentation.
As already mentioned, you are checking for printer == NULL before initializing it. I think you meant the following instead:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket\n");
printer = gethostbyname("100.0.69.23");
...
Also the structure of the code seems to indicate that when you want to send a command to the printer you connect(), write() then read(), which is OK if you are only ever sending one command, but suboptimal if you are sending multiple commands. In the latter case you want to separate the connect() from the write() as it's fairly expensive to connect so you want to do it just once.

C++ winsock connection refused when connecting to IPv4 address

I'm programming a server/client system using winsock2 and it works great when I connect the client to the server name or the server IPv6 address. However, when I use the server IPv4 address I get error "Connection refused" from the call to connect() in the client.
This error occurs with either my client or using telnet. However, I can successfully ping the server using either of the three name, IPv4 or IPv6.
I've tried this running both server and client on the same machine, on separate machines, and firewalls deactivated on all machines.
Here is an excerpt of my server initialization and listening code:
SOCKET sockfd = INVALID_SOCKET, in_socketID;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
sockaddr_storage incoming_addr;
int addr_size;
int tmp_err;
const char *sPort = "20152";
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
tmp_err = getaddrinfo(NULL, sPort, &hints, &servinfo);
if (tmp_err != 0)
throw exception("ERROR: getaddrinfo failed");
// loop through all the results and bind to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
ip = p;
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd == INVALID_SOCKET)
{
cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
} // end if
else if (bind(sockfd, p->ai_addr, p->ai_addrlen) == SOCKET_ERROR)
{
cerr << "ERROR on bind(): " << WSAGetLastError() << endl;
closesocket(sockfd);
sockfd = INVALID_SOCKET;
} // end if
} // end for
if (sockfd == INVALID_SOCKET)
{
// looped off the end of the list with no successful bind
throw exception("ERROR: Failed to bind socket");
}
// clean up
if (servinfo)
freeaddrinfo(servinfo);
if (listen(sockfd, SOMAXCONN ) == SOCKET_ERROR)
throw exception("Listen failed");
while (true)
{
memset(&incoming_addr, 0, sizeof(incoming_addr));
addr_size = sizeof(incoming_addr);
in_socketID = accept(socketID, (sockaddr *)&incoming_addr, &addr_size);
// do stuff with incoming connection
}
This is my client code:
int sockfd = INVALID_SOCKET;
struct addrinfo hints;
struct addrinfo *servinfo = NULL;
struct addrinfo *p;
struct addrinfo *ip;
int tmp_err;
const char *sHost = "192.168.1.136";
const char *sPort = "20152";
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC; // either IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // use TCP
tmp_err = getaddrinfo(sHost, // web address or ip to connect to
sPort, // port or protocol
&hints, // initialized hints structure
&servinfo); // return structure
if (tmp_err != 0)
throw exception("ERROR: getaddrinfo failed");
// loop through all the results and connect to the first we can
for(p = servinfo; p != NULL && sockfd == INVALID_SOCKET; p = p->ai_next)
{
ip = p;
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd == INVALID_SOCKET)
{
cerr << "ERROR on socket(): " << WSAGetLastError() << endl;
//continue;
} // end if
else if (connect(sockfd, p->ai_addr, p->ai_addrlen) < 0)
{
cerr << "ERROR on connect(): " << WSAGetLastError() << endl;
closesocket(sockfd);
sockfd = INVALID_SOCKET;
//continue;
} // end if
} // end for
if (sockfd == INVALID_SOCKET)
throw exception("ERROR: Failed to connect");
// clean up
if (servinfo)
freeaddrinfo(servinfo);
// do stuff with new socket
I already read several similar questions in the site, but none answered this issue.
How can I connect also to the server IPv4 address? I need help, please.
Thanks.
EDIT:
From a suggestion given by user Sorayuki, I made some changes just to test if his theory was correct.
I was able to connect to the IPv4 by changing on the server
hints.ai_family = AF_UNSPEC;
to
hints.ai_family = AF_INET;
I knew it would obviously work, but when I do this, of course IPv6 doesn't work.
It appears user Sorayuki was right and my loop was connecting to IPv6.
It seems that there is no easy way to unify IPv6 and IPv4. Your socket must listen to either one or the other which makes the process really annoying.
According to the documentation, the old style to listen to both IPv4 and IPv6 is to create a socket for each and listen on both. This is for Windows Server 2003 and Windows XP SP1.
The preferred modern style (Windows Vista, 7 and 8) is to turn your socket into a dual socket and it will listen to both IPv4 and IPv6. However, your client must also be able to set up a dual socket, so, if your application is serving an old client, you are stuck with the old method.
Thanks!
This is because binding to an IPv6 address does not magically bind to an IPv4 address as well.
On Linux, by default binding to [::] will cause IPv6 and IPv4 to work (unless /proc/sys/net/ipv6/bindv6only is set to 1).
However, on Mac OS X and Windows, binding to [::] will only work for IPv6. You must also bind to an IPv4 address (or 0.0.0.0) for it to work.
Your logic described in your comment "loop through all the results and bind to the first we can" is precisely the problem here. You should both bind to [::] with the IPV6_V6ONLY flag (see setsockopt()) and 0.0.0.0.
Is it because that you bind your server socket to an IPv6 address?
in the "for" loop, IPv6 address appearing before IPv4 address seems to cause your server's socket listen on an IPv6 address.
So your server is not listening on any IPv4 address, cause all connection towards IPv4 address of server is refused.
Try to see all listening port is on which IP address with tool or some command (eg. netstat)
have you tried to run the server and client on the same machine?
this sounds like a firewall problem.
if you succeed connecting telnet / your application on the same machine you'll know this is the problem.

setsockopt: Bad file descriptor in C++

I have the famous error "address already in use" because I have no check for the bind function.
Here is my code:
memset(&(this->serv_addr), 0, sizeof(this->serv_addr));
this->serv_addr.sin_family = AF_INET;
this->serv_addr.sin_port = htons(port);
this->serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int yes = 1;
if (setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int)) == -1) {
perror("setsockopt");
exit(1);
}
But running code I got this:
setsockopt: Bad file descriptor
The code is right, from the guide Beejnet.
But why I got the error?
Maybe the position of the code is wrong?
The first the that sock_fd is called is in the function w_socket:
int retv;
retv = socket(AF_INET, SOCK_STREAM, 0);
if(retv == -1)
{
std::string err_msg(strerror(errno));
err_msg = "[socket] " + err_msg;
throw err_msg;
}
else
{
int reuse_opt = 1;
setsockopt(this->sock_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_opt, sizeof(int));
return retv;
}
}
By default there's the sesockopt but no check.
I've tried but it doesn't work.
You need to first create the socket via the socket call, like:
sock_fd = socket(PF_INET, SOCK_STREAM, 0);
(and check the return value; see man 2 socket for details)
Only then you may do your setsockopt call. Before the call to socket, your sock_fd variable will contain a random value (or 0) instead of a socket file descriptor.
Edit after updated question:
Your call to setsockopt needs to use retv instead of this->sock_fd as at that point in time, the this->sock_fd variable is not yet containing the result of your call to socket.

Reopen connected datagram socket

I have a connection protocol that has been defined by our customer. Data are sent between two linux computers using UDP and TCP protocols. The IP addresses and ports are fixed on startup.
We are sending messages at 200 Hz and I have been using connect to save some time on the transmissions.
My problem is that if there is a communication error, I need to tear down the connections and reinitialise.
I have a problem with one of the UDP connections as it will not rebind to the required address and returns errno 22.
The code I am using is something like:
int
doConnect(int& sock, int local_port, char *local_ip, int remote_port, char *remote_ip)
{
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(sockaddr_in);
addr.sin_family = AF_INET;
addr.sin_port = htons(local_port);
inet_pton(local_ip,&addr.sin_addr.s_addr);
if (0 > bind(sock, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("Bind Error errno = %d\n", errno);
return ERR_BIND;
}
memset(&addr, 0, sizeof(sockaddr_in);
addr.sin_family = AF_INET;
addr.sin_port = htons(remote_port);
inet_pton(remote_ip,&addr.sin_addr.s_addr);
if (0 > connect(sock, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("Connect Error errno = %d\n", errno);
return ERR_CONNECT;
}
return ERR_OK;
}
The way that this is used is like this:
int s1(-1), s2(-1);
doConnect(s1, 31003, "172.17.21.255", 31006, "172.17.21.1");
doConnect(s2, 31001, "172.17.21.3", 31004, "172.17.21.1");
When an error occurs
close(s1);
close(s2);
doConnect(s1, 31003, "172.17.21.255", 31006, "172.17.21.1");
doConnect(s2, 31001, "172.17.21.3", 31004, "172.17.21.1");
Here the local address is 172.17.21.3 and I am connecting to 172.17.21.1. s1 listens to a broadcast message.
s1 successfully reconnects to the remote machine, but s2 fails with error 22 from the call to bind.
I have tried explicitly calling bind and connect to an AF_UNSPEC address immediately before I close the socket. This doesn't solve the problem.
Are there any options that I should be using?
Perhaps you could try:
int val = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
I also suggest you double check that you're not passing the same socket to the two consecutive doConnect() calls (as errno 22 = EINVAL, which in the case of bind() appears to mean that the socket is already bound to an address).
The underlying socket layer might hold the port & IP address still open, even after your call to close. Try some of the following:
do a sleep(10) (or more) between the close and the call to doConnect again
configure the sockets using setsockopt with the SO_LINGER set to off
This actually happens more commonly with TCP connections, but I see no reason UDP can't have this problem as well.