/** @file udp.c * @date April 24, 2009 */ #include <arpa/inet.h> #include <netdb.h> #include <sys/time.h> #include <unistd.h> #include "common.h" #include "config.h" #include "udp.h" #ifdef DEBUG /** A macro to call udp_dump only in the DEBUG configuration. */ #define UDP_DUMP(_TEXT, _PEER, _BUFFER, _SIZE) \ udp_dump(_TEXT, _PEER, _BUFFER, _SIZE) static void udp_dump( const char * text, const peer_t * peer, const void * buffer, size_t size); #else /* DEBUG */ /** This macro does nothing in the RELEASE configuration. */ #define UDP_DUMP(_TEXT, _PEER, _BUFFER, _SIZE) #endif /* DEBUG */ /** The definition for a socket. */ typedef int SOCKET; /** The time the last packet was sent. */ static struct timeval g_sendtime; /** The socket managed by the module. */ static SOCKET g_socket; static int udp_resolve_helper( const struct addrinfo * info); static void udp_pace(size_t bytes); /* ------------------------------------------------------------------------- */ int udp_recv( void * buffer, size_t size, size_t * actual, peer_t * peer) { int result; socklen_t sizeof_sin; struct sockaddr_in sin; assert(NULL != buffer); assert(NULL != actual); assert(NULL != peer); assert(size > 0); assert(g_socket != 0); /* Receive the datagram. */ sizeof_sin = sizeof(sin); result = recvfrom( g_socket, (char *)buffer, (int)size, 0, (struct sockaddr *)&sin, &sizeof_sin); /* Check for errors. */ RETURN_IF_FALSE(result != -1); RETURN_IF_FALSE(result != 0); RETURN_IF_FALSE(result > 0); RETURN_IF_FALSE((unsigned int)sizeof_sin == sizeof(sin)); /* Store the result. */ *actual = (size_t)result; peer->port = ntohs(sin.sin_port); peer->address = sin.sin_addr.s_addr; /* Display the buffer in the DEBUG build. */ UDP_DUMP("From", peer, buffer, *actual); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ int udp_resolve(const char * name, const char * port, peer_t * peer) { struct addrinfo hints; struct addrinfo * info; const struct sockaddr_in * sin; assert(NULL != name); assert(NULL != port); assert(NULL != peer); /* Set hints for getaddrinfo. */ memset(&hints, 0, sizeof(hints)); hints.ai_family = PF_INET; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = (int)IPPROTO_UDP; /* Get the host information. */ RETURN_IF_FALSE(0 == getaddrinfo(name, port, &hints, &info)); RETURN_IF_FALSE(NULL != info); /* Check each field using the helper function. */ if (EXIT_SUCCESS != udp_resolve_helper(info)) { freeaddrinfo(info); RETURN_FAILURE(); } /* Store the result into the peer structure. */ sin = (struct sockaddr_in *)info->ai_addr; peer->address = sin->sin_addr.s_addr; peer->port = ntohs(sin->sin_port); /* Free the block allocated by getaddrinfo, and return successfully. */ freeaddrinfo(info); return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ int udp_send(const peer_t * peer, const void * buffer, size_t size) { struct sockaddr_in sin; const char * buf; int len; assert(NULL != peer); assert(NULL != buffer); assert(size > 0); assert(g_socket != 0); /* Display information about the buffer. */ UDP_DUMP("To", peer, buffer, size); /* Do not exceed MAX_KBPS. */ udp_pace(size); /* Prepare the structure for the remote address. */ memset(&sin, 0, sizeof(sin)); sin.sin_addr.s_addr = peer->address; sin.sin_family = AF_INET; sin.sin_port = htons(peer->port); /* Prepare some pointers and values for the loop. */ buf = (const char *)buffer; len = (int)size; /* Loop until all the data is sent. */ while (len > 0) { int result; /* Send the buffer. */ result = sendto( g_socket, buf, len, 0, (const struct sockaddr *)&sin, sizeof(sin)); /* Check for errors or incomplete sends. */ RETURN_IF_FALSE(result > 0); assert(result <= len); len -= result; buf += result; } /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ int udp_start_client(void) { assert(g_socket == 0); /* Create the socket and check for errors. */ g_socket = socket(AF_INET, SOCK_DGRAM, (int)IPPROTO_UDP); RETURN_IF_FALSE(g_socket != 0); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ int udp_start_server(u_short port) { struct sockaddr_in sin; assert(g_socket == 0); /* Create the unbound socket. */ RETURN_IF_FAILURE(udp_start_client()); /* Bind the socket to the port. */ memset(&sin, 0, sizeof(sin)); sin.sin_family = AF_INET; sin.sin_port = htons(port); sin.sin_addr.s_addr = htonl(INADDR_ANY); if (0 != bind(g_socket, (struct sockaddr *)&sin, sizeof(sin))) { /* Close the socket if there was a problem. */ (void)close(g_socket); g_socket = 0; RETURN_FAILURE(); } /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ int udp_stop(void) { assert(g_socket != 0); /* Close the socket. */ RETURN_IF_FALSE(0 == close(g_socket)); g_socket = 0; /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ int udp_wait(size_t ms, int * timeout) { int result; fd_set fds; struct timeval tv; assert(g_socket != 0); *timeout = 0; /* Prepare a file descriptor set for the select function. */ FD_ZERO(&fds); FD_SET(g_socket, &fds); /* Prepare the timeout. */ memset(&tv, 0, sizeof(tv)); tv.tv_usec = (long)ms * 1000L; tv.tv_sec = tv.tv_usec / 1000000L; tv.tv_usec %= 1000000L; /* Wait for a datagram or a timeout. */ result = select(1 + g_socket, &fds, NULL, NULL, &tv); if (0 == result) { *timeout = 1; return EXIT_SUCCESS; } /* Check for an error. */ RETURN_IF_FALSE(result > 0); /* Return successfully. */ return EXIT_SUCCESS; } #ifdef DEBUG /* ------------------------------------------------------------------------- */ /** Displays information about a buffer received or sent. * * @param text The text to display To or From. * @param peer The peer address. * @param buffer The buffer of data. * @param size The number of bytes in the buffer. */ static void udp_dump( const char * text, const peer_t * peer, const void * buffer, size_t size) { struct in_addr addr; size_t count; const u_char * ptr; assert(NULL != buffer); /* Display the From or To address. */ ptr = (const u_char *)buffer; addr.s_addr = peer->address; printf("%s: %s:%hu\n{", text, inet_ntoa(addr), peer->port); /* Loop over the bytes of the buffer. */ count = 0; while (size-- > 0) { /* Print the byte in hex. */ printf(" %02x", (unsigned int)*ptr); ptr++; /* Don't bother to print long messages fully. */ if (++count == 24) { printf(" ..."); break; } } /* Clean up. */ printf(" }\n\n"); } #endif /* DEBUG */ /* ------------------------------------------------------------------------- */ /** A helper function for udp_resolve. This function simply verifies * the address info returned from getaddrinfo is valid. * * @param info The address information. * * @return EXIT_SUCCESS or EXIT_FAILURE. */ static int udp_resolve_helper(const struct addrinfo * info) { assert(NULL != info); /* Verify each field of the structure. */ RETURN_IF_FALSE(info->ai_family == PF_INET); RETURN_IF_FALSE(info->ai_socktype == SOCK_DGRAM); RETURN_IF_FALSE(info->ai_addrlen == sizeof(struct sockaddr_in)); /* Return successfully. */ return EXIT_SUCCESS; } /* ------------------------------------------------------------------------- */ /** A function that delays so the transfer rate never exceeds MAX_KBPS. * * @param bytes The number of bytes about to be sent. */ static void udp_pace(size_t bytes) { struct timeval now; struct timeval ready; struct timeval timeout; /* Get the current time. */ gettimeofday(&now, NULL); /* Compute the time that is okay to send. */ ready = g_sendtime; ready.tv_usec += (((suseconds_t)bytes * 8) * 1000) / UDP_MAX_KBPS; /* Compute the time to wait before sending. */ timeout.tv_sec = ready.tv_sec - now.tv_sec; timeout.tv_usec = ready.tv_usec - now.tv_usec; while (timeout.tv_usec < 0) { timeout.tv_sec--; timeout.tv_usec += 1000000; } /* Sleep if necessary. */ if (timeout.tv_sec >= 0) select(0, NULL, NULL, NULL, &timeout); /* Record the current time for the next pace calculation. */ g_sendtime = now; }