connect to external IP fails - c++

I have been stuck with this problem for ages...
So I coded a game with multiplayer using the BSD sockets/Winsock 2 TCP. My server is up and working, I can connect to it locally (indirectly by filling in my own external ip). Everything seems to be fine. I tested with canyouseeme.org whether my ip and server port were properly port forwarded, and it was perfectly working. Then I sent my client to a friend to test it out by connecting to the server, and it failed all the time. I expected him to have connected, but I could not see anything on the server side (while canyouseenme.org even popped up as invalid client). I do not get it, do I have to do something special for external connecting in TCP?
Here is my connect function:
int CConnectSocket(CSocket s, unsigned short port, char* ipaddress)
{
SOCKADDR_IN target;
target.sin_family = AF_INET;
target.sin_port = htons(port);
target.sin_addr.s_addr = htonl(inet_addr(ipaddress));
if (connect(s, (SOCKADDR*)&target, sizeof(target)) == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAEISCONN)
return CTCP_DONE;
else if (WSAGetLastError() != WSAEWOULDBLOCK && WSAGetLastError() != WSAEALREADY)
{
strcpy(inerr, "CConnectSocket: Failed to connect the socket. Error ");
char f[6];
sprintf(f, "%d", WSAGetLastError());
strcat(inerr, f);
return CTCP_ERROR;
}
return CTCP_WAIT;
}
return CTCP_DONE;
}
I keep looping this function until CTCP_DONE is returned (meaning I connected). My friend reported he timed out (so it returned CTCP_WAIT for too long). Again, I'm sure the client is the fault because my server was able to accept an external connection from canyouseeme.org. The IP address and port I fill in work because I can connect locally. If I change the port, local connection doesn't work anymore (proof that it is a correct argument). Changing the IP does yield the same result.
I appreciate any help! Hopefully I can solve this problem and begin the multiplayer.
EDIT
void CEngine::CSetSockAddr(CSockAddr* address, unsigned short port, char* ipaddress)
{
memset(address, 0, sizeof(CSockAddr));
address->sin_family = AF_INET;
address->sin_port = htons(port);
address->sin_addr.s_addr = htonl(inet_addr(ipaddress));
}
This is my socket address function. When I remove the htonl function, it works at my friend's side, but no at my side any more (I also host the server). My friend BTW uses XP, but since I called to use 2.2 WinSock that should be no issue. At least I hope that the setup function for WinSock guarantees a 2.2 implementation if it returns true.
EDIT 2
Hello guys,
I have got everything up and working now. It seems that if I add htonl I can connect as the host with the external IP rather than connecting with the loopback 127.0.0.0.1 or sth like that. So now I would like to know, how do I detect whether I am connecting to a server that I host? That would be a nice trick. Happy new year all!

Is very difficult to help you without a console... but you can talk with your friend and try this:
From your friend's PC, make a ping against your server.
If you recieve an answer, use tracert or traceroute to find out what's the problem
What's the route model of your friend? probably it has a System Event Log where you can check for rejected connections.
Luck!

You need to remove the htonl() call when calling inet_addr(). connect() expects the IP to be in network byte order, and inet_addr() returns the IP in network byte order, so there is no need to swap the bytes, on any platform.
Try this:
int CConnectSocket(CSocket s, unsigned short port, char* ipaddress)
{
SOCKADDR_IN target = {0};
target.sin_family = AF_INET;
target.sin_port = htons(port);
target.sin_addr.s_addr = inet_addr(ipaddress);
if (connect(s, (SOCKADDR*)&target, sizeof(target)) == SOCKET_ERROR)
{
int err = WSAGetLastError();
switch (err)
{
case WSAEISCONN:
return CTCP_DONE;
case WSAEWOULDBLOCK:
case WSAEALREADY:
return CTCP_WAIT;
default:
sprintf(inerr, "CConnectSocket: Failed to connect the socket. Error %d", err);
return CTCP_ERROR;
}
}
return CTCP_DONE;
}

Related

client socket is bindable but not connectable, because already in use

I write a client, where i have to bind the client socket. This works fine. After that i try to connect the Socket and i get error 10048. (Address already in use.) I don't understand how this is possible.
I have to implement a client speaking with multiple server. Every server only accepts messages from a specific port. (every Server expects a different port). so i have to bind my client socket. The code above is to create one of these sockets.
My code works some times. But very often the connect gives me the error 10048, while the binding before was fine. I know that bind can give also the error 10048 if the socket is already in use. But it doesn't. It returns 0. So i guess the port is free. Immediate after binding i call connect and get error 10048. I do not understand why? At the moment of the binding the port was obviously free.
bool TEthernetSocket::Open()
{
WSADATA wsaData;
if (WSAStartup((MAKEWORD(2, 0)), &wsaData) == SOCKET_ERROR)
{
return IsConnected();
}
Socket = socket(AF_INET, SOCK_STREAM, 0); // TCP
if (Socket == INVALID_SOCKET)
{
return false;
}
//bind Socket
struct sockaddr_in sa_loc;
memset(&sa_loc, 0, sizeof(struct sockaddr_in));
sa_loc.sin_family = AF_INET;
sa_loc.sin_port = htons(ClientPort);
sa_loc.sin_addr.s_addr = inet_addr(IPClient.substr(0, 15).c_str());
CALL_TRACE_CB("ethernetSocket connected");
if (!(bind(Socket, (struct sockaddr*)&sa_loc, sizeof(struct
sockaddr))))
{
CALL_TRACE_CB("Bind works");
}
else
{
AnsiString msg = AnsiString().sprintf("EN: error socket Bind:
%d", WSAGetLastError());
CALL_ERROR_CB(ERROR_NO_PORT_HANDLE, msg.c_str());
Close();
}
// TCP
SOCKADDR_IN sAdd;
sAdd.sin_family = AF_INET;
sAdd.sin_port = htons(Port);
sAdd.sin_addr.s_addr = inet_addr(IP.substr(0, 15).c_str());
if (connect(Socket, (SOCKADDR*)&sAdd, sizeof(SOCKADDR_IN)) ==
SOCKET_ERROR)
{
AnsiString msg = AnsiString().sprintf("EN: error connect
errorcode: %d", WSAGetLastError());
}
}
I expect that bind() returns 10048 before connect returns this error, but actual only connect() returns this error
I have to implement a client speaking with multiple server. The server only accepts messages from a specific port, so i have to bind my client socket.
This is an unsolvable problem. When you make an outbound TCP connection, the combination of local IP address and port is reserved exclusively for that particular outbound TCP connection. If you need to make multiple outbound TCP connections from the same port, each will have to be bound to its own local IP address. That is going to be extremely inconvenient.
There are other problems too. Say you finish one connection and then try to start a new one. The new one will have the same local IP address, local port (since the server only tolerates one), remote IP address, and remote port. How will packets from the new connection be distinguished from old, stale packets from the old one?
This is why you get the error when you try to connect. It's not until all four parameters of the connection (local and remote address and local and remote port) are known that the collision with the prior connection is detectable. That's not known until you call connect.
You need to fix the server to ignore the source port. If that absolutely cannot be done, you'll have to adopt a wait and retry mechanism to handle collisions with past connections.

UDP Broadcasting using winsock on a win 10 machine not working

I'm making a LAN multiplayer game using c++ and winsock, were I've created my own protocol for connecting two machines to eachother. The protocol involves broadcasting a message over the local LAN, which strangely isn't working on one of the two machines I'm using to test my code. The strange part is that it's as mentioned working on one machine, whereas not on the other. I've used wireshark to monitor outgoing packets, and the packets isn't being sent on the failing machine, even though that sendto() is returning the correct amount of bytes. The only difference between the machines is that one (the failing one) is using win10 and the other win8.
Is there any difference in the winsock library/networking layer between windows 10 and windows 8 that could cause this? Else, do you have any other ideas of what could cause the failure?
The code for sending a broadcast looks like this:
sockaddr_in send_addr;
send_addr.sin_family = AF_INET;
send_addr.sin_port = htons(PORT);
send_addr.sin_addr.s_addr = inet_addr("255.255.255.255");
int iResult = sendto(sock,
reinterpret_cast<char*>(&packet),
sizeof(Packet),
0,
(SOCKADDR *)&send_addr,
sizeof(send_addr));
if (iResult == SOCKET_ERROR)
{
printf("Failed to send broadcastmsg");
}
And the code to recieve it looks like this:
sockaddr_in sender_addr;
int sender_addrLen = sizeof(sender_addr);
Packet recvdPacket = {};
int iResult = recvfrom(sock,
reinterpret_cast<char*>(&recvdPacket),
sizeof(recvdPacket),
0,
(SOCKADDR*)&sender_addr,
&sender_addrLen);
if (iResult > 0)
{
return recvdPacket;
}
return Packet{};
You need to enable broadcast setting SO_BROADCAST before sending a broadcast message: https://msdn.microsoft.com/en-us/library/windows/desktop/ms740476(v=vs.85).aspx ,
Why do we need SocketOptions.SO_BROADCAST to enable broadcast?
char broadcast = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof(broadcast))
Also you should use directed broadcast (for example, 192.168.0.255) instead of Internet broadcast (255.255.255.255). I believe you don't need Internet broadcast.
Also, can you print the value returned by sendto? is iResult == sizeof(Packet)?
Last, which is the size of Packet? Is it a class? you are reinterpreting &packet as a char *. You must be sure there is no error there.
Could broadcast be blocked in the Win10 PC? I don't know if it's possible.
Consider using multicast.

Receiving broadcast packet addressed to 255.255.255.255 in C++

I have a device that is discovered by sending a broadcast packet to 255.255.255.255 on port 4930 and the device responds by sending a packet back to 255.255.255.255 on port 4930.
I have a snippet of C++ code which can send a packet to 255.255.255.255 on port 4930 (both source and destination port), but it can't receive a packet back from the broadcast address 255.255.255.255.
I can see the device is working fine, wireshark can see the packets coming back and forth and the propriety software supplied with the device can discover the device just fine, the problem is with the C++ program so please keep on topic with your responses.
Now, as I have said I can send a packet just find, but firstly I can't bind to the IP address 255.255.255.255 to receive the packets. I can change the multicast address to 239.255.255.250 and the socket will bind but I need the address 255.255.255.255.
My snippet of code is below, I am using VC++2010
bool CPTUProgramDlg::FindPTU(u_short port, const char * Destaddress){
{
//Data to send
char packet_data[10] = {0x44,0x43,0x55,0x44,0x5f,0x50,0x49,0x4e,0x47,0x00};
int packet_size=10;
SOCKET sock;
struct sockaddr_in addr;
sock = socket(AF_INET, SOCK_DGRAM, 0);
// set SO_BROADCAST on a socket to true (1): (so we can transmit to 255 addr)
//In order to use broadcast the options of socket must change
char broadcastON = 1;
setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &broadcastON, sizeof broadcastON);
if (sock < 0)
return false;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(Destaddress); // Specify dest IP
sendto(sock, packet_data, packet_size, 0, (struct sockaddr*)&addr, sizeof(addr));
if (bind(sock,(struct sockaddr*) &addr,sizeof(addr)) != -1){
char Buff[512];
recv(sock,Buff,512,0);
}
closesocket(sock);
}
return 1;
}
Wireshark screenshot to prove packets are being send:
From the wireshark output its seen that the special device is using broadcast to communicate and will use the same port number as source and destination.
Normal socket communication will require using matching port numbers but broadcast messages cannot be exchanged over the same socket, especially when the port numbers do not match as seen with wireshark.
Binding on 255.255.255.255 (INADDR_BROADCAST) should generally work but may be limited by your OS privileges and permissions.
You may try to solve the problem by using two sockets - one for receiving and one for sending. Of course the listening socket have to be setup first and bound to 0.0.0.0 (INADDR_ANY) and port 4930. In this case there is no easy way to filter by destination address (as I wrongly written in my comment) because most standard socket APIs do not provide a way to get the destination addess from the socket. On Linux there is an exception - IP_PKTINFO at SOL_IP...
By using recvfrom you will get the source unicast address of the responding device(s). You have to note that if you have more that one such device on your network you will get more than one response.

Receiving UDP broadcast

I have to receive an UDP broadcast (in Ubuntu if that makes any difference). Using Wireshark, I can see the packet being sent from the server machine, and I can see it being received by my client machine, but my program is completely oblivious. This is what I have:
sockaddr_in si_me, si_other;
int s;
assert((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))!=-1);
int port=6000;
int broadcast=1;
setsockopt(s, SOL_SOCKET, SO_BROADCAST,
&broadcast, sizeof broadcast);
memset(&si_me, 0, sizeof(si_me));
si_me.sin_family = AF_INET;
si_me.sin_port = htons(port);
si_me.sin_addr.s_addr = INADDR_ANY;
assert(::bind(s, (sockaddr *)&si_me, sizeof(sockaddr))!=-1);
while(1)
{
char buf[10000];
unsigned slen=sizeof(sockaddr);
recvfrom(s, buf, sizeof(buf)-1, 0, (sockaddr *)&si_other, &slen);
printf("recv: %s\n", buf);
}
It is compiled in debug mode, the asserts aren't being erased during compilation, and my program just blocks on recvfrom.
Is there any other hoop I have to jump through to receive an untargeted UDP broadcast?
Edit: just a bit more info, I have the two computers connected on a dedicated switch, no outside interference. I also have a second network card on my client computer that connects to the company network, which also works.
I can ping both the outside (Internet working) and my server machine (plus I can see the actual packets in Wireshark), but you never know what might cause this problem.
As it turns out my code is perfectly fine, as I thought it would be. There was a problem with the network setup itself.
For posterity, I had set up two static IP'd computers on their own hub, instead of using the built in DHCP server on the server machine to allocate the IP address for the other computer. Pretty localized for my problem but you never know..
To send and receive broadcast
Be sure that netmask is correct. in windows mask for broadcast packets does not matters, but not in linux.
bind socket to INADDR_ANY
setsockopt to BROADCAST
call sendto with sendaddr.sin_addr.s_addr = inet_addr("your_interface_broadcast_address").
or - call sento several times for each interface with its broadcast ip address.
call recvfrom. any time before calling recvfrom, set up length parameter.

Socket is invalid while hooking WSASend/WSARecv on the server

I am hooking WSASend, and WSARecv in C++ using the same method I've used to hook the client's WSASend and WSARecv functions. In the client I am able to get the IP, Port, and Socket from the SOCKET structure passed by WSASend/WSARecv; however, for the server when I try to use getpeername or getsockname() they both return the error 10057 (Socket not connected)...
I'm fairly sure that the hook is correct on the server, since it prints the bytes successfully, and I'm also sure the socket SHOULD be valid seeing how client and server establish a successful connection.
Is there a way to resolve this problem by any other alternative methods? I've been looking around the internet to find a solution, but I haven't seen anyone with the same problem.
I've tried this:
sockaddr *address = new sockaddr;
int peer_len;
getpeername(s, address, &peer_len);
int err = WSAGetLastError();
if(err==0)
{
char *Str = inet_ntoa(((sockaddr_in*)address)->sin_addr);
printf("[%s", Str);
printf(":%d]",ntohs(((sockaddr_in*)address)->sin_port));
}
else
{
printf("Error %i\n",err);
}
(Using both getpeername and getsockname)Both result in the same socket not connected error.
I'm planning on using the packets the C++ dll gets and forward the information to the C# dll since it'll be easier to manage on that (for me anyways), but I'd need to distinguish each packet with it's socket id.
You can only do that on the connected socket, i.e. the one returned from the accept() call, not on the listening "server" socket.