/*- 
 * 
 * New BSD License 2006
 *
 * Copyright (c) 2006, Jorgen Lundman
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1 Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 * 2 Redistributions in binary form must reproduce the above
 *   copyright notice, this list of conditions and the following
 *   disclaimer in the documentation and/or other materials provided
 *   with the distribution.  
 * 3 Neither the name of the stuff nor the names of its contributors 
 *   may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

// $Id: udp.c,v 1.13 2006/06/30 01:22:58 lundman Exp $
// UDP functionality
// Jorgen Lundman April 22nd, 2003.

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <errno.h>

#include "connections.h"
#include "sockets.h"
#include "lion.h"



#ifndef WIN32
#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#endif



//
// Create a new udp socket...
// If port is set (!0) we attempt to open that port, otherwise, it
// will pick first available. "iface" is optional bind ip.
// flags can be FULFILL.
//
connection_t *lion_udp_new(int *port, unsigned long iface,
						   int lion_flags, void *user_data)
{
	connection_t *newd;

	newd = connections_new();

	newd->type = LION_TYPE_UDP;
	newd->user_data = user_data;

	if (lion_flags & LION_FLAG_TRACE)
		lion_enable_trace(newd);

	if (newd->trace)
		fprintf(trace_file, "%p: lion_udp_new\n", newd);

	newd->socket = sockets_udp(port, iface);

#ifdef DEBUG
	printf("[udp] new opened %d port %d\n", newd->socket, *port);
#endif

	if (newd->socket == -1) {

		newd->status = ST_PENDING;
		newd->error_code = errno;

		if (lion_flags & LION_FLAG_FULFILL) {
			
			return newd;

		}

		_lion_userinput( newd, newd->user_data, LION_CONNECTION_LOST, 
						 errno, strerror( errno ) );
		
		connections_free(newd);
		
		return NULL;

	}



	newd->status = ST_PENDING;

	return newd;

}




//
// "bind" a host and port to a UDP connection. Note that this isn't a permanent
// bind if this port also receives "anonymous" incoming packets ie if 
// user_data = NULL. (Then the host/port is changed to that. And it returns
// NULL.
//
// If this function is called with a new user_data, it will allocate a new
// connection_t to deal ONLY with data to/from the host&port pair. No 
// anonymous/unregistered data will be received.
//
connection_t *lion_udp_bind(connection_t *node, unsigned long host, int port,
							void *user_data)
{
	connection_t *newd;
	
	if (node->trace)
		fprintf(trace_file, "%p: lion_udp_bind(%p)\n", node, user_data);

	if (!user_data) {

#ifdef DEBUG
		printf("[udp] bound for %08lX:%d\n", host, port);
#endif
		
		node->host = ntohl(host);
		node->port = ntohs(port);

		return NULL;

	}


	// TODO - create new node, set up to ONLY receive data from it
	newd = connections_new();

	connections_dupe( newd, node );

	// Assign unique user_data
	newd->user_data = user_data;

	// bind it. We could call ourselves with a NULL user_data, but how tedious
#ifdef DEBUG
	printf("[udp] dupe-bound for %08lX:%d\n", host, port);
#endif

	newd->host = ntohl(host);
	newd->port = ntohs(port);

	newd->udp_flags |= LION_UDP_BOUND;

	return newd;

}




//
// Alternative function to lion_udp_bind(). Works exactly the same
// but takes a handle instead.
//
connection_t *lion_udp_bind_handle(connection_t *node, connection_t *remote,
							void *user_data)
{

	if (!node || !remote)
		return NULL;


	return lion_udp_bind(node, ntohl(remote->host), 
						 (int)ntohs((short int)remote->port), user_data);

}












int udp_find_others_sub(connection_t *handle, void *arg1, void *arg2)
{
	connection_t *us = (connection_t *)arg1;

	if (!us) return 0; // stop, invalid situation

	if (handle == us) return 1; // keep going.

	// Next one technically is not required since fd are unique.
	if (handle->type != LION_TYPE_UDP) return 1; // keep going

#ifdef DEBUG_VERBOSE
	printf("[udp] comparing %d == %d\n", handle->socket, us->socket);
#endif

	if (handle->socket == us->socket) return 0; // there is another!

	return 1; // keep looking
}





//
// Check if there are other UDP nodes, with the same socket fd.
//
connection_t *udp_find_others(connection_t *us)
{
	//	printf("[udp] no others?\n");
	return connections_find(udp_find_others_sub, (void *) us, NULL);

}




int udp_input_sub(connection_t *handle, void *arg1, void *arg2)
{
	connection_t *data = (connection_t *) arg1;
	connection_t **anonymous = (connection_t **) arg2;

	if (!data) return 0; // Just a failure, just quit.

	if (handle->type != LION_TYPE_UDP) return 1; // Not UDP, keep going

	if (data->socket != handle->socket) return 1; // Not same socket...


	if (handle->udp_flags & LION_UDP_BOUND) {

#ifdef DEBUG_VERBOSE
		printf(" [udp] bound testing %08lX:%d\n",
			   handle->host, ntohs(handle->port));
#endif

		if (data->udp_host == handle->host) {  // same host
			
			if (data->udp_port == handle->port) { // same port
				
#ifdef DEBUG_VERBOSE
				printf("[udp] input_sub matched..\n");
#endif
				return 0; // Found a perfect match!
				
			}
		}

	} else { // unbound is an anonymous port

		// Unbound udp, if anonymous is passed along, set it
		if (anonymous)
			*anonymous = handle;

	}

	return 1;

}


// 
// We have received input from udp, but we need to find with udp packet is
// the one we should deliver this for. Basically, look up up the host name
// in our "cache", and match if there is one. If not, just deliver to
// the node that is not bound, (0.0.0.0) and input is only received on
// a node that is unbound.
//
void udp_input(connection_t *input, int status, int size, char *buff)
{
	connection_t *anonymous = NULL;
	connection_t *node;
	// See if it is bound..

#ifdef DEBUG_VERBOSE
	printf(" [udp] input from %08lX:%d\n", input->udp_host, 
		   ntohs(input->udp_port));
#endif

	// No? Just deliver as anonymous

	// No hash-cache yet, just look it up
	node = connections_find(udp_input_sub, (void *)input, (void *)&anonymous);


#ifdef DEBUG_VERBOSE
	printf(" [udp] input node=%08lX anonymous=%08lX\n", node, anonymous);
#endif

	// If we got a node we have a perfect match, send it it...

	if (node) {

		_lion_userinput( node, node->user_data, 
						 status, size, buff);


		return;
	}


	// No match, then if anonymous is set, use it
	if (anonymous) {

		// Assigned over where the data come from, since it is anonymous
		anonymous->host = input->udp_host;
		anonymous->port = input->udp_port;
		
		_lion_userinput( anonymous, anonymous->user_data, 
						 status, size, buff);

		return;
	}
	
	// We failed to match, shouldn't happen.
#ifdef DEBUG
	printf("[udp] input: no anonymous node to handle data from %08lX:%d -> %d bytes\n", 
		   input->udp_host, input->udp_port, size);
#endif
	
}




syntax highlighted by Code2HTML, v. 0.9.1