udp.c

/*
 * 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);
    }
}
Valid HTML 4.01 Valid CSS