lci/inet.c

305 lines
7.5 KiB
C

#include "inet.h"
// inet_setup (TCP:client / UDP:client)
//
// Sets up an inet_host structure.
void
inet_setup(inet_host_t *host,
int protocol,
const char *addr,
unsigned short port)
{
// Set up our inet_host structure
host->protocol = protocol;
// Set up our sockaddr_in structure
host->addr.sin_family = AF_INET;
host->addr.sin_port = htons(port);
host->addr.sin_addr.s_addr =
(addr == IN_ADDR_ANY ? IN_ADDR_ANY : inet_addr(addr));
memset(host->addr.sin_zero, 0, sizeof(host->addr.sin_zero));
}
// inet_open (TCP / UDP)
//
// Prepares `host' for transmission.
//
// `protocol' specifies the protocol to use for the transmission and is either
// `IN_PROT_TCP' or `IN_PROT_UDP'.
//
// `addr' is the IP address the host session structure will use: either a string
// of the form `X.X.X.X', where each `X' is the textual representation of a
// number in the range 0 to 255, or `IN_ADDR_ANY' to specify the current host's
// IP address.
//
// `port' is the port number to bind to on the host machine. Specifying
// IN_PORT_ANY attempts to use any available port assigned by the kernel. Use
// this option when the port your host structure is bound to does not matter.
//
// Returns one of:
// 0 Success.
// -EIN_SOCK Error acquiring socket file descriptor.
// -EIN_BIND Error binding socket to port.
int
inet_open(inet_host_t *host,
int protocol,
const char *addr,
unsigned short port)
{
// If we are not able to get a socket, fail with a socket error
host->fd = socket(PF_INET, protocol, 0);
if (host->fd < 0) {
perror("Error acquiring socket file descriptor!\n");
return -EIN_SOCK;
}
// Set up our sockaddr_in structure
inet_setup(host, protocol, addr, port);
// If we are not able to bind to a port, fail with a bind error
if (bind(host->fd, (struct sockaddr *)&(host->addr),
sizeof(host->addr)) < 0) {
perror("Error acquiring socket file descriptor!\n");
return -EIN_BIND;
}
// Set up our host protocol
host->protocol = protocol;
return 0;
}
// inet_accept (TCP:server)
//
// Accepts an incoming connection from the `remote' host to the `local' host.
//
// Returns one of:
// 0 Success.
// -EIN_PROT Incorrect protocol.
// -EIN_LSTN Error listening.
// -EIN_ACPT Error accepting connection.
int
inet_accept(inet_host_t *remote,
inet_host_t *local)
{
socklen_t len = sizeof(remote->addr);
// Make sure we only accept TCP sessions
if (local->protocol != IN_PROT_TCP) {
fprintf(stderr, "Error accepting from non-TCP protocol!\n");
return -EIN_PROT;
}
// Listen for an acceptable connection
if (listen(local->fd, IN_BACKLOG) < 0) {
perror("Error encountered while listening!\n");
return -EIN_LSTN;
}
// Accept the connection
remote->fd = accept(local->fd, (struct sockaddr *)&(remote->addr), &len);
if (remote->fd < 0) {
perror("Error accepting connection!\n");
return -EIN_ACPT;
}
return 0;
}
// inet_connect (TCP:client)
//
// Connects the `local' host to the `remote' host.
//
// Returns one of:
// 0 Success.
// -EIN_PROT Incorrect protocol.
// -EIN_CONN Error connecting to port.
int
inet_connect(inet_host_t *local,
inet_host_t *remote)
{
// Make sure we only connect TCP sessions
if (local->protocol != IN_PROT_TCP) {
fprintf(stderr, "Error accepting from non-TCP protocol!\n");
return -EIN_PROT;
}
// Connect to remote host
if (connect(local->fd, (struct sockaddr *)&(remote->addr), sizeof(remote->addr)) < 0) {
perror("Error connecting to remote host!\n");
return -EIN_CONN;
}
// Link `local->fd' and `remote->fd' on the client because `inet_send' always
// sends data through `remote->fd' for TCP connections (this makes sense
// intuitively, as one would send data *to* a remote address). However, a TCP
// client (which would be the only host to call this function) always
// communicates through it's `local->fd' and leaves `remote->fd' unused so we
// may safely repurpose it for the sake of continuity.
remote->fd = local->fd;
return 0;
}
// inet_receive (TCP / UDP)
//
// Receives a `len' bytes sent from `remote' to `local' and stores them in
// `data'. If no message is received within `timeout' seconds, -EIN_TIME is
// returned.
//
// Setting `timeout' to 0 causes the function to return immediately if there is
// no data to receive and setting `timeout' to -1 causes the function to block
// until data is ready to be received.
//
// Returns one of:
// Number of bytes received Success.
// -EIN_TIME Timeout.
// -EIN_RECV Error receiving data.
int
inet_receive(inet_host_t *remote,
inet_host_t *local,
void *data,
int len,
int timeout)
{
fd_set fds;
struct timeval time;
int size;
// Set the timeout interval
time.tv_sec = timeout;
time.tv_usec = 0;
// Initialize the file descriptor set
FD_ZERO(&fds);
// Depending on the protocol, receive data
switch (local->protocol) {
case IN_PROT_TCP:
FD_SET(remote->fd, &fds);
// Block for a specified amount of time
if (timeout != -1)
select(remote->fd + 1, &fds, NULL, NULL, &time);
else
select(remote->fd + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(remote->fd, &fds)) {
size = recv(remote->fd, data, len, 0);
if (size < 0) {
perror("Error receiving data!\n");
return -EIN_RECV;
}
}
else
return -EIN_TIME;
break;
case IN_PROT_UDP: {
socklen_t n = sizeof(remote->addr);
FD_SET(local->fd, &fds);
// Block for a specified amount of time
if (timeout != -1)
select(local->fd + 1, &fds, NULL, NULL, &time);
else
select(local->fd + 1, &fds, NULL, NULL, NULL);
if (FD_ISSET(local->fd, &fds)) {
size = recvfrom(local->fd, data, len, 0,
(struct sockaddr *)&(remote->addr), &n);
if (size < 0) {
perror("Error receiving data!\n");
return -EIN_RECV;
}
}
else
return -EIN_TIME;
break;
}
}
return size;
}
// inet_send (TCP / UDP)
//
// Sends `len' bytes of `data' from `local' to `remote'.
//
// Returns one of:
// Number of bytes sent Success.
// -EIN_SEND Error sending data.
int
inet_send(inet_host_t *local,
inet_host_t *remote,
void *data,
int len)
{
int size;
// Depending on the protocol, send data
switch (local->protocol) {
case IN_PROT_TCP:
size = send(remote->fd, data, len, 0);
if (size < 0) {
perror("Error sending data!\n");
return -EIN_SEND;
}
break;
case IN_PROT_UDP: {
size = sendto(local->fd, data, len, 0,
(struct sockaddr *)&(remote->addr), sizeof(remote->addr));
if (size < 0) {
perror("Error sending data!\n");
return -EIN_SEND;
}
break;
}
}
return size;
}
// inet_close (TCP / UDP)
//
// Closes an inet connection.
//
// Returns one of:
// 0 Success.
// -1 Invalid `host' pointer.
int
inet_close(inet_host_t *host)
{
// Sanity check
if (!host) return -1;
// Close the file descriptor
if (host->fd) close(host->fd);
return 0;
}
// inet_lookup
//
// Looks up information by resolving a DNS name and returns a pointer to the
// resolved IP address.
//
// Returns one of:
// 0 Unable to resolve name.
// Pointer to resolved IP address Must be freed by caller.
char *
inet_lookup(const char *name)
{
char *hostname;
char *temp;
struct hostent *ent;
// If unable to get host by name, quit with lookup error
if (!(ent = gethostbyname(name))) {
herror("gethostbyname");
return 0;
}
// Copy the looked up name
temp = inet_ntoa(*((struct in_addr *)ent->h_addr));
hostname = malloc(sizeof(char) * (strlen(temp) + 1));
strcpy(hostname, temp);
return hostname;
}