/******************************************************************************
 * Copyright (c) 2004, 2008 IBM Corporation
 * All rights reserved.
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Contributors:
 *     IBM Corporation - initial implementation
 *****************************************************************************/

/************************ DEFINITIONS & DECLARATIONS *********************/

#include <udp.h>
#include <sys/socket.h>
#include <dhcp.h>
#include <dhcpv6.h>
#include <dns.h>
#include <tftp.h>


/****************************** IMPLEMENTATION ***************************/


/**
 * NET: Handles UDP-packets according to Receive-handle diagram.
 *
 * @param  udp_packet UDP-packet to be handled
 * @param  packetsize Length of the packet
 * @return            ZERO - packet handled successfully;
 *                    NON ZERO - packet was not handled (e.g. bad format)
 * @see               receive_ether
 * @see               udphdr
 */
int8_t handle_udp(int fd, uint8_t * udp_packet, uint32_t packetsize)
{
	struct udphdr * udph = (struct udphdr *) udp_packet;

	if (packetsize < sizeof(struct udphdr))
		return -1; // packet is too small

	switch (htons(udph -> uh_dport)) {
	case UDPPORT_BOOTPC:
		if (udph -> uh_sport == htons(UDPPORT_BOOTPS))
			return handle_dhcp(fd, udp_packet + sizeof(struct udphdr),
			                    packetsize - sizeof(struct udphdr));
		else
			return -1;
	case UDPPORT_DNSC:
		if (udph -> uh_sport == htons(UDPPORT_DNSS))
			return handle_dns(udp_packet + sizeof(struct udphdr),
			                  packetsize - sizeof(struct udphdr));
		else
			return -1;
	case UDPPORT_DHCPV6C:
		return handle_dhcpv6(udp_packet+sizeof(struct udphdr),
		                     packetsize - sizeof(struct udphdr));
	case UDPPORT_TFTPC:
		return handle_tftp(fd, udp_packet, packetsize);
	default:
		return -1;
	}
}

/**
 * NET: This function handles situation when "Destination unreachable"
 *      ICMP-error occurs during sending UDP-packet.
 *
 * @param  err_code   Error Code (e.g. "Host unreachable")
 * @param  packet     original UDP-packet
 * @param  packetsize length of the packet
 * @see               handle_icmp
 */
void handle_udp_dun(uint8_t * udp_packet, uint32_t packetsize, uint8_t err_code)
{
	struct udphdr * udph = (struct udphdr *) udp_packet;

	if (packetsize < sizeof(struct udphdr))
		return; // packet is too small

	switch (htons(udph -> uh_sport)) {
	case UDPPORT_TFTPC:
		handle_tftp_dun(err_code);
		break;
	}
}

/**
 * NET: Creates UDP-packet. Places UDP-header in a packet and fills it
 *      with corresponding information.
 *      <p>
 *      Use this function with similar functions for other network layers
 *      (fill_ethhdr, fill_iphdr, fill_dnshdr, fill_btphdr).
 *
 * @param  packet      Points to the place where UDP-header must be placed.
 * @param  packetsize  Size of the packet in bytes incl. this hdr and data.
 * @param  src_port    UDP source port
 * @param  dest_port   UDP destination port
 * @see                udphdr
 * @see                fill_ethhdr
 * @see                fill_iphdr
 * @see                fill_dnshdr
 * @see                fill_btphdr
 */
void fill_udphdr(uint8_t * packet, uint16_t packetsize,
		 uint16_t src_port, uint16_t dest_port)
{
	struct udphdr * udph = (struct udphdr *) packet;

	udph -> uh_sport = htons(src_port);
	udph -> uh_dport = htons(dest_port);
	udph -> uh_ulen = htons(packetsize);
	udph -> uh_sum = htons(0);
}
