why can't i bind ipv6 socket to a linklocal address - c

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdio.h>
void error(char *msg)
{
perror(msg);
exit(0);
}
int main(int argc, char *argv[])
{
int sock, length, fromlen, n;
struct sockaddr_in6 server;
struct sockaddr_in6 from;
int portNr = 5555;
char buf[1024];
length = sizeof (struct sockaddr_in6);
sock=socket(AF_INET6, SOCK_DGRAM, 0);
if (sock < 0) error("Opening socket");
bzero((char *)&server, length);
server.sin6_family=AF_INET6;
server.sin6_addr=in6addr_any;
server.sin6_port=htons(portNr);
inet_pton( AF_INET6, "fe80::21f:29ff:feed:2f7e", (void *)&server.sin6_addr.s6_addr);
//inet_pton( AF_INET6, "::1", (void *)&server.sin6_addr.s6_addr);
if (bind(sock,(struct sockaddr *)&server,length)<0)
error("binding");
fromlen = sizeof(struct sockaddr_in6);
while (1) {
n = recvfrom(sock,buf,1024,0,(struct sockaddr *)&from,&fromlen);
if (n < 0) error("recvfrom");
write(1,"Received a datagram: ",21);
write(1,buf,n);
n = sendto(sock,"Got your message\n",17,
0,(struct sockaddr *)&from,fromlen);
if (n < 0) error("sendto");
}
}
when I compile and run the above code I got :
binding: Invalid argument
and if change to bind the ::1 and leave other thing unchanged in the source code, the code
works! so could you tell me what's wrong with my code ? thanks in advance.

For link-local addresses, you also need to specify the scope ID of the network interface that is associated with the address... something like this:
server.sin6_scope_id = 5; /* or whatever the scope ID is for the network interface you want to communicate over */
You can use getifaddrs() to find the various scope IDs available on your systems, and the network interfaces they correspond to.
(Yes, it's a pain... alternatively you might be able to append something like "%en0" to the end of the string you pass to inet_pton(), and inet_pton() might do the work for you... I'm not sure if inet_pton() handles that syntax or not)

Related

Cannot connect to IPv6 address if its not ::1 in C

I'd like to connect to a ssh server which has an ipv6 address with libssh2.
It works but when I give an ip that isn't the localhost it fails to connect.
The ip is correct because I can connect to it with ssh <ipv6> -p 22.
const char *ip = "::1";
struct sockaddr_storage storage;
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &storage;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(22);
int sock;
if(inet_pton(AF_INET6, ip, &addr6->sin6_addr) == 1)
{
if((sock = socket(AF_INET6, SOCK_STREAM, 0)) != -1)
{
if(connect(sock, (struct sockaddr *)(&storage),
sizeof(struct sockaddr_in6)) == 0)
{
printf("works\n");
}
close(sock);
}
}
Edit:
The suggestion (memset(&storage, 0, sizeof(storage))) by #idz seems to have resolved the problem.
Non-static structures in C are not zero-initialized, so to avoid the possibility of garbage in the memory causing errors, you should zero them out.
Adding:
memset(&storage, 0, sizeof(storage));
just after the storage declaration will do the trick.
While not directly related to the OP's error, it's always easier to find out what's going wrong if you do not throw away the error information available to you.
Exactly how you do this will depend on the environment you're coding in, but for a simple command line program you might do something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
// https://stackoverflow.com/questions/70157711/cannot-connect-to-ipv6-address-if-its-not-1-in-c
int main(int argc, const char * argv[]) {
const char *ip = "::1";
struct sockaddr_storage storage;
memset(&storage, 0, sizeof(storage));
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *) &storage;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(22);
int result = inet_pton(AF_INET6, ip, &addr6->sin6_addr);
if (result != 1) {
perror("inet_pton");
exit(EXIT_FAILURE);
}
int sock = socket(AF_INET6, SOCK_STREAM, 0);
if (sock < 0) {
perror("socket");
exit(EXIT_FAILURE);
}
result = connect(sock, (struct sockaddr *)(&storage), sizeof(struct sockaddr_in6));
if (result != 0) {
perror("connect");
exit(EXIT_FAILURE);
}
printf("It worked...\n");
close(sock);
return EXIT_SUCCESS;
}
In the case of garbage in the address this would report:
connect: Invalid argument
whereas a network routing issue would result in:
connect: No route to host
This makes it much easier to figure out what is going on!

Invalid Argument from sendto() Socket Program in C [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I'm trying to make a simple client-server API to be used for two of my machines. I made this simple program that uses the functions I made to test it. For some reason my client sends a message just fine, but my server can't (however, it receives the message from the client).
Server side output:
host name: my_host
Our port number is: 34440
Client msg: Client msg
Send failed: Invalid argument
Message from Server sent to Client
Client side output:
Connection established with server...
Message from Client sent to Server
Server side:
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include "my_socket.h"
int main() {
server_init();
char *msg = "Server msg";
char buffer[100];
int n = read_from_client((char *)buffer);
buffer[n] = '\0';
printf("Client msg: %s\n", buffer);
write_to_client((char *)msg);
printf("Message from Server sent to Client \n");
return 0;
}
Client Side:
#include <sys/socket.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include "my_socket.h"
int main() {
client_init();
char *msg = "Client msg";
char buffer[100];
write_to_server((char*)msg);
printf("Message from Client sent to Server \n");
int n = read_from_server((char *)buffer);
buffer[n] = '\0';
printf("Server msg: %s\n", buffer);
close_socket();
return 0;
}
my_socket.c:
#include "my_socket.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
int sockfd1;
int sockfd2;
int MAX_BUFF = 1024;
struct sockaddr_in server;
struct sockaddr_in client;
struct hostent *host;
ssize_t write_to_server(const void *buffer){
int bytes_sent, server_size = sizeof(server), buf_len = strlen(buffer);
if ((bytes_sent = sendto(sockfd2, buffer, buf_len, 0,
(const struct sockaddr *)&server, server_size)) < 0){
perror("Send failed");
}
return bytes_sent;
}
ssize_t write_to_client(const void *buffer){
int bytes_sent, client_size = sizeof(client), buf_len = strlen(buffer);
if ((bytes_sent = sendto(sockfd1, buffer, buf_len, 0,
(const struct sockaddr *)&client, client_size)) < 0){
perror("Send failed");
}
return bytes_sent;
}
int read_from_server(void *buffer){
int bytes_rcv, len;
if ((bytes_rcv = recvfrom(sockfd2, buffer, MAX_BUFF, MSG_WAITALL,
(struct sockaddr *)&server, &len)) < 0){
perror("Read failed");
}
return bytes_rcv;
}
int read_from_client(void *buffer){
int bytes_rcv, len;
if ((bytes_rcv = recvfrom(sockfd1, buffer, MAX_BUFF, MSG_WAITALL,
(struct sockaddr *)&client, &len)) < 0){
perror("Read failed");
}
return bytes_rcv;
}
void close_socket() {
close(sockfd1);
close(sockfd2);
}
void server_init(){
if ( (sockfd1 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
}
char name[1024];
name[1023] = '\0';
gethostname(name, 1023);
printf("host name: %s \n", name);
host = gethostbyname("my_host");
if(host == NULL){
perror("Host is null");
exit(0);
}
bzero((char *)&server, sizeof(server));
bzero((char *)&client, sizeof(client));
server.sin_family = AF_INET;
bcopy((char *)host->h_addr,
(char *)&server.sin_addr.s_addr, host->h_length);
//server.sin_port = 0;
server.sin_port = htons(34440);
if ( (bind(sockfd1, (struct sockaddr *)&server, sizeof(server) ) ) < 0 ){
perror("bind failed");
}
socklen_t len = sizeof(server);
if (getsockname(sockfd1, (struct sockaddr *)&server, &len) == -1){
perror("getsockname");
}else{
printf("Our port number is: %d\n", ntohs(server.sin_port));
}
}
void client_init(){
if ( (sockfd2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
perror("socket creation failed");
}
host = gethostbyname("my_host");
if(host == NULL){
perror("Host is null");
exit(0);
}
bzero((char *)&server, sizeof(server));
server.sin_family = AF_INET;
bcopy((char *)host->h_addr,
(char *)&server.sin_addr.s_addr, host->h_length);
//server.sin_port = 0;
server.sin_port = htons(34440);
if(connect(sockfd2, (struct sockaddr *)&server, sizeof(server)) == 0){
printf("Connection established with server...\n");
}
}
my_socket.h:
#ifndef MY_SOCKET
#define MY_SOCKET
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netdb.h>
extern int sockfd1;
extern int sockfd2;
extern int MAX_BUFF;
extern struct sockaddr_in server;
extern struct sockaddr_in client;
extern struct hostent *host;
// Send a message over the socket
ssize_t write_to_server(const void *buffer);
ssize_t write_to_client(const void *buffer);
// Blocks until told it's ready; receives bytes from socket
int read_from_server(void *buffer);
int read_from_client(void *buffer);
// Close the socket
void close_socket();
void server_init();
void client_init();
#endif
Any advice or criticism is welcome. Thanks in advance.
At least one issue is that you are not initializing the len variable in read_from_client. The manual page for recvfrom says, in part (emphasis added):
... addrlen is a value-result argument. Before the call, it should be initialized to the size of the buffer associated with src_addr. Upon return, addrlen is updated to contain the actual size of the source address. The returned address is truncated if the buffer provided is too small; in this case, addrlen will return a value greater than was supplied to the call.
That means that, as an uninitialized stack variable, len has an indeterminate value. Probably zero, but at least something smaller than sizeof(struct sockaddr_in). As a result, client is not getting filled in correctly by the recvfrom.
Just before the recvfrom, you should initialize it with:
len = sizeof(client);
On the client side, write_to_server and read_from_server need not use recvfrom and sendto as you have already done a connect on the socket. They can simply use recv and send since the remote socket endpoint is already established by the connect. I believe the address is simply ignored for a connected socket, but I cannot find where that is documented right now.
(In any case, if you do continue to use recvfrom on the client side, you should make the same len initialization there.)

accept() keeps returning 0

This is a simple server that merely accepts connections, then prints the socket descriptor. For some reason, whenever I run this the only socket descriptors I receive are of value 0. This even occurs with multiple clients connecting simultaneously. I seem to be misunderstanding something to do with the behavior of accept(), or there is some bug I cannot locate in my code. Here is the code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
/* Utility for consisely killing the program. */
void abort_program(const char *error_message)
{
fputs(error_message, stderr);
exit(EXIT_FAILURE);
}
/* Establishes a passive listening port, returns socket descriptor. */
int setup_passive_port(int port)
{
struct protoent *ptrp; // pointer to a protocol table entry
struct sockaddr_in sad; // structure to hold server's address
int sd; // socket descriptor for listening
/* Map TCP transport protocol name to protocol number. */
if (((long int) (ptrp = getprotobyname("tcp"))) == 0)
abort_program("ERROR: Cannot map TCP to protocol number\n");
/* Create a socket. */
sd = socket(PF_INET, SOCK_STREAM, ptrp->p_proto);
if (sd < 0)
abort_program("ERROR: Socket creation failed\n");
/* Prepare the socket address structure. */
memset((char *) &sad, 0, sizeof(sad));
sad.sin_family = AF_INET;
sad.sin_addr.s_addr = INADDR_ANY;
sad.sin_port = htons((u_short) port);
/* Bind a local address to the socket. */
if (bind(sd, (struct sockaddr*) &sad, sizeof(sad)) < 0)
abort_program("ERROR: Bind failed\n");
/* Establish passive listener socket. */
if (listen(sd, 0) < 0)
abort_program("ERROR: Listen failed\n");
return sd;
}
int main(int argc, char *argv[])
{
struct sockaddr_in cad; // structure to hold client's address
int alen; // length of address
int sd; // incoming socket
int listener; // listening socket
listener = setup_passive_port(30000);
while (1) {
if (sd = accept(listener, (struct sockaddr*) &cad, &alen) < 0)
abort_program("ERROR: Accept failed\n");
printf("%d\n", sd);
}
}
Can you help me understand why? Thanks for your consideration.
One thing you need to do is to set your alen to the sizeof(sockaddr_in) prior to calling accept(). The other is that at least clang complains about the missing brackets within your if( accept()...) line. Here the fixed up version.
telnet localhost 30000 worked as expected.
Also changed your int alen to socklen_t alen while being at it.
int main(int argc, char *argv[])
{
struct sockaddr_in cad; // structure to hold client's address
socklen_t alen = sizeof(sockaddr_in); // length of address
int sd; // incoming socket
int listener; // listening socket
listener = setup_passive_port(30000);
while (1) {
if ((sd = accept(listener, (struct sockaddr*) &cad, &alen)) < 0)
abort_program("ERROR: Accept failed\n");
printf("%d\n", sd);
}
}

UDP C Server sendto() error: Can't assign requested address

I'm writing a basic Client/Server program in C, using UDP. The idea of the program is that the client sends a message to the server, the server receives it, then echoes it back to the client (the goal being to measure RTT for UDP). Unfortunately, on the server side, when the program attempts to call sendto() to echo the message, I receive the error "Can't assign requested address".
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
#include <fcntl.h>
#define SERVER_PORT 7000
#define MAX_PENDING 10
#define MAX_LINE 1024000
int main()
{
struct sockaddr_in sin, sout;
socklen_t soutLen;
char buf[MAX_LINE];
int len;
int msgLen;
int s;
char *msg;
if( (s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
perror("could not establish UDP socket");
exit(1);
}
/* build address data structure */
bzero((char *)& sin, sizeof( sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
if( (bind(s, (struct sockaddr *)&sin, sizeof(sin))) < 0){
perror("udpServer: bind");
exit( 1);
}
while(1){
if((msgLen = recvfrom(s, buf, sizeof(buf), 0,(struct sockaddr *)&sout, &soutLen))<0){
perror("udpServer: recvfrom()");
exit( 1);
}
if( (sendto(s, buf, msgLen, 0, (struct sockaddr *)&sout, sizeof(sout)))<0 ){
perror("udpServer: sendto()");
exit( 1);
}
free(msg);
}
}
Thanks in advance: I'm pretty new to C, so any advice is much appreciated!
The problem is that your sout you pass to sendto is not correct, because you are not correctly setting it's size when passing it to recvfrom:
man recvfrom:
ssize_t
recvfrom(int socket, void *restrict buffer, size_t length,
int flags, struct sockaddr *restrict address,
socklen_t *restrict address_len);
If address is not a null pointer and the socket is not connection-oriented, the source address of the message is filled in. The address_len argument is a
value-result argument, initialized to the size of the buffer associated with address, and modified on return to indicate the actual size of the address
stored there.
When you pass &sout to recvfrom, you also have to tell recvfrom the size of the structure you're passing in so it knows how much data it can write there -- soutLen is both an in parameter and an out parameter. Since you are not initializing soutLen, it probably has some value smaller than the actual size of the structure, which means that what you end up with in sout is not valid.
So you need to initialize soutLen:
struct sockaddr_in sin, sout;
socklen_t soutLen = sizeof(sout);
You should then pass this value as the size to sendto instead of sizeouf(sout) (this may not be required but it's good practice):
if( (sendto(s, buf, msgLen, 0, (struct sockaddr *)&sout, soutLen))<0 ){
Also just as a note, you are freeing msg which you never allocated. This is unrelated but might cause problems later.
Hope this helps.

connect() function fails when passing sockaddr_in as argument

I have been at this for an ungodly amount of time, so I really hope someone can provide me some keen insight as to what is going on.
I have the following main function:
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv) {
char *serv_IP;
in_port_t serv_port;
int sock;
struct sockaddr_in serv_addr;
serv_IP = argv[1];
serv_port = atoi(argv[2]);
if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
fprintf(stderr, "Failed to create TCP socket\r\n");
exit(1);
}
memset(&serv_addr, 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
if (inet_pton(AF_INET, serv_IP, &serv_addr.sin_addr.s_addr) == 0) {
fprintf(stderr, "Invalid IP address\r\n");
exit(1);
}
serv_addr.sin_port = htons(serv_port);
if (connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
fprintf(stderr, "Failed to connect to serv\r\n");
exit(1);
}
else {
printf("You're connected!\n);
}
close(sock)
return 0;
}
Now, this code works just fine. However, what I want to do is to replace the call to connect() with a helper function call to something like this:
void function(int sock, struct sockaddr_in *serv_addr) {
if (connect(sock, (struct sockaddr *) serv_addr, sizeof(serv_addr)) < 0) {
printf("Server IP = %s\n", inet_ntoa(serv_addr->sin_addr));
printf("Server port = %d\n", ntohs(serv_addr->sin_port));
fprintf(stderr, "Failed to connect to server\r\n");
exit(1);
}
else {
// Do other stuff
}
}
I remove the call to connect() from main() and replace it with the function call:
function(sock, &serv_addr);
As soon as the function is called, the correct IP and port numbers are printed out, but I still fail to connect to my server. The only difference is, in my main function(), I preface serv_addr in the connect call with the & - i.e., connect(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) - to reference its address, and I don't do that in the helper function because the address of serv_addr is already being passed as an argument - i.e., connect(sock, (struct sockaddr *) serv_addr, sizeof(serv_addr)). It makes no difference if I add the &, just in case you were wondering.
So, with the &serv_addr being passed to function() seemingly correctly, as verified by me being able to print out the correct IP and port numbers, why is it that I can connect in main() but not when I pass the serv_addr struct as an argument to another function and call connect() from there?
Thanks in advance for any help!
sizeof(serv_addr) returns 16 when serv_addr is declared as sockaddr_in, but returns 4 (in 32bit) or 8 (in 64bit) when declared as sockaddr_in*. It is too small either way, AF_INET needs 16. Had you looked at errno when connect() failed, it would have told you that you were passing an invalid parameter value.
You need to use sizeof(sockaddr_in), either directly:
void function(int sock, struct sockaddr_in *serv_addr)
{
if (connect(sock, (struct sockaddr *) serv_addr, sizeof(struct sockaddr_in)) < 0)
Or indirectly via sizeof(*serv_addr):
void function(int sock, struct sockaddr_in *serv_addr)
{
if (connect(sock, (struct sockaddr *) serv_addr, sizeof(*serv_addr)) < 0)
That did it! Thank you very much for your quick response!! I never would have thought about the return of sizeof() as a potential problem with returning different sizes for actual values vs. pointers to values. Totally makes sense, though. And I just read about the errno.h header as well as how to use it.
The exact line that fixed it was:
if (connect(sock, (struct sockaddr *) serv_addr, sizeof(struct sockaddr_in)) < 0)
I was right about not needing the '&' in front of serv_add. You also need the "struct" in sizeof() or else it returns sockaddr_in as an undeclared variable.
Anyways, thanks again.
Now I can finally move on with my code.

Resources