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!
Related
I'm trying this simple code, to get the client's IP address. It works nice on FreeBSD, but strangely returns zeroes on MacOS. I'm confused and can't understand what's wrong.
#include <stdio.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
int main() {
int s, c;
socklen_t len;
struct sockaddr_in saddr, caddr;
if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
printf("socket()\n"); exit(1);
}
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
saddr.sin_port = htons(9090);
if ((bind(s, (struct sockaddr *)&saddr, sizeof(saddr))) != 0) {
printf("bind()\n"); exit(1);
}
if ((listen(s, 5)) != 0) {
printf("listen()\n"); exit(1);
}
if ((c = accept(s, (struct sockaddr *)&caddr, &len)) < 0) {
printf("accept()\n"); exit(0);
}
char ipstr[INET6_ADDRSTRLEN];
inet_ntop(AF_INET, &caddr.sin_addr, ipstr, len);
printf("Client IP address: [%s:%d]\n", ipstr, ntohs(caddr.sin_port));
close(c); close(s);
return 0;
}
On FreeBSD:
Client IP address: [127.0.0.1:17225]
On MacOS:
Client IP address: [0.0.0.0:0]
You must initialize len before calling accept. It tells accept the size of the structure passed to it:
len = sizeof caddr;
if ((c = accept(s, (struct sockaddr *)&caddr, &len)) < 0) { ... }
If it's not initialized it will have an indeterminate (read: garbage) value which could lead to undefined behavior.
I'm writing a client side as part of a TCP client server program.
My code reaches the connect part and throws an Invalid argument error, I have gone through the code several times and I couldn't find the problem.
The code receives 3 arguments, first one is an IP address or a hostname, second one is port and the third is the maximum length of the message to be sent.
My code uses getaddrinfo in order to convert the ip address or hostname, creates the needed variables, starts a connection, read from file, send data and receive data.
I run the code with:
gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c
./pcc_client 127.0.0.1 2233 4
The output is:
sockaddr_in initialized
Error starting connection : Invalid argument
#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#define FILE_ADDR "/dev/urandom"
int main(int argc, char *argv[]) {
if (argc != 4) {
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
//Get command line arguments
unsigned int port = atoi(argv[2]);
int length = atoi(argv[3]); //Number of bytes to read
char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server
struct addrinfo hints, *servinfo, *p;
struct sockaddr_in *serv_addr;
int rv;
char ip[100];
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
if ((rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo)) != 0) {
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
serv_addr = (struct sockaddr_in *) p->ai_addr;
strcpy(ip, inet_ntoa(serv_addr->sin_addr));
}
// inet_aton(ip, &h.sin_addr);
freeaddrinfo(servinfo);
//Initialize socket
int sockfd;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) //Error creating socket
{
perror("Error creating socket \n");
exit(1);
}
printf("socket created\n");
//Initialize sockaddr_in structure
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
//Initialize connection
if (connect(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) { //Error connecting
perror("Error starting connection \n");
exit(1);
}
printf("connect succesful\n");
exit(0);
}
You are using serv_addr all wrong.
You have declared serv_addr as a sockaddr_in* pointer. After getaddrinfo() exits successfully, you are looping through the output list, assigning serv_addr to point at every ai_addr in the list, and then you free the list, leaving serv_addr pointing at invalid memory. You then trash memory when you try to populate serv_addr with data. And then you end up not even passing a valid pointer to a sockaddr_in to connect() at all, you are actually passing a pointer to a pointer to a sockaddr_in, which is why it complains about an "invalid argument".
In fact, you are going about this situation all wrong in general. When using getaddrinfo(), since it returns a linked list of potentially multiple socket addresses, you need to loop through the list attempting to connect() to every address until one of them is successful. This is especially important if you ever want to upgrade the code to support both IPv4 and IPv6 (by setting hints.ai_family = AF_UNSPEC;).
Try something more like this instead:
int main(int argc, char *argv[])
{
if (argc != 4)
{
printf("should receive 3 arguments Received %d args\n", argc);
exit(1);
}
struct addrinfo hints, *servinfo, *p;
int sockfd = -1;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET; // or AF_UNSPEC
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
if (rv != 0)
{
perror("getaddrinfo error\n");
return 1;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
//Initialize socket
sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if (sockfd < 0) continue;
//Initialize connection
rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
if (rv == 0) break;
close(sockfd);
sockfd = -1;
}
freeaddrinfo(servinfo);
if (sockfd < 0) //Error creating/connecting socket
{
perror("Error creating/connecting socket \n");
exit(1);
}
printf("connect successful\n");
...
close(sockfd);
exit(0);
}
You define serv_addr
struct sockaddr_in *serv_addr;
Then you use it
memset((void*)serv_addr, 0,(size_t) sizeof(*serv_addr));
serv_addr->sin_family = AF_INET;
serv_addr->sin_port = htons(port);
serv_addr->sin_addr.s_addr = inet_addr("127.0.0.1"); //change?
But nowhere in between those two places in the code do you initialize the pointer! That means serv_addr is uninitialized and its value is indeterminate and will point to some seemingly random location. Dereferencing the pointer will lead to undefined behavior.
The simple and natural and de facto standard solution is to make serv_addr not a pointer, but a structure object:
struct sockaddr_in serv_addr;
Then when you need a pointer you use the address-of operator &.
The issue above is further complicated by you actually using the & operator when calling connect. With serv_addr being a pointer, then &serv_addr is a pointer to the pointer. It will be of type struct sockaddr_in **. It is this issue, with the pointer to the pointer, that leads to the error message, since the pointer you send in is not a pointer to a sockaddr_in structure object.
By using a structure object as shown above will solve this problem as well.
I'm kind of new to sockets. So I setup a server and I want to connect a client to it through the internet. I don't understand what I'm doing wrong because the following example works with the loopback address (127.0.0.1) but not when someone else tries to connect to my server using my external IP address. Is it just not that simple or am I doing something wrong in my code?
Edit: (The client specifically gets stuck when it reaches connect() giving a connect: Connection timed out error)
Edit2: I tried using my broadcast address inet_addr("192.168.1.255") instead of INADDR_ANY in the server code. No change.
This is my server code:
#include <stdio.h>
#include <stdlib.h\
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(void) {
int temp;
struct sockaddr_in my_addr;
struct sockaddr_in their_addr;
int sin_size;
int numbytes;
char buf[100];
int sock_fd; // server file descriptor
int new_fd; // client file descriptor
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock_fd == -1) { perror("socket"); exit(1); }
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(9000);
my_addr.sin_addr.s_addr = INADDR_ANY;
bzero(&(my_addr.sin_zero), 8);
temp = bind(sock_fd, (struct sockaddr*)&my_addr, sizeof(struct sockaddr));
if (temp == -1) { perror("bind"); exit(1); }
temp = listen(sock_fd, 5);
if (temp == -1) { perror("listen"); exit(1); }
printf("\nListening for new connections on port %d ...\n\n", 9000);
while (1) {
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sock_fd, (struct sockaddr*)&their_addr, &sin_size);
if (new_fd == -1) { perror("accept"); continue; }
printf("Got connection from IP (%s)\n", inet_ntoa(their_addr.sin_addr));
close(new_fd);
}
}
This is my client code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
int main(int argc, char *argv[]) {
int temp;
struct hostent *host_info;
struct sockaddr_in serv_addr;
char ip_addr[100];
int numbytes;
char buf[100];
int sock_fd;
inet_pton(AF_INET, argv[5], &serv_addr.sin_addr);
host_info = gethostbyaddr(&serv_addr.sin_addr, sizeof serv_addr.sin_addr, AF_INET);
if (host_info == NULL) { herror("gethostbyname"); exit(1); }
printf("\nServer: '%s' (IP: %s , Port: %d)\n\n", argv[1], inet_ntop(AF_INET, host_info->h_addr, ip_addr, 100), 9000);
sock_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock_fd == -1) { perror("socket"); exit(1); }
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9000);
serv_addr.sin_addr = *((struct in_addr *)host_info->h_addr);
bzero(&(serv_addr.sin_zero), 8);
temp = connect(sock_fd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr));
if (temp == -1) { perror("connect"); exit(1); }
close(sock_fd);
exit(0);
}
Posting an answer here so I can accept my overall solution. The problem was that I had not forwarded a port on my router as #AdamRosenfield indicated, then after I did that I changed my server's address to:
my_addr.sin_addr.s_addr = inet_addr("192.168.1.2");
which is MY local IP address according to ifconfig (I am using Ubuntu).
Now my only problem is that anyone else can connect to my server except me from my own pc, but that might be a problem specific to my router according to #nos.
If anyone has an answer on how to fix that please update me (I tried connecting a client using the loopback address, my external IP, my local IP and a few more, nothing worked).
the client.c should be as below
int main(int argc, char *argv[]){
int temp;
struct hostent *host_info;
struct sockaddr_in serv_addr;
char ip_addr[100];
int numbytes;
char buf[100];
int sock_fd;
unsigned long inaddr;
//inet_pton(AF_INET, argv[5], &serv_addr.sin_addr);
//host_info = gethostbyaddr(&serv_addr.sin_addr, sizeof serv_addr.sin_addr, AF_INET);
//if (host_info == NULL) { herror("gethostbyname"); exit(1); }
printf("\nServer: '%s' (IP: %s , Port: %d)\n\n", argv[0], argv[1], 9000);
inaddr = inet_addr(argv[1]);
sock_fd = socket(AF_INET, SOCK_STREAM, 0);
if (sock_fd == -1) { perror("socket"); exit(1); }
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(9000);
memcpy(&serv_addr.sin_addr, &inaddr, sizeof(inaddr));
//bzero(&(serv_addr.sin_zero), 8);
temp = connect(sock_fd, (struct sockaddr*)&serv_addr, sizeof(struct sockaddr));
if (temp == -1) { perror("connect"); exit(1); }
close(sock_fd);
exit(0);
}
then use g++ compile it:g++ client.c -o client.
use client like this:./client yourserverIP
when set the server address in client code, you can do like this:
serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");//your server's ip address
I have a C function to check a host and its port, when I use FQDN host name, the function return error like: connect() failed: connect time out, but if I use IP address instead, it seems ok, how to fix this?
Thanks.
#include <unistd.h>
#include <string.h>
#include <syslog.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <errno.h>
int is_network_up(char *chkhost, unsigned short chkport) {
int sock;
struct sockaddr_in chksock;
struct hostent *host = NULL;
if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {
syslog(LOG_ERR, "socket() creation error: %s", strerror(errno));
return 0;
}
memset(&chksock, 0, sizeof(chksock));
chksock.sin_family = AF_INET;
chksock.sin_port = htons(chkport);
/* get the server address */
if (inet_pton(AF_INET, chkhost, &(chksock.sin_addr.s_addr)) <= 0) {
if ((host = gethostbyname(chkhost)) == NULL) {
syslog(LOG_ERR, "%s", hstrerror(h_errno));
return 0;
}
memcpy(&(chksock.sin_addr.s_addr), &(host->h_addr_list[0]),
sizeof(struct in_addr));
}
/* try to connect */
if (connect(sock, (struct sockaddr *) &chksock, sizeof(chksock)) < 0) {
syslog(LOG_ERR, "connect() failed: %s", strerror(errno));
return 0;
}
close(sock);
return 1;
}
inet_pton() is the wrong task for that. It only accepts numerical addresses.
In former times, people used to use gethostbyname() for name resolution.
But as we have 2012 meanwhile, this method is outdated for several years now, as it is still restricted to AF_INET.
With the program below, you should achieve about the same and stay future compatible.
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdlib.h>
#include <stdio.h>
int is_network_up(char *chkhost, unsigned short chkport) {
int sock = -1;
struct addrinfo * res, *rp;
int ret = 0;
char sport[10];
snprintf(sport, sizeof sport, "%d", chkport);
struct addrinfo hints = { .ai_socktype=SOCK_STREAM };
if (getaddrinfo(chkhost, sport, &hints, &res)) {
perror("gai");
return 0;
}
for (rp = res; rp && !ret; rp = rp->ai_next) {
sock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (sock == -1) continue;
if (connect(sock, rp->ai_addr, rp->ai_addrlen) != -1) {
char node[200], service[100];
getnameinfo(res->ai_addr, res->ai_addrlen, node, sizeof node, service, sizeof
service, NI_NUMERICHOST);
printf("Success on %s, %s\n", node, service);
ret = 1; /* Success */
}
close(sock);
}
freeaddrinfo(res);
return ret;
}
int main(int argc, char** argv) {
if (argc > 1) {
printf("%s: %d\n", argv[1], is_network_up(argv[1], 22));
}
}
Make sure name resolution is working. See if you can ping the machine by name from the exact same environment in which your code runs.
If ping works, try telnet <machinename> <portnumber> -- If both of those work it is likely a problem with your code (which I did not look at in depth, too sleepy:).
Make sure you're converting anything returned by the OS as an ip address from network order to host order. IIRC, gethostbyname returns binary ip addresses in network order.
ntohl can be used on chksock.sin_addr.s_addr after the memcpy to achieve this.
#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)