Socket Programming, Casting sockaddr_in to sockaddr. Why? - c++

Here is a sample socket programming snippet from the client side. My question revolves around the type-casting that casted sockaddr_in into sockaddr. My question is that why is that casting necessary there, what is going on there exactly?
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#define PORT 8080
int main ( int argument, char const *argv[] )
{
int obj_socket = 0, reader;
struct sockaddr_in serv_addr;
char *message = "A message from Client !";
char buffer[1024] = {0};
if (( obj_socket = socket (AF_INET, SOCK_STREAM, 0 )) < 0)
{
printf ( "Socket creation error !" );
return -1;
}
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
if(inet_pton ( AF_INET, "127.0.0.1", &serv_addr.sin_addr)<=0)
{
printf ( "\nInvalid address ! This IP Address is not supported !\n" );
return -1;
}
if ( connect( obj_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr )) < 0) // <--- QUESTION ABOUT THIS LINE
{
Printf ( "Connection Failed : Can't establish a connection over this socket !" );
return -1;
}
send ( obj_socket , message , strlen(message) , 0 );
printf ( "\nClient : Message has been sent !\n" );
reader = read ( obj_socket, buffer, 1024 );
printf ( "%s\n",buffer );
return 0;
}
I've been reading the documentation and going throughout the header files to understand the structures however there is one last thing that I couldn't get.
The <sys/socket.h> header defines the sockaddr structure that includes
at least the following members:
sa_family_t sa_family address family
char sa_data[] socket address (variable-length data)
The <netinet/in.h> header shall define the sockaddr_in structure,
which shall include at least the following members:
sa_family_t sin_family AF_INET.
in_port_t sin_port Port number.
struct in_addr sin_addr IP address.
My question is, at this line of the code:
if ( connect( obj_socket, (struct sockaddr *)&serv_addr, sizeof(serv_addr )) < 0)
What is the purpose of this casting here which casted sockaddr_in to sockaddr. I understand that it is mandatory to cast here, but I would like to know why is it so? Thank you

What is the purpose of this casting here which casted sockaddr_in to sockaddr.
sockaddr_in is not casted to sockaddr. sockaddr_in* is casted to sockaddr*.
That cast is done because the function connect which is being called doesn't accept a sockaddr_in* parameter, but a sockaddr*.
P.S. I recommend to not manually create a sockaddr_in, but instead to use getaddrinfo to create it.
P.P.S char *message = "A message from Client !"; is ill-formed in C++. The code that you've posted seems to be C rather than C++.

Related

How convert/put the char* variable into struct sockaddr?

Hi
I have a problem with giving a name to socket by a program parameter.
I tried do it in many ways, some conversions (strcpy function)etc. but in the best option it gives me a one letter from my name. How can I convert/put it into that structure?
void socket_out(char* name)
{
int listfd;
int connfd;
listfd = socket(AF_UNIX, SOCK_STREAM, 0);
if(listfd == -1)
exit(-1);
struct sockaddr saddr = {AF_UNIX, name};
socklen_t saddrlen = sizeof(struct sockaddr) + 6;
bind(listfd, &saddr, saddrlen);
And error:
error: invalid conversion from ‘char*’ to ‘char’ [-fpermissive]
struct sockaddr saddr = {AF_UNIX, name};
Sorry, I just realized my previous answer was out of topic, as you seem to use Unix Domain Sockets, not TCP/IP sockets.
The problem is that you should never use struct sockaddr directly, but rather one of its "specializations", in this case struct sockaddr_un, and then cast it at the last minute. Have a look at the following example:
unsigned int s, s2;
struct sockaddr_un local, remote;
int len;
s = socket(AF_UNIX, SOCK_STREAM, 0);
local.sun_family = AF_UNIX;
strcpy(local.sun_path, "/home/beej/mysocket");
unlink(local.sun_path);
len = strlen(local.sun_path) + sizeof(local.sun_family);
bind(s, (struct sockaddr *)&local, len);
Taken from the excellent Beej's Guide to Unix IPC: http://beej.us/guide/bgipc/output/html/multipage/unixsock.html
you are tying to initialize a structure there, but the second parameter is not of type char*, but of type char[].
See the definition for struct sockaddr:
The <sys/socket.h> header shall define the sockaddr structure that includes at least the following members:
sa_family_t sa_family Address family.
char sa_data[] Socket address (variable-length data).
To get around that, you have to use strcpy/strncpy for the second parameter:
struct sockaddr saddr;
saddr.sun_family = AF_UNIX;
strncpy(saddr.sun_path, name, UNIX_PATH_MAX);
addr.sun_path[UNIX_PATH_MAX - 1] = '\0';

Calling socket::connect, socket::bind, socket::listen without using getaddrinfo( ) function before it

In all the example including Beej's Guide, the IP address is provided in dot notation and then it's fed to ::getaddrinfo(). This post doesn't answer my question.
After which the addrinfo struct is used for socket related functions (e.g. connect(), bind(), listen()). For example:
struct addrinfo hints, *res;
// ... create socket etc.
connect(sockfd, res->ai_addr, res->ai_addrlen);
Example
The variable ai_addr is of type sockaddr which can be safely typecasted to sockaddr_storage, sockaddr_in and sockaddr_in6.
Question:
If I typecast sockaddr to sockaddr_in (or sockaddr_in6)
sockaddr_in& ipv4 = (sockaddr_in&)(sockaddr_variable);
and feed below info:
ipv4.sin_family = AF_INET
ipv4.sin_addr = [IP Address in net byte order]
ipv4.sin_port = [Port number in net byte order]
Can I call the connect() method directly using above info?
connect(sockfd, &ipv4, sizeof(ipv4));
With my program it doesn't appear to work. Am I missing something, or is there a better way?
The motivation behind is that, if we have the information of IPAddress, Port etc. in socket readable format then why to go through the cycle of getaddrinfo()
Be sure you're placing your values in network order, here's a small example:
#include<stdio.h>
#include<sys/socket.h>
#include<arpa/inet.h>
int main(int argc, char *argv[])
{
int sock;
struct sockaddr_in server;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == -1)
{
printf("Could not create socket\n");
}
printf("Socket created\n");
server.sin_family = AF_INET;
// 173.194.32.207 is a google address
server.sin_addr.s_addr = 173 | 194 << 8 | 32 << 16 | 207 << 24;
server.sin_port = 0x5000; // port 80
if (connect(sock, (struct sockaddr *)&server, sizeof(server)) < 0)
{
perror("connect failed. Error");
return 1;
}
printf("Connected\n");
close(sock);
return 0;
}
First check whether the machine is reachable & the server application is running on the machine using "netstat" utility. Use inet_aton method to convert dotted address to network byte order. Finally, log the error value returned by the connect to get the exact reason of failure.
It's worth noting that calling socket::{connect, bind, ...} is wrong: these are C APIs and C doesn't have namespaces, classes and so on.
You should use getaddrinfo as it's much easier and safer to use. But nothing prevents you from using struct sockaddr and all its variants. Indeed, getaddrinfo is a sort of wrapper as stated in man(3) getaddrinfo:
The getaddrinfo() function combines the functionality
provided by the gethostbyname(3) and getservbyname(3) functions into a
single interface, but unlike the latter functions, getaddrinfo() is
reentrant and allows programs to eliminate IPv4-versus-IPv6 dependen‐
cies.
An example:
#include <sys/socket.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main()
{
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
inet_pton(addr.sin_family, "198.252.206.16", &addr.sin_addr);
int fd = socket(addr.sin_family, SOCK_STREAM, 0);
if (fd == -1)
; /* could not create socket */
if (connect(fd, (struct sockaddr *) &addr, sizeof(addr)) == -1)
; /* could not connect */
close(fd);
}

C++ connect function error - Invalid argument

I'm trying to make a simple socket client (using debian 6 and g++ compiler) and when I'm calling "connect" function, it returns error code 22 - Invalid argument. Tell me, what am I doing wrong, please.
I've read the man page for this function and it says that the 3 arguments must be as followed: int, struct sockaddr* and socklen_t.
My code is:
int serverPort;
u_long serverHost;
struct sockaddr_in serverAddress;
socklen_t serverAddressLength;
int clientSocket;
serverPort = 44444;
serverHost = inet_addr ( "88.198.237.65" );
serverAddress.sin_family = AF_INET;
serverAddress.sin_port = htons ( serverPort );
serverAddress.sin_addr.s_addr = htons ( serverHost );
serverAddressLength = sizeof ( serverAddress );
clientSocket = socket ( AF_INET , SOCK_STREAM , 0 );
connect ( clientSocket , (sockaddr*)&serverAddress , serverAddressLength )
serverAddress.sin_addr.s_addr = htons ( serverHost );
An IPv4 address is a long, not a short, so htons is right out. The data is in network byte order already anyway, so no conversion is needed.
The inet_addr() function converts the Internet host address cp from IPv4 numbers-and-dots notation into binary data in network byte order.
Also:
char serverHost;
How's it supposed to fit in a char?

Socket transmit data but can't receive response

I am trying to implement UpNP in C++, I found a few sources on google but none worked. I found this one working (http://www.codeproject.com/KB/IP/upnplib.aspx) but it's for .NET, so I decided to sniff the network to see what the code was doing and then do the same with sockets.
Here are the results (full size: http://i.stack.imgur.com/eLoHK.jpg):
That shows me that the packet doesn't look bad, everything seems to be the same, everything but the source address of my code, which I don't know how to control (both my code and finder.net.exe are being tested on the same computer connected to the same network).
Here's my code:
#define upnp_broadcast_ip "239.255.255.250"
#define upnp_broadcast_port 1900
#define upnp_search_request "M-SEARCH * HTTP/1.1\r\n" \
"Host:239.255.255.250:1900\r\n" \
"ST:upnp:rootdevice\r\n" \
"Man:\"ssdp:discover\"\r\n" \
"MX:3\r\n" \
"\r\n"
WSAStartup(MAKEWORD(2, 2), &WsaData);
BOOL discover( )
{
SOCKET ConnectSocket;
struct sockaddr_in Addr;
char Buffer[1450];
int t = 0,
iResult = 0,
TrueLen = sizeof(bool);
bool True = true;
ulong One = 1;
// Open datagram socket
ConnectSocket = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
// Clear out struct
memset( &Addr, 0, sizeof(Addr) );
// Specify the address family, IP address, and port
Addr.sin_family = AF_INET;
Addr.sin_port = htons( upnp_broadcast_port );
Addr.sin_addr.s_addr = inet_addr( upnp_broadcast_ip );
iResult = setsockopt( ConnectSocket, SOL_SOCKET, SO_BROADCAST, (char*)&True, TrueLen ); // Not sure what is this for
// Transmit data
int sent = sendto( ConnectSocket, upnp_search_request, strlen(upnp_search_request), 0, (struct sockaddr*)&Addr, sizeof(Addr) );
// Try to receive data 10 times
for( t = 0; t < 10; t++ )
{
ioctlsocket( ConnectSocket, FIONBIO, &One );
// Clear out buffer
memset( &Buffer, 0, sizeof(Buffer) );
int length = sizeof(Addr);
// Receive data
iResult = recvfrom( ConnectSocket, Buffer, (sizeof(Buffer) - 1), 0, (struct sockaddr*)&Addr, &length );
if( iResult == SOCKET_ERROR)
{
Sleep( 1000 );
continue;
} else {
// Do stuff with received data
}
}
closesocket( ConnectSocket );
return FALSE;
}
I removed all the WSAGetLastError() error checking to make the code easier to read, everything goes fine until recvfrom, that always returns -1 and strerror(WSAGetLastError()) prints "Unknown error".
I hope someone could guide me in the right direction, I've been the last two days trying to make this work.
Why broadcast? UPnP uses multicast, so as far as I remember (Unix) you should use setsockopt() to request that the kernel join a multicast group. I am not sure about Windows, it could be the same call.
Something like:
struct ip_mreq mreq;
....
mreq.imr_multiaddr.s_addr=inet_addr(GROUP_IP);
mreq.imr_interface.s_addr=htonl(INADDR_ANY);
setsockopt(fd,IPPROTO_IP,IP_ADD_MEMBERSHIP,&mreq,sizeof(mreq))
EDIT:
As hasturkun pointed out, my initial answer was wrong. Also, slemdx correctly diagnosed the problem: the uPnP request is going out from the wrong interface. The problem is, I am not sure how you can determine the right interface. One possibility is to use the interface containing the default gateway on the routing table, but I don't think that would be the right choice. There may be uPnP devices hooked to other interfaces.
One option is to send the initial search packet on all available interfaces. Maybe the answers to this question can help. There is also another link on the last answer that you should check out.

Client is having trouble connecting to server using sockets?

My server is up and running (connecting through telnet worked so I know its functional) but my client cannot seem to establish a connection. I have a feeling it has something to do with the way I'm filling out the sockaddr_in serverAddr struct.
Can anyone please help? Thank you.
int clientSocket;
char hostname[256];
struct sockaddr_in serverAddr;
struct hostent *host;
socklen_t theirAddrSize;
gethostname(hostname, sizeof(hostname));
host = gethostbyname(hostname);
memset(&serverAddr, 0, sizeof(serverAddr));
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.s_addr = inet_addr( host->h_name );
serverAddr.sin_port = htons( 30000 );
if ( clientSocket = socket( AF_INET, SOCK_STREAM , 0 ) == -1) {
cerr << "socket failed ; exiting..." << endl;
exit(1);
}
if ( connect( clientSocket , (struct sockaddr *) &serverAddr , sizeof(serverAddr) ) == -1 ) {
cerr << "connect failed ; exiting..." << endl;
exit(1);
}
connect always returns -1.
Instead of inet_addr(host->h_name), use host->h_addr_list[0].
Does this work?
memcpy(&serverAddr.sin_addr,
host->h_addr,
sizeof(serverAddr.sin_addr));
As far as I can see there's nothing wrong with the code you've posted here. It's pretty much identical with socket client code I've been writing for years. So the problem either lies elsewhere in the code, or it's in the data.
Ah - you've edited the code... and added some comments. OK, the return value from inet_addr is -1 (4294967295 == 0xFFFFFFFF == -1 == INADDR_NONE), so it doesn't seem to like what you're passing it.
You need to run the code through a debugger, concentrating on the calls to gethostname and gethostbyname. I'm assuming this is test code, since you're connecting to the same machine you're running on.
I did require the memcpy but alot of this headache stemmed from a very mindless syntax error:
if ( clientSocket = socket( AF_INET, SOCK_STREAM , 0 ) == -1)
I had to wrap the assignment in parenthesis before comparing it to -1.
if (( clientSocket = socket( AF_INET, SOCK_STREAM , 0 )) == -1)
Gah, you live you learn :)