I am trying to send a buffer via UDP sockets in C but I always get an invalid argument error in sendto. I just don't find the error. Could anyone maybe help me. Thanks in advance.
Here's my code:
/**/ void IPCSend(char *pazClientAddress, int iClientPort, char *pazBuffer )
{
int iSocket;
/* */
if ((iSocket = socket(AF_INET, SOCK_DGRAM, 0)) != -1)
{
int iSendToReturn;
struct sockaddr_in sinServerAddress;
struct hostent *pstHost;
/* */
pstHost = (struct hostent *)gethostbyname((char *)pazClientAddress);
/* */
sinServerAddress.sin_family = AF_INET;
sinServerAddress.sin_addr = *((struct in_addr *)pstHost->h_addr);
sinServerAddress.sin_port = iPortNumber;
bzero(&(sinServerAddress.sin_zero),8);
/* */
fprintf(stdout,"sending '%s' to client '%s:%d'\n",pazBuffer,pazClientAddress,iClientPort);
iSendToReturn = sendto(iSocket, pazBuffer, sizeof(pazBuffer), 0, (struct sockaddr *)&sinServerAddress, sizeof(sinServerAddress));
/* */
if(iSendToReturn != -1)
fprintf(stdout,"%d bytes sent\n",iSendToReturn);
else
perror("SendTo");
/* */
close(iSocket);
}
else
fprintf(stdout,"could not connect to server\n");
}
Firstly, you're making the perennial error of novice C programmers: using sizeof to get the size of a pointer. The variable pazBuffer is a pointer, not an array, so the sizeof operator will not return the array length, which is what you want. Your IPCSend function needs to take in the length of pazBuffer as a separate parameter:
void IPCSend(char *pazClientAddress, int iClientPort, char *pazBuffer, size_t len)
As for the error you're getting - it might be related to something else. Invalid argument means that one of the parameters you're passing to sendto is somehow invalid. Since I see that you are properly initializing the socket descriptor, the problem might be that the send buffer is somehow not valid - possibly a null pointer or bad address. This means that the problem is not apparent from the code you posted, and is likely in some code that is calling IPCSend.
Related
First of all, i understand what the code (see at the end of the post) does in general and i dont want an overall explanation.
What i don't understand is this particular line of code:
memset(&hints, 0, sizeof(struct addrinfo));
What i get so far is that a memset() is a function to fill the string which it is pointed to up.
It has three parameters, first the pointer to the string, second the value to be set and third the number of bytes set to the value.
In this case the the value to be filled up is &hints which would be the adress of the variable hints. The value which should be set is 0 so it is filled with zeroes. And last it is filled to the size of the struct addrinfo.
So in this case memset() generates for the variable hints zeroes to the size of the struct. Did i get this right?
If yes, why is this needed in my example?
#include <stdlib.h>/* EXIT_SUCCESS */
#include <stdio.h>/* printf */
#include <string.h>/* memset() */
#include <errno.h>/* int errno */
#include <sys/types.h>/* socket defines */
#include <sys/socket.h>/* socket() */
#include <netdb.h>/* getaddrinfo() */
#define ECHO_PORT "7"
int main (int argc, char* argv[]){
if (argc != 2) {
printf ("Usage: %s HOSTNAME\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Resolve host addresses: */
struct addrinfo hints;
struct addrinfo* result, *rp;
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;/* Allow IPv4 or IPv6 */
hints.ai_socktype = SOCK_STREAM;/* Stream socket */
hints.ai_flags = 0;
hints.ai_protocol = 0;/* Any protocol */
int err = getaddrinfo(argv[1], ECHO_PORT, &hints, &result);
/* Handle potential error: */
if (err) {
printf("Error: getaddrinfo: %s\n", gai_strerror(err));
exit(EXIT_FAILURE);
}
/* Print names found: */
printf("Addresses for %s:\n", argv[1]);
for (rp = result; rp != NULL; rp = rp->ai_next) {
int af = rp->ai_family;
char* address = NULL;
int ok;
if (AF_INET == rp->ai_family) {
uint8_t in_addr =((struct sockaddr_in*)rp->ai_addr)->sin_addr.s_addr;
address = malloc(INET_ADDRSTRLEN);
ok = inet_ntop(af, &in_addr, address, INET_ADDRSTRLEN);
}
if (AF_INET6 == rp->ai_family) {
char* in6_addr =((struct sockaddr_in6*)rp->ai_addr)->sin6_addr.s6_addr;
address = malloc(INET6_ADDRSTRLEN);
ok = inet_ntop(af, in6_addr, address, INET6_ADDRSTRLEN);
}
if (ok) {
printf("%s\n", address);
}
else {
perror("inet_ntop");
}
free(address);
}
freeaddrinfo(result);
return EXIT_SUCCESS;
}
Yes, you understood it correctly.
It's needed in the code below because in
struct addrinfo hints;
hints is left uninitialized and the programmer wanted to make sure all the fields a zeroed.
An easier solution would be to initialize it directly:
addrinfo hints{}; // C++11 and later
struct addrinfo hints = {0}; /* C and C++ */
and skip memset.
Another option is to initialize it with the correct values using designated initializers (C99 and C++20). In C you can specify the fields out-of-order, but not in C++, so this order would work in both:
struct addrinfo hints = { /* "struct" not needed in C++ */
.ai_flags = 0,
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM,
/* .ai_protocol and the rest will be zeroed */
};
For older standards without designated initializers:
struct addrinfo hints = {
0,
AF_UNSPEC,
SOCK_STREAM
};
Yes, your understanding is basically correct. The code is simply filling the entire hints variable with 0x00 bytes before passing it to getaddrinfo(). This is needed to initialize the hints to a default state, which is important because addrinfo contains flags and memory pointers to control getaddrinfo()'s behavior. So you can't just leave the hints in an uninitialized state, it will contain random garbage that will cause undefined behavior, confusing getaddrinfo() and/or even leading to corrupted memory, crashes, etc.
Using the memset() is a quick way to initialize all of the fields of the hints to zeros in one quick operation, instead of initializing each field individually. This way, you can focus on assigning values to just the fields you are actually interested in.
An easier way to initialize the hints is like this instead:
struct addrinfo hints = {0};
This will value-initialize the first field (ai_flags) to 0, and default-initialize the remaining fields to their default values, which in this case is also zeros.
You're correct that memset is being used here to set all bytes of hints to zero.
This is being done so that any field that is not explicitly set later has the value 0. Because hints is uninitialized, its fields have indeterminate values, so doing this sets all fields to 0. If you look at the definition of struct addrinfo:
struct addrinfo {
int ai_flags;
int ai_family;
int ai_socktype;
int ai_protocol;
socklen_t ai_addrlen;
struct sockaddr *ai_addr;
char *ai_canonname;
struct addrinfo *ai_next;
};
You can see that the program only explicitly sets the first 4 fields. The call to memset first takes care of the rest.
In the next code, while I try to connect a client the server shows the following error:
"invalid argument", I can't see the error.
if((l_sock=socket(AF_INET,SOCK_STREAM,0))!=-1)
{
struct sockaddr_in srv_dir;
srv_dir.sin_family=AF_INET;
srv_dir.sin_port=8500;
srv_dir.sin_addr.s_addr=INADDR_ANY;
if((bind(l_sock,(struct sockaddr *)&srv_dir,sizeof(struct sockaddr_in)))!=-1)
{
if(!(listen(l_sock,5)))
{
signal(SIGINT,cerraje);
int t_sock;
struct sockaddr_in cli_dir;
socklen_t tam;
time_t tstmp;
struct tm * res;
res=(struct tm *)malloc(sizeof(struct tm));
while(!key)
{
if((t_sock=accept(l_sock,(struct sockaddr *)&cli_dir,&tam))!=-1)
{
tstmp=time(&tstmp);
res=gmtime(&tstmp);
send(t_sock,res,sizeof(struct tm),0);
wr_hora(*res,cli_dir.sin_addr);
}
else
perror("PeticiĆ³n no atendida");//The error is printed here.
Read the documentation on accept(2):
The addrlen argument is a value-result argument: it should initially contain the size of the structure pointed to by addr; on return it will contain the actual length (in bytes) of the address returned. When addr is NULL nothing is filled in.
So you need to initialize the value of tam passed into accept with sizeof(cli_dir). You're fortunate that the socket library was able to catch your error, because you're passing in uninitialized memory, which results in undefined behavior.
I had an error with the following code, wherein it would return to me that sendto failed because "Address family not supported by protocol". I realized my mistake on the 3rd line, leaving a semicolon after the for loop declaration. Once I removed this, everything worked fine.
struct sockaddr_in their_addr;
if ((numbytes=recvfrom(sockfd, buf, 512, 0,
(struct sockaddr *)&their_addr, &addr_len)) == -1 ) {
perror("recvfrom") ;
exit(1) ;
}
char thedata[512];
int i;
for (i = 0; i < 512; i++);
{
thedata[i] = 'a';
}
unsigned int addr_len = sizeof(struct sockaddr);
if ((numbytes=sendto(sockfd, thedata, 512, 0, (struct sockaddr *)&their_addr,
addr_len)) == -1 ) {
perror("sendto") ;
exit(1) ;
}
My question is why that message specifically was returned to me. I sent an array with uninitialized values, but my assumption was that because the char array was initialized to have 512 elements, that it would have memory allocated for all 512 elements in a row. Why doesn't it send the garbage values? It's a question of curiosity more than anything, but I'd love to know more about this situation.
Thanks
When your loop exits, i is 512, so thedata[i] points to beyond the end of thedata[]. I am guessing this overwrote their_addr, specifically the protocol which from memory is at the head of the struct.
Please insert the normal warning here about the fact you overwrote memory so behaviour is undefined and anything could have happened.
I faced an issue, when I try to send to multicast group by setting the intended outgoing interface by the code bellow, Actually when the condition is TRUE (if(config.enable_if == 1)) the sendto system call returns error Invalid Argument, but if the condition was False sendto send data and doesn't generate any error.
Please Anyone has an idea, or should I modify anything in my code?
/* Create a datagram socket on which to send. */
sd = socket(AF_INET, SOCK_DGRAM, 0);
/* Set local interface for outbound multicast datagrams. */
/* The IP address specified must be associated with a local */
/* multicast capable interface. */
if(config.enable_if == 1){
mreqn.imr_ifindex = if_nametoindex("eth3");
rc = setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, (void *)&mreqn, sizeof(mreqn));
}
/* Initialize the group sockaddr structure with a */
/* group address of dynamic address and port dynamic port. */
memset((char *) &groupSock, 0, sizeof(groupSock));
groupSock.sin_family = AF_INET;
groupSock.sin_addr.s_addr = inet_addr(config.mip);
groupSock.sin_port = htons(config.port);
/* Send a message to the multicast group specified by the*/
/* groupSock sockaddr structure. */
rc = sendto(sd, (const char *)& databuf, datalen, 0, (const struct sockaddr*)&groupSock, sizeof (struct sockaddr));
printf("errno %d\n",errno);
One reason sendto fails is because you pass it a data pointer it does not expect. If you have char* databuf and you then do &databuf you get the address of the pointer, i.e. a pointer to a pointer, of type char**. If you remove the cast (which is not needed) then you will get at least a warning or maybe even an error when compiling.
I have been trying to send and receive structures on the same machine using UDP and the server and client in this case run on the same machine and share common structure definitions (using a header file).
Hostent structure defn(UNIX built-in type) :
struct hostent{
char *h_name;
char **h_aliases;
int h_addrtype;
int h_length;
char **h_addr_list;
}
Server Code snippet follows :
struct hostent* resolved_host = DNS_translate(DNSname);
if((numbytes = sendto(sockfd, (void*)&resolved_host, sizeof(struct hostent), 0, (struct sockaddr *)&client_addr, sizeof(struct sockaddr))) == -1)
{
perror("sendto failed");
exit(EXIT_FAILURE);
}
Client Code snippet follows:
struct hostent resolved_host;
int addr_len = sizeof(struct sockaddr);
if((numbytes = recvfrom(sockfd, (void*)&resolved_host, sizeof(struct hostent), 0, (struct sockaddr *)&server_addr, &addr_len)) == -1)
{
perror("recvfrom failed");
exit(EXIT_FAILURE);
}
The server sends and the client receives as normal (no error raised).
The *resolved_host* structure is filled in the server and all its data can be accessed with no problem.
However, if I now try to use the *resolved_host* structure in the client, I get a seg fault. For example:
printf("Name : %s\n", resolved_host.h_name);
raises a seg fault. (but works in the server)
Your struct is full of pointers. When you send it over the network, you send the actual addresses, not the data pointed to by those pointers.
Those addresses are invalid in the target process.
You will need to serialize the data yourself. See for examples:
Serialization/Deserialization of a struct to a char* in C
Serialization techniques
The structure contains pointers - so when you copy the structure over UDP you're only copying the values of those pointers (i.e. the addresses of some other pieces of data) and not the actual data itself.
When you receive those pointers in the server they no longer mean anything - those pointer addresses are meaningless to the other program.
You are sending pointers. Even on the same machine these are not valid in different address spaces.