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

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';

Related

Socket Programming, Casting sockaddr_in to sockaddr. Why?

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++.

Why am I getting "Invalid argument" from this sendto()?

So I'm passing an argument to my main which is an IP as char* name ipStr.
Then I'm initialising a variable to hold the length of it before calling sendto():
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
char buffer[8];
socklen_t ipLen = sizeof(ipStr);
res = sendto(sock, buffer, sizeof(buffer), 0,
(struct sockaddr*) &ipStr, ipLen);
Yet I'm still getting Invalid argument as a returned error.
I'm getting no compile errors with g++ so I'm not sure where the problem is.
EDIT
I noticed I hadn't deleted sizeof before ipLen, changed the above and still getting Invalid argument.
Your sockaddr is probably wrong, it's a struct with a binary representation of the address and some other data in it. It's a sockaddr_in cast to a sockaddr.
Try:
struct sockaddr_in ip4addr;
ip4addr.sin_family = AF_INET;
ip4addr.sin_port = htons(1234);
addrDest.sin_addr.s_addr = inet_addr("1.2.3.4");
socklen_t ipLen = sizeof(ipStr) returns the size of ipStr data type, not the length of the string it points to. The data type of ipStr is char *, and sizeof(ipStr)therefore yields 8 (or 4 on 32 bit systems). So your parameter will probably not be interpreted correctly.
Use socklen_t ipLen = strlen(ipStr) instead.

How to use getnameinfo instead of gethostbyname?

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.

Linux system call getname info return EAI_AGAIN

In my C++ application I use getnameinfo and it returns EAI_AGAIN - what causes this error?
struct sockaddr_storage ss;
socklen_t salen = sizeof(ss);
struct sockaddr *sa;
struct addrinfo hints, *paddr, *paddrp;
sa = (struct sockaddr *)&ss;
if (getpeername(sock, sa, &salen) != 0) {
error = errno;
return -1;
}
char hbuf[NI_MAXHOST];
char pbuf[NI_MAXSERV];
if (0 != (error = getnameinfo(sa, salen,
hbuf, sizeof(hbuf),
pbuf, sizeof(pbuf),
0))) {
return -1;//here it returns, the error is -3=EAI_AGAIN
}
According to getnameinfo:
[EAI_AGAIN] The name could not be resolved at this time. Future attempts may succeed.
The most likely reason would be some kind of timeout during the DNS lookup.

Segfault (possibly due to casting)

I don't normally go to stackoverflow for sigsegv errors, but I have done all I can with my debugger at the moment.
The segmentation fault error is thrown following the completion of the function. Any ideas what I'm overlooking? I suspect that it is due to the casting of the sockaddr to the sockaddr_in, but I am unable to find any mistakes there. (Removing that line gets rid of the seg fault -- but I know that may not be the root cause here).
// basic setup
int sockfd;
char str[INET_ADDRSTRLEN];
sockaddr* sa;
socklen_t* sl;
struct addrinfo hints, *servinfo, *p;
int rv;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
// return string
string foundIP;
// setup the struct for a connection with selected IP
if ((rv = getaddrinfo("4.2.2.1", NULL, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return "1";
}
// loop through all the results and make a socket
for(p = servinfo; p != NULL; p = p->ai_next) {
if ((sockfd = socket(p->ai_family, p->ai_socktype,
p->ai_protocol)) == -1) {
perror("talker: socket");
continue;
}
break;
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return "2";
}
// connect the UDP socket to something
connect(sockfd, p->ai_addr, p->ai_addrlen); // we need to connect to get the systems local IP
// get information on the local IP from the socket we created
getsockname(sockfd, sa, sl);
// convert the sockaddr to a sockaddr_in via casting
struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;
// get the IP from the sockaddr_in and print it
inet_ntop(AF_INET, &(sa_ipv4->sin_addr), str, INET_ADDRSTRLEN);
printf("%s\n", str);
// return the IP
return foundIP;
}
It doesn't look like you ever initialize the pointer sa to point at a valid sockaddr (or sockaddr_in) object.
If you replace
sockaddr* sa;
with
sockaddr addr;
and replace all uses of sa with &addr you should be in better shape.
The same is also true of sl. At least according to the documentation for my getsockname the socklen_t* parameter needs to point at a valid socklen_t object initialized to the size in bytes of the address buffer.
E.g.
socklen_t slen = sizeof addr;
and use &slen instead of sl.
It looks to me like you don't ever set up the sa pointer to actually point at anything. Commenting out the line "struct sockaddr_in *sa_ipv4 = (struct sockaddr_in *)sa;" should cause a compile error, so I guess I can see how not being able to compile your program would also cause it to not segfault - hard for a non-existent binary to crash :)
just to add, valgrind is pretty useful in detecting uninitialized pointers or for that matter any pointer related errors.
You did not allocate sa before passing it to getsockname, so you effectively passed in some garbage pointer value there. It needs to be:
struct sockaddr_in sa;
socklen_t sl;
...
getsockname(sockfd, (struct sockaddr*)&sa, &sl);
...