/*
* File Name: udp.c
* Author: Jade Cheng
* Date: March 29, 2009
* Course: ICS 451
* Assignment: Project 2
*/
#include "common.h"
#include "udp.h"
#include "log.h"
/** The value of the socket when it is closed. */
#define UDP_CLOSED_SOCKET -1
/** To allow sockaddr without struct. */
typedef struct sockaddr sockaddr;
/** To allow sockaddr_in without struct. */
typedef struct sockaddr_in sockaddr_in;
/** A union of sockaddr and sockaddr_in. */
typedef union address_u {
sockaddr sa;
sockaddr_in sin;
} address_u;
static void log_buffer(
const char * message,
const peer_t * peer,
const void * data,
size_t size);
/** The listening UDP socket. */
static int g_socket = UDP_CLOSED_SOCKET;
/* ------------------------------------------------------------------------ */
extern int udp_fd(void)
{
assert(g_socket != UDP_CLOSED_SOCKET);
return g_socket;
}
/* ------------------------------------------------------------------------ */
extern int udp_recv(void * buffer, size_t max, size_t * actual, peer_t * from)
{
address_u u;
ssize_t r;
socklen_t len;
assert(buffer != NULL);
assert(actual != NULL);
assert(from != NULL);
assert(max > 0);
/* Make sure the socket is open before receiving data. */
RETURN_IF_FALSE(g_socket != UDP_CLOSED_SOCKET);
/* Receive the buffer of data from the peer. */
memset(&u, 0, sizeof(u));
len = sizeof(sockaddr_in);
r = recvfrom(g_socket, buffer, max, 0, &u.sa, &len);
/* Check everything was successful. */
RETURN_IF_FALSE(r > 0);
RETURN_IF_FALSE(u.sin.sin_family == AF_INET);
RETURN_IF_FALSE(len == sizeof(sockaddr_in));
/* Store the number of bytes received. */
*actual = (size_t)r;
/* Store the port and address (from network to host endian). */
from->port = ntohs(u.sin.sin_port);
from->addr = u.sin.sin_addr.s_addr;
log_buffer("Receiving from", from, buffer, *actual);
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern int udp_send(const peer_t * to, const void * buffer, size_t size)
{
address_u u;
ssize_t r;
assert(to != NULL);
assert(buffer != NULL);
assert(size > 0);
/* Make sure the socket is open before sending. */
RETURN_IF_FALSE(g_socket != UDP_CLOSED_SOCKET);
log_buffer("Sending to", to, buffer, size);
/* Specify the peer's port and address (from host to network endian). */
u.sin.sin_family = AF_INET;
u.sin.sin_port = htons(to->port);
u.sin.sin_addr.s_addr = to->addr;
/* Send the data to the peer. */
r = sendto(g_socket, buffer, size, 0, &u.sa, sizeof(sockaddr_in));
RETURN_IF_FALSE(r == (ssize_t)size);
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern int udp_start(u_short port)
{
address_u u;
/* Make sure the socket is closed. */
RETURN_IF_FALSE(g_socket == UDP_CLOSED_SOCKET);
/* Listen on the specified port from any IP address. */
u.sin.sin_family = AF_INET;
u.sin.sin_port = htons(port);
u.sin.sin_addr.s_addr = INADDR_ANY;
/* Create the socket for listening. */
g_socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
/* Check for errors creating the socket. */
if (g_socket < 0) {
g_socket = UDP_CLOSED_SOCKET;
RETURN_FAILURE();
}
/* Bind to the port and check for errors. */
if (bind(g_socket, &u.sa, sizeof(sockaddr_in)) < 0) {
close(g_socket);
g_socket = UDP_CLOSED_SOCKET;
RETURN_FAILURE();
}
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
extern int udp_stop(void)
{
/* Verify the socket is open before closing. */
RETURN_IF_FALSE(g_socket != UDP_CLOSED_SOCKET);
close(g_socket);
g_socket = UDP_CLOSED_SOCKET;
return EXIT_SUCCESS;
}
/* ------------------------------------------------------------------------ */
static void log_buffer(
const char * message,
const peer_t * peer,
const void * data,
size_t size)
{
FILE * f;
if ((f = log_open()) != NULL) {
fprintf(f, "%s ", message);
fprint_peer(f, peer);
fprintf(f, "\n");
fprint_buffer(f, data, size);
fprintf(f, "\n");
log_close(f);
}
}