connect() function fails when passing sockaddr_in as argument - c

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.

Related

issues with connect function

I'm trying to get started on socket programming in C, and I was following a few guides, but I'm always getting this error:
warning:
passing argument 2 of ‘connect’ from incompatible pointer type [-Wincompatible-pointer-types]
45 | int status= connect(socket_desc , (struct sockaddr *) &server , sizeof(server));
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| struct sockaddr *
It wants struct sockaddr * to be constant, but when I try to make it constant, the connect() function doesn't have enough arguments.
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main (){
int netsocket;
netsocket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(9002);
server.sin_addr.s_addr = INADDR_ANY;
int Verbindungsstatus = connect(netsocket, (struct sockaddr *) &server, sizeof(server));
if (Verbindungsstatus == -1){
printf("Connection error");
}
printf("Connected!");
return 0;
}
From the man page:
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
The second argument expects a const struct sockaddr *addr.
Perhaps try casting to (const struct sockaddr *) instead of (struct sockaddr *).
New code should use getaddrinfo() instead of manually filling the struct. See the man page for an example:
https://man7.org/linux/man-pages/man3/getaddrinfo.3.html
Aside: socket() may fail:
netsocket = socket(AF_INET, SOCK_STREAM, 0);
Check its return value.
errno = 0;
if ((netsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
}

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!

Problems with C code for buffer overflow

I'm writing a small tcp echo server for testing buffer overruns on Linux. I have two slightly different versions of the server code. When an over sized buffer is sent the the first it overflows as expected in the read function causing a Segmentation Fault. For the second version of the code I added a While (1) loop around the accept, read, and write functions so that the server will not exit under normal use, however when the same buffer is sent to the second server there is no overflow and the server does not crash at all. I'm having trouble figuring out why, the code is identical short of the while loop. Any help would be very appreciated. :)
SERVER 1
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen;
char recv[512];
bzero(recv,512);
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) exit(1);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) exit(1);
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) exit(1);
listen(sockfd,5);
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) exit(1);
int n = read(newsockfd,recv,1024);
if (n < 0) exit(1);
write(newsockfd,recv,n);
close(newsockfd);
return 0;
}
SERVER 2
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
int main(int argc, char *argv[])
{
int sockfd, newsockfd, portno, clilen;
char recv[512];
bzero(recv,512);
struct sockaddr_in serv_addr, cli_addr;
if (argc < 2) exit(1);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) exit(1);
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = atoi(argv[1]);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) exit(1);
listen(sockfd,5);
clilen = sizeof(cli_addr);
while (1) {
newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
if (newsockfd < 0) continue;
int n = read(newsockfd,recv,1024);
if (n < 0) continue;
write(newsockfd,recv,n);
close(newsockfd);
}
return 0;
}
Buffer overflows cause the stack to be overwritten, in particular the return address from a function. The actual overflow itself isn't what causes the segmentation fault, it's that when you later on return from the function that had the overflow, the return address has been corrupted. (Other possible segfaults from a buffer overflow include accessing memory from overwritten pointers, or using function pointers that have been overwritten, etc).
In your example, the while loop is preventing you from ever reaching the return statement, so while your buffer is being overflowed and your return address clobbered, that return address is never used, so the segfault doesn't occur.
If you want to verify that the overflow is occurring, I would recommend either watching in a debugger, or printing out the values inside the serv_addr and cli_addr structures, which I would expect would be clobbered by your overflow.
Also if you want to see the segfault from overflow, move the recv call and its destination buffer into a separate function, then call that function from inside the while(1) loop. The segfault should occur when the function with recv in it returns.
You cannot predict what your program will do when there is a buffer overflow. The behavior depends on what happens to be after the buffer and exactly what's in the overly-long input. Those things may depend on unrelated parts of your program (what addresses things are compiled at), and possibly even things like load addresses that change from run to run.

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

#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)

Extract IP from connection that listen and accept in socket programming in Linux in c

In the following code I would like to extract the IP address of the connected client after accepting an incoming connection. What should I do after the accept() to achieve it?
int sockfd, newsockfd, portno, clilen;
portno = 8090;
clilen = 0;
pthread_t serverIn;
struct sockaddr_in serv_addr, cli_addr;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
{
perror("ERROR opening socket");
}
bzero((char *) & serv_addr, sizeof (serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(portno);
serv_addr.sin_addr.s_addr = INADDR_ANY;
if (bind(sockfd, (struct sockaddr *) & serv_addr, sizeof (serv_addr)) < 0)
{
perror("ERROR on binding");
}
listen(sockfd, 5);
clilen = sizeof (cli_addr);
newsockfd = accept(sockfd, (struct sockaddr *) & cli_addr, &clilen);
Your cli_addr already contains the IP address and port of the connected client after accept() returns successfully, in the same format as your serv_addr variable. Use inet_ntop to convert IP to a string.
getpeername()
See the helpful description of how to use it over at the indispensable Beej's Guide to Network Programming.
You can follow this example :
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
{
int s;
struct sockaddr_in peer;
int peer_len;
.
.
.
/* We must put the length in a variable. */
peer_len = sizeof(peer);
/* Ask getpeername to fill in peer's socket address. */
if (getpeername(s, &peer, &peer_len) == -1) {
perror("getpeername() failed");
return -1;
}
/* Print it. The IP address is often zero because */
/* sockets are seldom bound to a specific local */
/* interface. */
printf("Peer's IP address is: %s\n", inet_ntoa(peer.sin_addr));
printf("Peer's port is: %d\n", (int) ntohs(peer.sin_port));
.
.
.
}
I think getpeername() is not needed - the client address is already filled into cli_addr by the accept() call.
You only need to use inet_ntop(), getnameinfo(), or gethostbyaddr() to print or get more information.
The API is described in the manual pages. You can either browse them from the console, starting with man socket and follow references to man getpeername or use Konqueror, which renders it nicely with links, if you ask for #socket address. In my case on Kubuntu it was necessary to install manpages-dev package.

Resources