C open socket on a specific IP - c

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.

Related

C open second connection on socket

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?

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);
}

What Is The Purpose Of A sockaddr_in In This Program?

For one of my classes I have to program a server and a client, and we were given some sample code to work with. Here is the snippet I am confused about:
main()
{
int sock, sock_current, cc, fromlen, tolen; /*sd is the socket */
int addrlen;
struct sockaddr_in sin;
struct sockaddr_in pin;
/* get an internet domain socket */
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
/* complete the socket structure */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(PORT);
/* bind the socket to the port number */
if (bind(sock, (struct sockaddr *) &sin, sizeof(sin)) == -1) {
perror("bind");
exit(1);
}
I think that the purpose of the sockaddr_in struct sin is to store a local IP address to associate with sock when it gets bound. Am I correct on that? If I am correct, how does this snippet of code accomplish that? I don't get it:
/* complete the socket structure */
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(PORT);
The bind function assigns a local protocol address to a socket. The purpose of sin here is to tell bind which local address to assign.
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET; //it's an IPv4 address
sin.sin_addr.s_addr = INADDR_ANY; //wildcard IP address
sin.sin_port = htons(PORT); //bind to this port number
In addition to what Yu said (which is all correct), if you don't call bind before calling listen() (which is likely right after the snippet you posted), it will listen on a random TCP port (because you opened an IPv4 TCP socket when you called socket()).
This is actually useful sometimes -- I have an app that broadcasts the port it runs on over the LAN for clients to find it, so it doesn't matter what port it's on.
Also, you can bind before you call connect(), if you want, to force what port or interface the outgoing connection will be made on, but this is also uncommon.

How to determine IP used by client connecting to INADDR_ANY listener socket in C

I have a network server application written in C, the listener is bound using INADDR_ANY so it can accept connections via any of the IP addresses of the host on which it is installed.
I need to determine which of the server's IP addresses the client used when establishing its connection - actually I just need to know whether they connected via the loopback address 127.0.0.1 or not.
Partial code sample as follows (I can post the whole thing if it helps):
static struct sockaddr_in serverAddress;
serverAddress.sin_family = AF_INET;
serverAddress.sin_addr.s_addr = INADDR_ANY;
serverAddress.sin_port = htons(port);
bind(listener, (struct sockaddr *) &serverAddress, sizeof(serverAddress));
listen(listener, CONNECTION_BACKLOG);
SOCKET socketfd;
static struct sockaddr_in clientAddress;
...
socketfd = accept(listener, (struct sockaddr *) &clientAddress, &length);
The solution to my specific problem (thanks to zildjohn01) in case anyone needs it, is shown below:
int isLocalConnection(int socket){
struct sockaddr_in sa;
int sa_len = sizeof(sa);
if (getsockname(socket, &sa, &sa_len) == -1) {
return 0;
}
// Local access means any IP in the 127.x.x.x range
return (sa.sin_addr.s_addr & 0xff) == 127;
}
You can use the getsockname function.
The getsockname() function retrieves the locally-bound name of the specified socket
From your code
socketfd = accept(listener, (struct sockaddr *) &clientAddress, &length);
Analyse the returned clientAddress. This is what you need.

Resources