SOCKET s;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
SOCKADDR_IN sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
sin.sin_addr.s_addr = inet_addr(gethostbyname("www.steamcommunity.com") -> h_addr_list[0]);
cout << gethostbyname("www.steamcommunity.com") -> h_addr_list[0]; /* this prints "?ф▀gwww.steamcommunity.com" in cmd */
if(SOCKET_ERROR == connect(s, (sockaddr *)&sin, sizeof(sockaddr))){
cout << "\nconnect = SOCKET_ERROR "; /* error and exit the function */
return;
}
Whats wrong here? I add some more details to to to otto to.
Two problems:
You're asserting that sin contains an AF_INET address, but then not checking whether gethostbyname() have given you an AF_INET address
gethostbyname() gives you a binary address. inet_addr() takes a textual address ("1.2.3.4") and converts it into a binary address. You need something like:
sin.sin_addr = *(in_addr *) gethostbyname("www.steamcommunity.com")->h_addr_list[0];
(The use of char * as the type of *h_addr_list is just C's way of saying "it's a bunch of bytes", rather than any sort of readable string.)
However:
Please use getaddrinfo() instead of gethostbyname(). MSDN says: "The gethostbyname function has been deprecated by the introduction of the getaddrinfo function. Developers creating Windows Sockets 2 applications are urged to use the getaddrinfo function instead of gethostbyname" (for all sorts of reasons).
You can't assume that your DNS (or whatever) lookup will return exactly on AF_INET address. It can return any number of any sort of address - that's one reason why getaddrinfo() exists.
Please check return values:
socket() can fail.
gethostbyname() can return NULL: you need to check this before dereferencing the return value as a struct hostent *.
Related
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.
I'm trying to make a simple botnet(not for evil purposes) with winsock, the client is ok(at least at the syntax), but the server has error when i call the accept function, it returns SOCKET_ERROR, i called WSAGetLastError() to get the error number and it returned 10014. In the MSDN page, it says this:
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).
Well, i don't have any idea of what to do.
botnetserver.cpp
#include <winsock2.h>
#include <windows.h>
#include <iostream>
#define PORT 5051
#define BUFFMAX 1024 // Buffer max
using namespace std;
int main() {
SOCKADDR_IN svaddr; // server address
SOCKADDR_IN claddr; // client addres
SOCKET listensocket;
SOCKET client;
WSADATA WsaData;
char buffer[BUFFMAX];
int i = sizeof(client);
//ShowWindow(GetConsoleWindow(), SW_HIDE);, fail
WSAStartup(MAKEWORD(2, 2), &WsaData);
listensocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
svaddr.sin_family = AF_INET;
svaddr.sin_port = htons(PORT);
svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(listensocket, (SOCKADDR*) &svaddr, sizeof(svaddr));
client = listen(listensocket, 5)) == SOCKET_ERROR
accept(listensocket, (SOCKADDR*)&claddr, &i) // Error here
while(true) {
/* other things i tried
cout << "\n\n" << buffer << "\n\n";
cout << o << "\n";
buffer[BUFFMAX] = '\0';*/
recv(client, buffer, BUFFMAX, 0);
if(strcmp(buffer, "<fim>") != 0) {
system(buffer);
//break;
} else {break;}
}
closesocket(client);
closesocket(listensocket);
WSACleanup();
system("pause");
}
There are two mistakes in your code:
int i = sizeof(client);
i needs to be initialized as sizeof(claddr) instead. This is what accept() is failing on. sizeof(client) is smaller than sizeof(claddr) so accept() thinks your claddr buffer is too small to receive the client's IP address. This is clearly stated in the documentation you quoted:
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).
client = listen(listensocket, 5)) == SOCKET_ERROR
client is a SOCKET handle. You cannot assign the result of the == operator to a SOCKET. You need to assign the result of accept() to client instead.
Change those lines to look like this instead:
int i = sizeof(claddr);
...
listen(listensocket, 5);
client = accept(listensocket, (SOCKADDR*)&claddr, &i);
With that said, you also need to fix your recv() loop. recv() does not return null-terminated data, but strcmp() requires that. You need to null-terminate the buffer after reading, or use strncmp() instead, using the result of recv() as the buffer length. And you need to take into account that it may take multiple calls to recv() to receive <fim>, so you need to implement proper buffering.
And, you need to add proper error handling on ALL function calls.
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.
in code :
if ((host = (struct hostent*) gethostbyname(address) ) == 0) // address is a string
I've got warning when cross compiling (generic arm architecture) on 4.5.x gcc :
(.text+0x1558): warning: gethostbyname is obsolescent, use getnameinfo() instead.
getnameinfo is:
int WSAAPI getnameinfo(
__in const struct sockaddr FAR *sa,
__in socklen_t salen,
__out char FAR *host,
__in DWORD hostlen,
__out char FAR *serv,
__in DWORD servlen,
__in int flags
);
And it got more parameters... And I'm confused with it, I just need it work as gethostbyname were working. What parameter to pass to keep it simple stupid as it was with gethostbyname?
Finally here is my try:
struct sockaddr_in servAddr;
struct hostent *host; /* Structure containing host information */
/* open socket */
if ((handle = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0)
return LILI_ERROR;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = AF_INET;
servAddr.sin_addr.s_addr = inet_addr(address.ptr());
servAddr.sin_port = htons(port);
char servInfo[NI_MAXSERV];
if ( ( host = (hostent*) getnameinfo(
(struct sockaddr *) &servAddr
,sizeof (struct sockaddr)
,address.ptr(), address.size()
,servInfo, NI_MAXSERV
,NI_NUMERICHOST | NI_NUMERICSERV ) ) == 0)
return LILI_ERROR;
if (::connect(handle, (struct sockaddr *) &servAddr, sizeof(servAddr)) < 0)
return LILI_ERROR;
It compiles well and no segmentation fault on start up but I can't connect my server with it :(
gethostbyname() does a name→IP lookup. It should be replaced with getaddrinfo(), which can do the same.
This means the warning is completely wrong. getnameinfo() is the replacement of gethostbyaddr(), both for IP→name lookups. The reverse.
name→IP: gethostbyname(), getaddrinfo()
IP→name: gethostbyaddr(), getnameinfo()
The newer functions can do more: they handle IPv6 and can translate strings like 'http' to 80 (port). In the future they can also determine if e.g. TCP should be used for the service in question or SCTP. The interface is ready.
Beej's explains it pretty good. gethostbyname() does not works well with IPV6 and thus you should use getnameinfo() instead. All you have to do is to fill in the required informations, i.e.
getnameinfo(
&sa, // Pointer to your struct sockaddr
sizeof sa, // Size of this struct
host, // Pointer to hostname string
sizeof host, // Hostname string buffer length
service, // Pointer to service name string
sizeof service, // Service name string buffer length
0 // No flags given
);
Edit: After some research, I've found that
getnameinfo(&sa, sizeof(sa), hostname, size_hostname, NULL, 0, 0);
should be sufficient.
Edit #2 I've noticed you are trying to use the return value of getnameinfo as hostname. But that is not correct, the hostname is saved within the provided host pointer. The return value indicates whether the operation was sufficient. Also have a look at the man page.
I'm new to Windows networking, and I am trying to find out which PORT number my socket is bound to (C++, Windows 7, Visual Studio 2010 Professional). It is a UDP socket, and from what I understand, using the following initial setup should bind it to a random available port/address:
sockaddr_in local;
local.sin_family = AF_INET;
local.sin_addr.s_addr = INADDR_ANY;
local.sin_port = 0; //randomly selected port
int result = bind(clientSock, (sockaddr*)&local, sizeof(local));
//result is always 0
As far as using this method, it works for sending data or binding it to a specific port (replacing the 0 with a desired port number). What I need is to bind it randomly, and then find out which port it was bound to afterwards. Is there any way I can do this? It seems that the "local" struct contains "0.0.0.0" as the IP address and "0" as the PORT number.
Thanks for any and all help! I appreciate it.
Use getsockname. For example:
struct sockaddr_in sin;
int addrlen = sizeof(sin);
if(getsockname(clientSock, (struct sockaddr *)&sin, &addrlen) == 0 &&
sin.sin_family == AF_INET &&
addrlen == sizeof(sin))
{
int local_port = ntohs(sin.sin_port);
}
else
; // handle error
This also works for *nix-based systems, but note that some systems define the third argument of getsockname to be of type socklen_t* instead of int*, so you might get warnings about pointers differing in signedness if you're writing cross-platform code.