305 lines
7.5 KiB
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;
|
|
}
|