Getting an address IP and using a socket to connect to it - c

I'm writing an HTTP client using UNIX sockets (as part of a homework assignment). I currently have this working code to connect to a given IP Address:
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
char *server_address = "127.0.0.1";
struct sockaddr_in address;
if (sockfd < 0) {
printf("Unable to open socket\n");
exit(1);
}
// Try to connect to server_address on port PORT
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(server_address);
address.sin_port = htons(PORT);
if (connect(sockfd, (struct sockaddr*) &address, sizeof(address)) < 0) {
printf("Unable to connect to host\n");
exit(1);
}
However, I now want to modify it so that server_address could also be something that is not an IP, such as "google.com". I've been trying to figure out how to do this using gethostbyname, but I am having trouble.
Will gethostbyname accept both an IP Address OR an address like "google.com" and have it work correctly? (or should I try and run a regex on the address first and do something else if it is an IP Address)?
I have tried the following code to try to get it working with something like "google.com", but I am getting a warning warning: assignment makes integer from pointer without a cast
struct hostent *host_entity = gethostbyname(server_address);
address.sin_addr.s_addr = host_entity->h_addr_list[0];
I know I am doing-it-wrong, but the gethostbyname documentation is atrocious.

What you want is maybe getaddrinfo(3):
#include <sys/socket.h>
#include <netdb.h>
static int
resolve(const char *host, const char *port)
{
struct addrinfo *aires;
struct addrinfo hints = {0};
int s = -1;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
#if defined AI_ADDRCONFIG
hints.ai_flags |= AI_ADDRCONFIG;
#endif /* AI_ADDRCONFIG */
#if defined AI_V4MAPPED
hints.ai_flags |= AI_V4MAPPED;
#endif /* AI_V4MAPPED */
hints.ai_protocol = 0;
if (getaddrinfo(host, port, &hints, &aires) < 0) {
goto out;
}
/* now try them all */
for (const struct addrinfo *ai = aires;
ai != NULL &&
((s = socket(ai->ai_family, ai->ai_socktype, 0)) < 0 ||
connect(s, ai->ai_addr, ai->ai_addrlen) < 0);
close(s), s = -1, ai = ai->ai_next);
out:
freeaddrinfo(aires);
return s;
}
This version gets you a socket from a host/port pair. It also takes IP addresses for host and service strings for port. It will, however, connect to the host in question already.

Related

Connecting a socket, using getaddrinfo, times out

I spent hours trying to find out my issue, just found the solution as I was writing my question (it always help when you need to formalize your issue and explain it). I post it, hopefully it helps someone.
Using getaddrinfo, if I try to connect a socket to my server, doing (what I thought was) exactly what is being explained on tons of website aswell as in the man page sample code of getaddrinfo, it FAILS with a "connection timed out" error message:
(Simplifying the code to be more concise)
void connect_UsingGetAddrInfo_Wrong (std::string host, unsigned short int port, int& socketfd)
{
//simplified loops & error handling for concision
int x;
int domain = AF_INET; // IP_v4
int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.
addrinfo hints, *addr;
//fine-tune hints according to which socket you want to open
hints.ai_family = domain;
hints.ai_socktype = socketType;
hints.ai_protocol = 0; // no enum : possible value can be read in /etc/protocols
hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
x = getaddrinfo(hostname, NULL, &hints, &addr);
//shall rather loop on addr linked list, but this is not the topic here.
socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}
However, I was able to connect a socket to the same server, using gethostbyname method.
void connect_UsingGetHostByName_Deprecated (std::string host, unsigned short int port, int& socketfd)
{
//simplified loops & error handling for concision
int x;
int domain = AF_INET; // IP_v4
int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.
struct hostent DNS, *r;
char buf[1024];
x = gethostbyname_r(hostname.c_str(), & DNS, buf, sizeof(buf), & r, & err));
socketfd = socket(domain, socketType, 0);
//server.
sockaddr_in server;
memset(&server, 0x00, sizeof(server));
server.sin_family=domain;
server.sin_port=htons(port);
memcpy(& server.sin_addr.s_addr, DNS.h_addr, (size_t) DNS.h_length);
x = connect(socketfd, (struct sockaddr *) & server, sizeof(server));
}
Running code shows that both version correctly retrieve the valid IP address of the server. Still the first one won't connect and will time out.
Why ?
The reason why it kept failing was : to retrieve the addrinfo, I had left the field 'service' equals to NULL. It will still return success and provide you an address (which you can map with getnameinfo to the right IP address). Still the address won't be usable to connect your socket !
I had found an hybrid version of both methods here :
https://codereview.stackexchange.com/questions/17863/socket-connect-realization-gethostbyname-or-getnameinfo
This one is functional, but i don't buy the casting
void connect_UsingGetAddrInfo_HYBRID (std::string host, unsigned short int port, int& socketfd)
{
//simplified loops & error handling for concision
int x;
int domain = AF_INET; // IP_v4
int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.
addrinfo hints, *addr;
//fine-tune hints according to which socket you want to open
hints.ai_family = domain;
hints.ai_socktype = socketType;
hints.ai_protocol = 0; // no enum : possible value can be read in /etc/protocols
hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
x = getaddrinfo(host, NULL, &hints, &addr);
socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
//here is the hybrid part
sockaddr_in servAddr;
memset(&servAddr, 0, sizeof(servAddr));
servAddr.sin_family = addr->ai_family;
servAddr.sin_addr.s_addr = *((uint32_t*) & (((sockaddr_in*)addr->ai_addr)->sin_addr));
servAddr.sin_port = htons(port);
x=connect(socketfd, (struct sockaddr*) &servAddr, sizeof(servAddr));
}
In the end, it helped me find the rootcause :
void connect_UsingGetAddrInfo_FIXED (std::string host, unsigned short int port, int& socketfd)
{
//simplified loops & error handling for concision
int x;
int domain = AF_INET; // IP_v4
int socketType = SOCK_STREAM; // Sequenced, reliable, connection-based byte streams.
addrinfo hints, *addr;
//fine-tune hints according to which socket you want to open
hints.ai_family = domain;
hints.ai_socktype = socketType;
hints.ai_protocol = 0; // no enum : possible value can be read in /etc/protocols
hints.ai_flags = AI_CANONNAME | AI_ALL | AI_ADDRCONFIG;
//Precise here the port !
const char* service = std::to_string(port).c_str();
x = getaddrinfo(host, service, &hints, &addr);
socketfd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
x = connect(socketfd, addr->ai_addr, addr->ai_addrlen);
}
Hope this will help someone one day !

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

sockaddr value changes unexpectedly after calling getaddrinfo()

I am programming an UDP client. I want to bind the socket to a given port on the client machine, so that the same port is always used for all sends. I get the sockaddr for the server using getaddrinfo, and I do the same to get the sockaddr which I pass to the call to getaddrinfo. However, after the second call to getaddrinfo the address of the server machine changes, and I end up sending the packet from the client machine to the client machine itself.
The following code is a standalone example that reproduces the error:
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVER_HOST "www.google.com"
#define UDP_PORT "4000"
static struct sockaddr_in *destination_addr = NULL;
static int client_port;
int main(){
uint8_t bytes[5] = { 0xaa, 0xab, 0xac, 0xad, 0xaf}; //some data to send
uint16_t length = 5;
int status;
//initialize socket and bind
if (destination_addr == NULL) {
struct addrinfo hints;
struct addrinfo *servinfo, *p;
srand(time(NULL));
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
if ((status = getaddrinfo(SERVER_HOST, UDP_PORT, &hints, &servinfo)) != 0) {
printf("Unable to send UDP. Reason: %s", gai_strerror(status));
return 0;
}
for (p = servinfo; p != NULL; p = p->ai_next) {
if (p->ai_addr != NULL)
destination_addr = (struct sockaddr_in *) p->ai_addr;
}
client_port = 1027 + rand()%50000;
freeaddrinfo(servinfo);
printf("Created destination_addr with IP %s\n", inet_ntoa(destination_addr->sin_addr));
}
int send_socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
if (send_socket_fd == -1) {
printf("Unable to create UDP socket. Reason: %s", strerror(errno));
return 0;
}
printf("IP after socket creation is %s\n", inet_ntoa(destination_addr->sin_addr));
int yes = 1;
if (setsockopt(send_socket_fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof (int)) == -1) {
perror("setsockopt");
return 0;
}
printf("IP after sockopt is %s\n", inet_ntoa(destination_addr->sin_addr));
// bind to local address
char str_client_port[6];
snprintf(str_client_port, 5, "%d", client_port);
struct addrinfo *source_addr_info;
struct addrinfo hints;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
// ***** destination_addr changes after this call *****
getaddrinfo (NULL, str_client_port, &hints, &source_addr_info);
printf("IP after getaddrinfo is %s\n", inet_ntoa(destination_addr->sin_addr));
bind(send_socket_fd, source_addr_info->ai_addr, source_addr_info->ai_addrlen);
printf("IP after binding is %s\n", inet_ntoa(destination_addr->sin_addr));
// send
int bytes_sent = sendto(send_socket_fd, bytes, length, 0, (struct sockaddr *)destination_addr, sizeof *destination_addr);
printf("Sent to IP %s\n", inet_ntoa(destination_addr->sin_addr));
if (bytes_sent != length){
if (bytes_sent == -1){
printf("UDP send failed. Reason: %s", strerror(errno));
}
else {
printf("UDP: not all bytes could be sent.");
}
}
close(send_socket_fd);
return 1;
}
The output generated by the execution of this program in my machine is:
Created destination_addr with IP 64.233.167.105
IP after socket creation is 64.233.167.105
IP after sockopt is 64.233.167.105
IP after getaddrinfo is 0.0.0.0
IP after binding is 0.0.0.0
Sent to IP 0.0.0.0
I am rather new to socket programming in C, and pretty sure I am doing some silly mistake, but after googling a lot and trying many things, I am still stuck with this. Any idea?
Solved. As #molbdnilo pointed out, the error was caused by the call to freeaddrinfo. To fix it I now copy the value pointed by p->ai_addr, so that it is not lost when freeing. I substituted:
if (p->ai_addr != NULL)
destination_addr = (struct sockaddr_in *) p->ai_addr;
with
if (p->ai_addr != NULL){
destination_addr = malloc(sizeof *destination_addr);
memcpy(destination_addr, (struct sockaddr_in *)p->ai_addr, sizeof *p->ai_addr);
}
and it worked.

trouble accepting ipv6 requests on c web server

The following c function I wrote returns a file descriptor that accepts IPv4 connections but not IPv6.
Could someone help me figure out what went wrong?
I suspect I did not use getaddrinfo() correctly.
Open file descriptor that listens to connections.
/*
* open_listenfd - open and return a listening socket on port
* Returns -1 and sets errno on Unix error.
*/
int open_listenfd(int port)
{
const char* hostname=0;
// Converts port to string
char* pName = malloc(numPlaces(port) + 1);
sprintf(pName, "%d", port);
const char* portname= pName;
struct addrinfo hints;
memset(&hints,0,sizeof(hints));
hints.ai_family=AF_UNSPEC;
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol= 0;
hints.ai_flags=AI_PASSIVE|AI_ADDRCONFIG;
struct addrinfo* res=0;
int err=getaddrinfo(hostname,portname,&hints,&res);
free(pName);
if (err!=0) {
return -1;
}
int listenfd, optval=1;
struct sockaddr_in serveraddr;
/* Create a socket descriptor */
if ((listenfd = socket(res->ai_family,res->ai_socktype, res->ai_protocol)) < 0)
return -1;
/* Eliminates "Address already in use" error from bind. */
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int)) < 0)
return -1;
/* Listenfd will be an endpoint for all requests to port
on any IP address for this host */
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = /*AF_INET;*/ AF_UNSPEC;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(listenfd, res->ai_addr, res->ai_addrlen/*(SA *)&serveraddr, sizeof(serveraddr)*/) < 0)
return -1;
freeaddrinfo(res);
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, 1024) < 0)
return -1;
return listenfd;
}
If you want your server to listen on IPV4 and IPV6 addresses you need to could set up two socket to listen on.
getaddrinfo() might return information for more then one internet address for given host and/or service.
The member ai_family of the hints structure passed specifies which address family the caller is interest in. If AF_UNSPEC is specified IPV4 and IPV6 addresses might be returned.
To find out if there are such addresses available you might like to mod you code like so:
int open_listenfd(int port, int * pfdSocketIpV4, int * pfdSocketIpV6)
{
...
struct addrinfo hints = {0};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE|AI_ADDRCONFIG;
hints.ai_next = NULL;
struct addrinfo * res = NULL;
int err=getaddrinfo(hostname, portname, &hints, &res);
free(pName);
if (err)
{
return -1;
}
struct addrinfo * pAddrInfoIpV4 = NULL;
struct addrinfo * pAddrInfoIpV6 = NULL;
{
struct addrinfo * pAddrInfo = res;
/* Loop over all address infos found until a IPV4 and a IPV6 address is found. */
while (pAddrInfo)
{
if (!pAddrInfoIpV4 && AF_INET == pAddrInfo->ai_family)
{
pAddrInfoIpV4 = pAddrInfo; /* Take first IPV4 address available */
}
else if (!pAddrInfoIpV6 && AF_INET6 == pAddrInfo->ai_family)
{
pAddrInfoIpV6 = pAddrInfo; /* Take first IPV6 address available */
}
else
{
break; /* Already got an IPV4 and IPV6 address, so skip the rest */
}
pAddrInfo= pAddrInfo->ai_next; /* Get next address info, if any */
}
}
if (pAddrInfoIpV4)
{
... /* create, bind and make IPV4 socket listen */
int fdSocketIpV4 = socket(pAddrInfoIpV4->ai_family,...
*pfdSocketIpV4 = fdSocketIpV4;
}
if (pAddrInfoIpV6)
{
/* create, bind and make IPV6 socket listen */
int fdSocketIpV6 = socket(pAddrInfoIpV6->ai_family,...
*pfdSocketIpV6 = fdSocketIpV6;
}
freeaddrinfo(res);
...
Then call it like so:
...
int fdSocketIpV4 = -1;
int fdSocketIpV6 = -1;
if (0 > open_listenfd(port, &fdSocketIpV4, &fdSocketIpV6))
{
printf("Error executing 'open_listenfd()'\n");
}
else
{
... /* go for accepting connectings on 'fdSocketIpV4' and 'fdSocketIpV6' */
Update:
As commented by Per Johansson an alternative approach would be to set up a dual stack socket, supporting both, Ipv4 and Ipv6, as mentioned by this answer: How to support both IPv4 and IPv6 connections

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

Resources