C open second connection on socket - c

I need to program a distributed hash table with P2P. The peer which gets the request from the client needs to have an open connection to send the response back and also needs to open a connection to its predecessor to receive the information. I tried using accept a second time on the same socket but it didn't work.
I read a few solutions about forking but I'm not sure if it's suitable for my needs. (And I'm not really sure how to implement it correctly here)
My basic setup:
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
if ((gai_status = getaddrinfo(NULL, port, &hints, &md_srvinfo)) != 0) {
fprintf(stderr, "error: %s\n", gai_strerror(gai_status));
exit(gai_status);
}
int md_listen_socket = socket(md_srvinfo->ai_family, md_srvinfo->ai_socktype, md_srvinfo->ai_protocol);
int yes;
setsockopt(md_listen_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(1));
bind(md_listen_socket, md_srvinfo->ai_addr, md_srvinfo->ai_addrlen);
listen(md_listen_socket, 2);
while(1) {
// accept connection
struct sockaddr_storage client_address;
socklen_t addr_size = sizeof(client_address);
int md_active_socket;
md_active_socket = accept(md_listen_socket, (struct sockaddr *) &client_address, &addr_size);
.
.
.
.
recv(stuff from predecessor peer)
send(stuff to md_active_socket)
}
Edit:
struct addrinfo *pred_srvinfo;
getaddrinfo(NULL, pred_port, &hints, &pred_srvinfo);
int pred_listen_socket = socket(pred_srvinfo->ai_family, pred_srvinfo->ai_socktype, pred_srvinfo->ai_protocol);
struct sockaddr_storage pred_address;
socklen_t pred_addr_size = sizeof(pred_address);
int pred_active_socket = accept(pred_listen_socket, (struct sockaddr *) &pred_address, &pred_addr_size);
connect(pred_active_socket, pred_srvinfo->ai_addr, pred_srvinfo->ai_addrlen);
Can someone lead me to the right path?

Related

getaddrinfo and INADDR_ANY

Spent a couple of hours searching, still puzzled. From what I've found, INADDR_ANY is meant to specify that the socket will accept connections with any address that is assigned to the server. The following, however, results in the client only being able to connect to localhost:7777 from the same machine.
addrinfo hints;
addrinfo* result;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
getaddrinfo(INADDR_ANY, "7777", &hints, &result);
SOCKET listenSocket = INVALID_SOCKET;
listenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
bind(listenSocket, result->ai_addr, (int)result->ai_addrlen);
The only solution that I've found is to change INADDR_ANY to the machine's local IP:
getaddrinfo("192.168.0.105", "7777", &hints, &result);
I need to understand how INADDR_ANY works, because I feel like I'm just misusing it somehow. Any help would be appreciated.
INADDR_ANY has nothing to do with your issue.
The first parameter of getaddrinfo() is a const char * specifying an IP address or hostname. But INADDR_ANY is an integer instead. The only reason your code even compiles is because INADDR_ANY is defined as integer constant 0, which is the only integer constant that is allowed to be assigned to a pointer. So, you are actually passing NULL to the first parameter. Which is fine in this situation, as that is what you need anyway.
What you are not taking into account is that getaddrinfo() returns a linked list of addresses, which may contain multiple addresses, especially if you use AF_UNSPEC. Using that family tells getaddrinfo() that it can return addresses for both IPv4 and IPv6. But you are only using the first address in the list, which just happens to correspond to localhost (127.0.0.1 in IPv4, ::1 in IPv6).
For a server, you should be creating and binding a separate listening socket for each address in the output list. That will let you bind to all of the addresses that match the criteria you passed to getaddrinfo(), eg:
addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
addrinfo* result;
if (getaddrinfo(NULL, "7777", &hints, &result) == 0)
{
for (addrinfo *addr = result; addr != NULL; addr = addr->ai_next)
{
SOCKET listenSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (listenSocket != INVALID_SOCKET)
{
bind(listenSocket, addr->ai_addr, (int)addr->ai_addrlen);
listen(listenSocket, ...);
// store listenSocket in a list for later use...
}
}
freeaddrinfo(result);
}
// use listening sockets as needed...
Alternatively, as Sam V mentioned in comments, you can skip getaddrinfo(), it is not really helping you in this situation. You could just create and bind 2 sockets directly, one for IPv4 and one for IPv6:
SOCKET listenSocket4 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket4 != INVALID_SOCKET)
{
sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_port = htons(7777);
addr.sin_addr.s_addr = INADDR_ANY;
bind(listenSocket4, (sockaddr*) &addr, sizeof(addr));
listen(listenSocket4, ...);
}
SOCKET listenSocket6 = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket6 != INVALID_SOCKET)
{
sockaddr_in6 addr = {};
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(7777);
addr.sin6_addr = in6addr_any;
bind(listenSocket6, (sockaddr*) &addr, sizeof(addr));
listen(listenSocket6, ...);
}
// use listening sockets as needed...
Or better, create and bind a single dual-stack socket that supports both IPv4 and IPv6:
SOCKET listenSocket = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (listenSocket != INVALID_SOCKET)
{
BOOL off = FALSE;
setsockopt(listenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&off, sizeof(off));
sockaddr_in6 addr = {};
addr.sin6_family = AF_INET6;
addr.sin6_port = htons(7777);
addr.sin6_addr = in6addr_any;
bind(listenSocket, (sockaddr*) &addr, sizeof(addr));
listen(listenSocket, ...);
}
// use listening socket as needed...

gethostbyname() alternatives in c

I am currently doing a project on networking game where I need to design a game where maximum of 3 clients can connect to the server and the game is played between all the clients and server. I am using the "sockaddr_in" structure at both the server and client side.
In my game, anyone can become the server and the clients should give the correct IP address and port number to be able to connect to the server. When I hard code the values of the IP address of server and port number in "server_address.sin_addr.s_addr" and "server_address.sin_port" respectively the game works fine. But hard coded will not solve my problem of anyone being a server and asking the clients to enter the server's address and port number. So, I used "gethostbyname()" function call on the client's side. But it did not solve my problem. (may the reason is that behaviour of gethostbyname() when passed a numeric string is unspecified. (Source : link) .
Below is the code used by me at server side :
struct sockaddr_in serv_addr, client_addr;
/* open a socket */
if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
err_ret = errno;
return err_ret;
}
/* set initial values */
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
serv_addr.sin_addr.s_addr = inet_addr(IP);
memset(&(serv_addr.sin_zero), 0, 8);
/* bind address with socket */
if(bind(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) == -1)
{
err_ret = errno;
return err_ret;
}
and at client side
struct sockaddr_in serv_addr;
struct hostent *to;
/* generate address */
if((to = gethostbyname(IP))==NULL)
{
err_ret = h_errno;
fprintf(stderr, "gethostbyname() error...\n");
return err_ret;
}
/* open a socket */
if((newfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
err_ret = errno;
fprintf(stderr, "socket() error...\n");
return err_ret;
}
/* set initial values */
serv_addr.sin_family = AF_INET;
serv_addr.sin_port = htons(PORT);
memcpy(&serv_addr.sin_addr, to->h_addr_list[0], to->h_length);
memset(&(serv_addr.sin_zero), 0, 8);
Can anyone here tell an efficient way to carry out the above process?
Any help would be appreciated.
getaddrinfo has superseded gethostbyname. That should make it easier to create sockaddr_in structs from IP address strings.
Sample code to convert either a string in numeric form or as a hostname to a sockaddr_in.
struct addrinfo hints = {};
addrinfo* pResultList = NULL;
struct sockaddr_in addr = {};
char* hostname = "1.2.3.4";
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
// hints.ai_flags = AI_NUMERICHOST; // if you know hostname is a numeric stirng, you can skip the DNS lookup by setting this flag
result = getaddrinfo(hostname , NULL, &hints, &pResultList);
if (result)
memcpy(&addr, pResultList->ai_addr, sizeof(addr));
if (pResultList != NULL)
{
::freeaddrinfo(pResultList);
}

Invalid Argument: connect() C socket programming

I'm working on a C program that creates a connection between a client and server. When I run connect on the socket I've already created I keep getting an error that I'm passing an invalid argument.
Any help would be awesome!
void client(char* ipAddress, char* serverPort){
//code for setting up the IP address and socket information from Beej's Guide to Network Programming
//Need to setup two addrinfo structs. One for the client and one for the server that the connection will be going to
int status;
//client addrinfo
struct addrinfo hints, *res; // will point to the results
//server addrinfo
int socketDescriptor;
int addressLength;
memset(&hints, 0, sizeof hints); // make sure the struct is empty
hints.ai_family = AF_UNSPEC; // don't care IPv4 or IPv6
hints.ai_socktype = SOCK_STREAM; // TCP stream sockets
hints.ai_flags = AI_PASSIVE; // fill in my IP for me
//setup client socket
if ((status = getaddrinfo(ipAddress, serverPort, &hints, &res)) != 0) {
printf("%s \n", "This error above");
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
exit(1);
}
if((socketDescriptor = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) ==-1){
perror("client: socket");
}
addressLength = sizeof hints;
if(connect(socketDescriptor, res->ai_addr, addressLength)==-1){
close(socketDescriptor);
perror("client: connect");
}
}
Apart from some inconsistent variable names in your code,
this seems to be wrong:
addressLength = sizeof hints;
if(connect(socketDescriptor, res->ai_addr, addressLength)==-1) ...
It should be
if(connect(socketDescriptor, res->ai_addr, res->ai_addrlen)==-1) ...

UDP Socket in C - Setting it up wrong?

I'm working on a project that involves sending various requests to a server through UDP. However, I seem to be setting up the socket entirely wrong, as the server does not respond to any of my requests. We were provided with a server binary to test against, and the code below ellicits no response. Am I setting up the UDP socket correctly? If so, am I somehow using sendto wrong? I have confirmed that I am sending the correct number of bits.
The input for the program is: ./client [URL] [port] [username], and I always test with ./client localhost 8080 user. Here is the struct I am sending and the code.
struct request_login {
int req_type; /* = REQ_LOGIN */
char req_username[32];
} packed;
Code:
struct sockaddr_in sa;
int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd == -1){
printf("Could not create socket.");
exit(EXIT_FAILURE);
}
// Prepare the socket address
memset(&sa, 0, sizeof sa);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr(argv[1]);
// Convert to network order
sa.sin_port = htonl(atoi(argv[2]));
// Assemble and send login request
struct request_login * reqlogin = (struct request_login *) malloc(sizeof(struct request_login));
reqlogin->req_type = REQ_LOGIN;
strcpy(reqlogin->req_username, argv[3]);
int res = sendto(sockfd, reqlogin, sizeof (struct request_login), 0, (struct sockaddr*)&sa, sizeof sa);
free(reqlogin)
Huh?
This:
sa.sin_addr.s_addr = inet_addr(argv[1]);
certainly won't do the right thing if, as you say, argv[1] is typically "localhost". You need to look up the host name, so that you get an IP address. You can only use inet_addr() if the input is a dotted IP address, not a host name.
Look at getaddrinfo().
After re-reading your code a couple of times, I think I know what one cause of the error may be:
sa.sin_port = htonl(atoi(argv[2]));
The port number is a short so you should use htons instead. It's very small and easy to miss.
try this instead:
struct addrinfo hint;
memset(&chk,0,sizeof(chk));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_DGRAM;
hint.ai_protocol = IPPROTO_UDP;
struct addrinfo* servAddr = NULL;
int ret = getaddrinfo(argv[1],atoi(argv[2]),&hint,&servAddr);
if (-1 == ret)
{
perror("getaddrinfo failed");
exit(EXIT_FAILURE);
}
int sockfd = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
if(sockfd == -1){
printf("Could not create socket.");
exit(EXIT_FAILURE);
}
// Assemble and send login request
struct request_login reqlogin;
reqlogin.req_type = REQ_LOGIN;
strcpy(reqlogin.req_username, argv[3]);
int res = sendto(sockfd, &reqlogin, sizeof (struct request_login), 0, servAddr->ai_addr, servAddr->ai_addrlen);

C open socket on a specific IP

My website can be accessed using any of 2 different static IPs (IPv4).
Is it possible to open a TCP connection to another server, specifying which of the 2 IPs to use as a return address ?
x.x.x.x (my server) => z.z.z.z (destination server)
y.y.y.y (my server) => z.z.z.z (destination server)
Error-checking, etc. in this example has been omitted for simplicity sakes :
struct addrinfo hints, *result;
hints.ai_flags = 0;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_addrlen = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
getaddrinfo(domain, "80", &hints, &result);
socket(result->ai_family, result->ai_socktype, result->ai_protocol);
Like #hochl says, you use the bind system call:
struct sockaddr_in sin = { 0 };
int sock;
/* Create a socket address, with a specific port and (local) ipnumber */
sin.sin_family = AF_INET;
sin.sin_port = htons(80);
inet_aton("1.2.3.4", &sin.sin_addr);
/* Create socket */
sock = socket(AF_INET, SOCK_STREAM, 0);
/* Bind socket to the local address */
bind(sock, (struct sockaddr *) &sin, sizeof(sin));
/* Now connect to remote server... */
/* connect(...) */
It should be noted that use of inet_aton is normally discouraged, in favor of inet_pton.
Not sure, but can't you bind your socket to one of your local addresses before you connect? In this case you may choose which of your IPs is used for the connection.

Resources