/*
 * Copyright (c) 2003 Sun Microsystems, Inc.  All Rights Reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * Redistribution of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * 
 * Redistribution 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.
 * 
 * Neither the name of Sun Microsystems, Inc. or the names of
 * contributors may be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * 
 * This software is provided "AS IS," without a warranty of any kind.
 * ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES,
 * INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED.
 * SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL NOT BE LIABLE
 * FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.  IN NO EVENT WILL
 * SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA,
 * OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR
 * PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF
 * LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
 * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
 */
#define _XOPEN_SOURCE 700
#define _GNU_SOURCE 1
#define _DARWIN_C_SOURCE 1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if defined(HAVE_CONFIG_H)
# include <config.h>
#endif

#if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <unistd.h>
#include <netdb.h>
#endif


#include <ipmitool/ipmi_intf.h>
#include <ipmitool/ipmi.h>
#include <ipmitool/ipmi_sdr.h>
#include <ipmitool/log.h>

#define IPMI_DEFAULT_PAYLOAD_SIZE   25

#ifdef IPMI_INTF_OPEN
extern struct ipmi_intf ipmi_open_intf;
#endif
#ifdef IPMI_INTF_IMB
extern struct ipmi_intf ipmi_imb_intf;
#endif
#ifdef IPMI_INTF_LIPMI
extern struct ipmi_intf ipmi_lipmi_intf;
#endif
#ifdef IPMI_INTF_BMC
extern struct ipmi_intf ipmi_bmc_intf;
#endif
#ifdef IPMI_INTF_LAN
extern struct ipmi_intf ipmi_lan_intf;
#endif
#ifdef IPMI_INTF_LANPLUS
extern struct ipmi_intf ipmi_lanplus_intf;
#endif
#ifdef IPMI_INTF_FREE
extern struct ipmi_intf ipmi_free_intf;
#endif
#ifdef IPMI_INTF_SERIAL
extern struct ipmi_intf ipmi_serial_term_intf;
extern struct ipmi_intf ipmi_serial_bm_intf;
#endif
#ifdef IPMI_INTF_DUMMY
extern struct ipmi_intf ipmi_dummy_intf;
#endif
#ifdef IPMI_INTF_USB
extern struct ipmi_intf ipmi_usb_intf;
#endif

struct ipmi_intf * ipmi_intf_table[] = {
#ifdef IPMI_INTF_OPEN
	&ipmi_open_intf,
#endif
#ifdef IPMI_INTF_IMB
	&ipmi_imb_intf,
#endif
#ifdef IPMI_INTF_LIPMI
	&ipmi_lipmi_intf,
#endif
#ifdef IPMI_INTF_BMC
	&ipmi_bmc_intf,
#endif
#ifdef IPMI_INTF_LAN
	&ipmi_lan_intf,
#endif
#ifdef IPMI_INTF_LANPLUS
	&ipmi_lanplus_intf,
#endif
#ifdef IPMI_INTF_FREE
	&ipmi_free_intf,
#endif
#ifdef IPMI_INTF_SERIAL
	&ipmi_serial_term_intf,
	&ipmi_serial_bm_intf,
#endif
#ifdef IPMI_INTF_DUMMY
	&ipmi_dummy_intf,
#endif
#ifdef IPMI_INTF_USB
	&ipmi_usb_intf,
#endif
	NULL
};

/* ipmi_intf_print  -  Print list of interfaces
 *
 * no meaningful return code
 */
void ipmi_intf_print(struct ipmi_intf_support * intflist)
{
	struct ipmi_intf ** intf;
	struct ipmi_intf_support * sup;
	int def = 1;
	int found;

	lprintf(LOG_NOTICE, "Interfaces:");

	for (intf = ipmi_intf_table; intf && *intf; intf++) {

		if (intflist != NULL) {
			found = 0;
			for (sup=intflist; sup->name != NULL; sup++) {
				if (strncmp(sup->name, (*intf)->name, strlen(sup->name)) == 0 &&
				    strncmp(sup->name, (*intf)->name, strlen((*intf)->name)) == 0 &&
				    sup->supported == 1)
					found = 1;
			}
			if (found == 0)
				continue;
		}

		lprintf(LOG_NOTICE, "\t%-12s  %s %s",
			(*intf)->name, (*intf)->desc,
			def ? "[default]" : "");
		def = 0;
	}
	lprintf(LOG_NOTICE, "");
}

/* ipmi_intf_load  -  Load an interface from the interface table above
 *                    If no interface name is given return first entry
 *
 * @name:	interface name to try and load
 *
 * returns pointer to inteface structure if found
 * returns NULL on error
 */
struct ipmi_intf * ipmi_intf_load(char * name)
{
	struct ipmi_intf ** intf;
	struct ipmi_intf * i;

	if (name == NULL) {
		i = ipmi_intf_table[0];
		if (i->setup != NULL && (i->setup(i) < 0)) {
			lprintf(LOG_ERR, "Unable to setup "
				"interface %s", name);
			return NULL;
		}
		return i;
	}

	for (intf = ipmi_intf_table;
	     ((intf != NULL) && (*intf != NULL));
	     intf++) {
		i = *intf;
		if (strncmp(name, i->name, strlen(name)) == 0) {
			if (i->setup != NULL && (i->setup(i) < 0)) {
				lprintf(LOG_ERR, "Unable to setup "
					"interface %s", name);
				return NULL;
			}
			return i;
		}
	}

	return NULL;
}

void
ipmi_intf_session_set_hostname(struct ipmi_intf * intf, char * hostname)
{
	if (intf->ssn_params.hostname != NULL) {
		free(intf->ssn_params.hostname);
		intf->ssn_params.hostname = NULL;
	}
	if (hostname == NULL) {
		return;
	}
	intf->ssn_params.hostname = strdup(hostname);
}

void
ipmi_intf_session_set_username(struct ipmi_intf * intf, char * username)
{
	memset(intf->ssn_params.username, 0, 17);

	if (username == NULL)
		return;

	memcpy(intf->ssn_params.username, username, __min(strlen(username), 16));
}

void
ipmi_intf_session_set_password(struct ipmi_intf * intf, char * password)
{
	memset(intf->ssn_params.authcode_set, 0, IPMI_AUTHCODE_BUFFER_SIZE);

	if (password == NULL) {
		intf->ssn_params.password = 0;
		return;
	}

	intf->ssn_params.password = 1;
	memcpy(intf->ssn_params.authcode_set, password,
	       __min(strlen(password), IPMI_AUTHCODE_BUFFER_SIZE));
}

void
ipmi_intf_session_set_privlvl(struct ipmi_intf * intf, uint8_t level)
{
	intf->ssn_params.privlvl = level;
}

void
ipmi_intf_session_set_lookupbit(struct ipmi_intf * intf, uint8_t lookupbit)
{
	intf->ssn_params.lookupbit = lookupbit;
}

void
ipmi_intf_session_set_cipher_suite_id(struct ipmi_intf * intf, uint8_t cipher_suite_id)
{
	intf->ssn_params.cipher_suite_id = cipher_suite_id;
}

void
ipmi_intf_session_set_sol_escape_char(struct ipmi_intf * intf, char sol_escape_char)
{
	intf->ssn_params.sol_escape_char = sol_escape_char;
}

void
ipmi_intf_session_set_kgkey(struct ipmi_intf *intf, const uint8_t *kgkey)
{
	memcpy(intf->ssn_params.kg, kgkey, IPMI_KG_BUFFER_SIZE);
}

void
ipmi_intf_session_set_port(struct ipmi_intf * intf, int port)
{
	intf->ssn_params.port = port;
}

void
ipmi_intf_session_set_authtype(struct ipmi_intf * intf, uint8_t authtype)
{
	/* clear password field if authtype NONE specified */
	if (authtype == IPMI_SESSION_AUTHTYPE_NONE) {
		memset(intf->ssn_params.authcode_set, 0, IPMI_AUTHCODE_BUFFER_SIZE);
		intf->ssn_params.password = 0;
	}

	intf->ssn_params.authtype_set = authtype;
}

void
ipmi_intf_session_set_timeout(struct ipmi_intf * intf, uint32_t timeout)
{
	intf->ssn_params.timeout = timeout;
}

void
ipmi_intf_session_set_retry(struct ipmi_intf * intf, int retry)
{
	intf->ssn_params.retry = retry;
}

void
ipmi_intf_session_cleanup(struct ipmi_intf *intf)
{
	if (intf->session == NULL) {
		return;
	}

	free(intf->session);
	intf->session = NULL;
}

void
ipmi_cleanup(struct ipmi_intf * intf)
{
	ipmi_sdr_list_empty(intf);
	ipmi_intf_session_set_hostname(intf, NULL);
}

#if defined(IPMI_INTF_LAN) || defined (IPMI_INTF_LANPLUS)
//jimbo modify
int
ipmi_intf_socket_connect(struct ipmi_intf * intf)
{
	//printf("---> ipmi_intf_socket_connect->hostname %s\n", intf->ssn_params.hostname);
	struct ipmi_session_params *params;
	struct sockaddr_in serveraddr;

	if (!intf) {
		return -1;
	}

	params = &intf->ssn_params;

	if (params->hostname == NULL || strlen((const char *)params->hostname) == 0) {
		lprintf(LOG_ERR, "No hostname specified!");
		return -1;
	}

	//本版本ipmitool只支持127.0.0.1这一个IP地址。
	if(strcmp(params->hostname, "127.0.0.1") != 0)
	{
		lprintf(LOG_ERR, "This ipmitool only support 127.0.0.1 ipaddress.\n");
		return -1;
	}

	intf->fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
	if (intf->fd == -1) {
		return -1;
	}

		serveraddr.sin_family = AF_INET;
		inet_aton(params->hostname, &serveraddr.sin_addr);
		serveraddr.sin_port = htons(623);
		//printf("---> %#x\n", serveraddr.sin_addr.s_addr);
		if (connect(intf->fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -1) {
			lprintf(LOG_ERR, "Connect failed\n");
			return -1;
		}

	return ((intf->fd != -1) ? 0 : -1);
}
#endif

uint16_t
ipmi_intf_get_max_request_data_size(struct ipmi_intf * intf)
{
	int16_t size;

	size = intf->max_request_data_size;

	/* check if request size is not specified */
	if (!size) {
		/*
		 * The IPMB standard overall message length for ‘non -bridging’
		 * messages is specified as 32 bytes, maximum, including slave
		 * address. This sets the upper limit for typical IPMI messages.
		 * With the exception of messages used for bridging messages to
		 * other busses or interfaces (e.g. Master Write-Read and Send Message)
		 * IPMI messages should be designed to fit within this 32-byte maximum.
		 * In order to support bridging, the Master Write -Read and Send Message
		 * commands are allowed to exceed the 32-byte maximum transaction on IPMB
		 */

		size = IPMI_DEFAULT_PAYLOAD_SIZE;

		/* check if message is forwarded */
		if (intf->target_addr && intf->target_addr != intf->my_addr) {
			/* add Send Message request size */
			size += 8;
		}
	}

	/* check if message is forwarded */
	if (intf->target_addr && intf->target_addr != intf->my_addr) {
		/* subtract send message request size */
		size -= 8;

		/*
		 * Check that forwarded request size is not greater
		 * than the default payload size.
		 */
		if (size > IPMI_DEFAULT_PAYLOAD_SIZE) {
			size = IPMI_DEFAULT_PAYLOAD_SIZE;
		}

		/* check for double bridging */
		if (intf->transit_addr && intf->transit_addr != intf->target_addr) {
			/* subtract inner send message request size */
			size -= 8;
		}
	}

	/* check for underflow */
	if (size < 0) {
		return 0;
	}

	return size;
}

uint16_t
ipmi_intf_get_max_response_data_size(struct ipmi_intf * intf)
{
	int16_t size;

	size = intf->max_response_data_size;

	/* check if response size is not specified */
	if (!size) {
		/*
		 * The IPMB standard overall message length for ‘non -bridging’
		 * messages is specified as 32 bytes, maximum, including slave
		 * address. This sets the upper limit for typical IPMI messages.
		 * With the exception of messages used for bridging messages to
		 * other busses or interfaces (e.g. Master Write-Read and Send Message)
		 * IPMI messages should be designed to fit within this 32-byte maximum.
		 * In order to support bridging, the Master Write -Read and Send Message
		 * commands are allowed to exceed the 32-byte maximum transaction on IPMB
		 */

		size = IPMI_DEFAULT_PAYLOAD_SIZE; /* response length with subtracted header and checksum byte */

		/* check if message is forwarded */
		if (intf->target_addr && intf->target_addr != intf->my_addr) {
			/* add Send Message header size */
			size += 7;
		}
	}

	/* check if message is forwarded */
	if (intf->target_addr && intf->target_addr != intf->my_addr) {
		/*
		 * Some IPMI controllers like PICMG AMC Carriers embed responses
		 * to the forwarded messages into the Send Message response.
		 * In order to be sure that the response is not truncated,
		 * subtract the internal message header size.
		 */
		size -= 8;

		/*
		 * Check that forwarded response is not greater
		 * than the default payload size.
		 */
		if (size > IPMI_DEFAULT_PAYLOAD_SIZE) {
			size = IPMI_DEFAULT_PAYLOAD_SIZE;
		}

		/* check for double bridging */
		if (intf->transit_addr && intf->transit_addr != intf->target_addr) {
			/* subtract inner send message header size */
			size -= 8;
		}
	}

	/* check for underflow */
	if (size < 0) {
		return 0;
	}

	return size;
}

void
ipmi_intf_set_max_request_data_size(struct ipmi_intf * intf, uint16_t size)
{
	if (size < IPMI_DEFAULT_PAYLOAD_SIZE) {
		lprintf(LOG_ERR, "Request size is too small (%d), leave default size",
				size);
		return;
	}

	if (intf->set_max_request_data_size) {
		intf->set_max_request_data_size(intf, size);
	} else {
		intf->max_request_data_size = size;
	}
}

void
ipmi_intf_set_max_response_data_size(struct ipmi_intf * intf, uint16_t size)
{
	if (size < IPMI_DEFAULT_PAYLOAD_SIZE - 1) {
		lprintf(LOG_ERR, "Response size is too small (%d), leave default size",
				size);
		return;
	}

	if (intf->set_max_response_data_size) {
		intf->set_max_response_data_size(intf, size);
	} else {
		intf->max_response_data_size = size;
	}
}
