Skip to content

xps_utils ​

xps_utils is a module containing a collection of utility functions that will be utilized throughout the project. Additional utility functions will be implemented as needed according to the project roadmap.

Usage ​

Utility code and functions are defined inside xps_utils.c and xps_utils.h placed in expserver/src/utils.

expserver/src/utils/xps_utils.h
c
#ifndef XPS_UTILS_H
#define XPS_UTILS_H

#include "../xps.h"

// Sockets
bool is_valid_port(u_int port);
int make_socket_non_blocking(u_int sock_fd);
struct addrinfo *xps_getaddrinfo(const char *host, u_int port);
char *get_remote_ip(u_int sock_fd);

// Other functions

#endif

Functions ​

is_valid_port() ​

Checks if a port number is within the valid range of 0 to 65535 (inclusive).

c
bool is_valid_port(u_int port) { return port >= 0 && port <= 65535; }

xps_getaddrinfo() ​

Resolves the given host and port to an IPv4 address using getaddrinfo(), and returns a pointer to the resulting addrinfo structure.

The getaddrinfo() function provided by the <netdb.h> header used to perform DNS lookup to resolve a hostname and service name to a set of socket addresses.

c
struct addrinfo {
  int              ai_flags;
  int              ai_family;
  int              ai_socktype;
  int              ai_protocol;
  socklen_t        ai_addrlen;
  struct sockaddr *ai_addr;
  char            *ai_canonname;
  struct addrinfo *ai_next;
};

Given above is the definintion for struct addrinfo. A call to getaddrinfo(host, port_str, &hints, &result) will return a linked list of all the resolved addresses. A given host and port combination could have multiple addresses. For example, the host google.com could resolve to multiple IP addresses.

We are only interested in the ai_addr field as it is taken by bind() system call to bind to the particular addresss.

inet_ntop() provided by <arpa/inet.h> converts the IPv4 address in binary form to text form.

The memory for struct addrinfo * is dynamically allocated by getaddrinfo(). Hence a corresponding free funciton, freeaddrinfo() is used to free the memory.

c
struct addrinfo *xps_getaddrinfo(const char *host, u_int port) {
  assert(host != NULL);
  assert(is_valid_port(port));

  struct addrinfo hints, *result;
  memset(&hints, 0, sizeof(struct addrinfo));
  hints.ai_family = AF_INET;
  hints.ai_socktype = SOCK_STREAM;

  char port_str[20];
  sprintf(port_str, "%u", port);

  int err = getaddrinfo(host, port_str, &hints, &result);
  if (err != 0) {
    logger(LOG_ERROR, "xps_getaddrinfo()", "getaddrinfo() error");
    return NULL;
  }

  char ip_str[30];
  struct sockaddr_in *ipv4 = (struct sockaddr_in *)result->ai_addr;
  if (inet_ntop(result->ai_family, &(ipv4->sin_addr), ip_str, sizeof(ip_str)) == NULL) {
    logger(LOG_ERROR, "xps_getaddrinfo()", "inet_ntop() failed");
    perror("Error message");
    freeaddrinfo(result);
    return NULL;
  }

  logger(LOG_DEBUG, "xps_getaddrinfo()", "host: %s, port: %u, resolved ip: %s", host, port, ip_str);

  return result;
}

make_socket_non_blocking() ​

Makes a socket non-blocking by modifying its file descriptor flags using the fcntl system call. It returns an error code if unsuccessful.

c
int make_socket_non_blocking(u_int sock_fd) {
  // Get the current socket flags
  int flags = fcntl(sock_fd, F_GETFL, 0);
  if (flags < 0) {
    logger(LOG_ERROR, "make_socket_non_blocking()", "failed to get flags");
    perror("Error message");
    return E_FAIL;
  }

  // Set flags with O_NONBLOCK
  if (fcntl(sock_fd, F_SETFL, flags | O_NONBLOCK) < 0) {
    logger(LOG_ERROR, "make_socket_non_blocking()", "failed to set flags");
    perror("Error message");
    return E_FAIL;
  }

  return OK;
}

get_remote_ip() ​

Retrieves the IP address of the remote peer connected to the given socket file descriptor sock_fd. It uses getpeername() to get the peer's address, then converts it from binary to text using inet_ntop().

c
char *get_remote_ip(u_int sock_fd) {
  struct sockaddr_in addr;
  socklen_t addr_len = sizeof(addr);
  char ipstr[INET_ADDRSTRLEN];

  if (getpeername(sock_fd, (struct sockaddr *)&addr, &addr_len) != 0) {
    logger(LOG_ERROR, "get_remote_ip()", "getpeername() failed");
    perror("Error message");
    return NULL;
  }

  char *ip_str = malloc(INET_ADDRSTRLEN);
  if (ip_str == NULL) {
    logger(LOG_ERROR, "get_remote_ip()", "malloc() failed for 'ip_str");
    return NULL;
  }

  inet_ntop(AF_INET, &addr.sin_addr, ip_str, INET_ADDRSTRLEN);

  return ip_str;
}