/*
 * net.c
 *
 * Copyright 2004, Minoru Murashima. All rights reserved.
 * Distributed under the terms of the BSD License.
 *
 * ネットワーク共通ルーチン。
 */


#include<types.h>
#include<errno.h>
#include<lib.h>
#include<mm.h>
#include<proc.h>
#include<lock.h>
#include<fs.h>
#include<time.h>
#include<console.h>
#include<net/arp.h>
#include<net/icmp.h>
#include<net/udp.h>
#include<net/tcp.h>
#include<net/net.h>
#include<net/ether.h>
#include<debug.h>


/************************************************************************************
 *
 * ソケット操作
 *
 ************************************************************************************/


enum{
	IP_PROTNUM_MAX=17,		/* IP 上位プロトコルナンバー最大値。 */
	SOCKET_MAX=10,			/* プロセスの持つソケットの最大数。 */
};


/*================================== プライベート ========================================*/


static SOCKET_INFO *sockInfo[IP_PROTNUM_MAX+1];


/*================================== パブリック ========================================*/


static FS *fs;


/*================================== グローバル ========================================*/


/*
 * Get  ephemeral port number.
 * return : ephemeral port number
 */
ushort getEphemeralPort()
{
	enum{BEGIN_EPHM_PORT=1024};		/* エフェメラルポートの最初の番号。 */

	static int gate=0;
	static int volatile port=BEGIN_EPHM_PORT;
	int pt;


	enter_spinlock(&gate);
	{
		pt=port++;
	}
	exit_spinlock(&gate);

	return pt;
}


/*
 * Close socket.
 * parameters : socket address
 */
void closeSocket(SOCKET *sock)
{
	/* ソケットの開放。 */
	switch (sock->sockType)
	{
		case SOCK_STREAM:
			if (releaseSockFromConect(sock) != 0){
				return;
			}
			break;
		case SOCK_DGRAM:
			releaseSockUdp(sock);
			break;
		case SOCK_RAW:
			releaseSockIcmp(sock);
			break;
		default:
			ASSERT(0);
	}
	
	kfree(sock);
}


/*
 * Make socket
 * parameters : sockest type,protocol,socket address
 * return : 0 or error number
 */
int makeSocket(const int type, const int protocol, SOCKET **o_sock)
{
	SOCKET *sock;
	
	if ((sock = kmalloc(sizeof(SOCKET))) == NULL){
		return -ENOMEM;
	}
	memset(sock, 0, sizeof(*sock));
	sock->fs = fs;
	sock->sc = sock;
	sock->sockType = type;
	sock->sinf = sockInfo[protocol];
	sock->next = sock->prev = sock;

	*o_sock = sock;

	return 0;
}


/*
 * Regist socket information.
 * parameters : SOCKET_INFO address,protocol number
 * return : 0
 */
int registSocket(SOCKET_INFO *sinf, int prot)
{
	sockInfo[prot] = sinf;

	return 0;
}


/************************************************************************************
 *
 * Systemcall interface
 *
 ************************************************************************************/


int sys_socket(int domain, int type, int protocol)
{
	int prot;
	int fd;
	int rest;
	SOCKET *sock;

	if (isFileOpenMax() == TRUE){
		return -EMFILE;
	}

	switch(domain)
	{
		case AF_INET:
			switch(type)
			{
				case SOCK_DGRAM:
					if((protocol == 0)||(protocol == IPPROTO_UDP))
						prot = IPPROTO_UDP;
					else
						return -EPROTONOSUPPORT;
					break;
				case SOCK_STREAM:
					if((protocol == 0)||(protocol == IPPROTO_TCP))
						prot = IPPROTO_TCP;
					else 
						return -EPROTONOSUPPORT;
					break;
				case SOCK_RAW:
					if((protocol == 0)||(protocol == IPPROTO_ICMP))
						prot = IPPROTO_ICMP;
					else 
						return -EPROTONOSUPPORT;
					break;
				default:
					return -EPROTONOSUPPORT;
			}
			break;
		default:
			return -EPROTONOSUPPORT;
	}

	rest = makeSocket(type, prot, &sock);
	if (rest < 0)
		return rest;
	initFd((VENTRY*)&sock->fs, FD_FORM_NETSOKET, O_RDWR, (F_DSC*)sock);
	fd = setFd((F_DSC*)sock, 0);
	if(fd < 0)
	{
		kfree(sock);
		return fd;
	}

	/* プロセスに登録。 */
	return fd;
}


int sys_send(int sd, void *msg, size_t len, int flags, struct sockaddr *to)
{
	SOCKET *sock;


	if ((sock = (SOCKET*)getFd(sd)) == NULL)
		return -EBADF;
	if (sock->fd.form != FD_FORM_NETSOKET)
		return -ENOTSOCK;
	if (to != NULL)
	{
		if (checkMem(to,sizeof(struct sockaddr)) == ERR)
			return -EFAULT;
	}
	if (sock->srcport == 0)
		sock->srcport = swapWord(getEphemeralPort());

	return ((SOCKET_INFO*)sock->sinf)->send(sock, msg, len, flags, to);
}


int sys_sendmsg(int s, const struct msghdr *msg, int flags)
{
	if (checkMem((void*)msg,sizeof(struct msghdr)) == ERR)
		return -EFAULT;

	return -EOPNOTSUPP;
}


int sys_recv(int sd,void *buf,size_t len,int flags,struct sockaddr *from,int *fromlen)
{
	SOCKET *sock;
	struct iovec iov = {
		.iov_base = buf, 
		.iov_len = len,
	};
	struct msghdr msg = {
		.msg_iov = &iov,
		.msg_iovlen = 1,
		.msg_control = NULL,
		.msg_controllen = 0,
	};
	int rest;

	if ((sock = (SOCKET*)getFd(sd)) == NULL)
		return -EBADF;
	if (sock->fd.form != FD_FORM_NETSOKET)
		return -ENOTSOCK;

	if (checkMem(buf,len) == ERR)
		return -EFAULT;

	if (from != NULL)
	{
		if (checkMem(from,sizeof(struct sockaddr)) == ERR)
			return -EFAULT;
		if (checkMem(fromlen,sizeof(int)) == ERR)
			return -EFAULT;

		/* 受信 */
		rest = ((SOCKET_INFO*)sock->sinf)->recv(sock,&msg);

		/* 送信元のアドレスの代入 */
		if (sizeof(struct sockaddr_in) <= *fromlen)
		{
			((struct sockaddr_in*)from)->sin_family = AF_INET;
			((struct sockaddr_in*)from)->sin_addr.s_addr = sock->dstip;
			((struct sockaddr_in*)from)->sin_port = sock->dstport;
			*fromlen = sizeof(struct sockaddr_in);
		}

		return rest;
	}
	else
		/* 受信 */
		return ((SOCKET_INFO*)sock->sinf)->recv(sock,&msg);
}


int sys_recvmsg(const int sd, struct msghdr *msg, const int flags)
{
	SOCKET *sock;
	int i,rest;
	
	if ((sock = (SOCKET*)getFd(sd)) == NULL)
		return -EBADF;
	if (sock->fd.form != FD_FORM_NETSOKET)
		return -ENOTSOCK;

	/* メモリーチェック */
	for (i = 0; i < msg->msg_iovlen; ++i)
		if (checkMem(msg->msg_iov->iov_base,msg->msg_iov->iov_len) == ERR)
			return -EFAULT;
	if (checkMem(msg->msg_control,msg->msg_controllen) == ERR)
		return -EFAULT;

	/* 受信 */
	rest = ((SOCKET_INFO*)sock->sinf)->recv(sock,msg);
	
	/* 送信元のアドレスを設定する */
	if (msg->msg_name != NULL)
	{
		/* メモリーチェック */
		if (checkMem(msg->msg_name,msg->msg_namelen) == ERR)
			return -EFAULT;

		/* 送信元のアドレスの代入 */
		if (sizeof(struct sockaddr_in) <= msg->msg_namelen)
		{
			struct sockaddr_in *from = (struct sockaddr_in*)msg->msg_name;
			
			from->sin_family = AF_INET;
			from->sin_addr.s_addr = sock->dstip;
		}
	}

	return rest;
}


int sys_connect(int sockfd, const struct sockaddr *serv_addr)
{
	SOCKET *sock;

	sock = (SOCKET*)getFd(sockfd);
	if (sock == NULL){
		return -EBADF;
	}
	if (sock->fd.form != FD_FORM_NETSOKET){
		return -ENOTSOCK;
	}
	if (checkMem((void*)serv_addr,sizeof(struct sockaddr)) == ERR){
		return -EFAULT;
	}

	sock->dstip = ((struct sockaddr_in*)serv_addr)->sin_addr.s_addr;
	sock->dstport = ((struct sockaddr_in*)serv_addr)->sin_port;
	if (sock->srcport == 0){
		sock->srcport = swapWord(getEphemeralPort());
	}

	return ((SOCKET_INFO*)sock->sinf)->connect(sock);
}


int sys_shutdown(int s, int how)
{
	SOCKET *sock;


	if ((sock = (SOCKET*)getFd(s)) == NULL)
		return -EBADF;
	if (sock->fd.form != FD_FORM_NETSOKET)
		return -ENOTSOCK;

	return ((SOCKET_INFO*)sock->sinf)->shutdown(sock,how);
}


int sys_bind(int sockfd, struct sockaddr *my_addr)
{
	SOCKET *sock;


	if ((sock = (SOCKET*)getFd(sockfd)) == NULL)
		return -EBADF;
	if (sock->fd.form != FD_FORM_NETSOKET)
		return -ENOTSOCK;
	if (checkMem(my_addr,sizeof(struct sockaddr)) == ERR)
		return -EFAULT;

	sock->srcip = ((struct sockaddr_in*)my_addr)->sin_addr.s_addr;
	sock->srcport = ((struct sockaddr_in*)my_addr)->sin_port;
	if (sock->srcport == 0)
		sock->srcport = swapWord(getEphemeralPort());

	return 0;
}


int sys_listen(int s, int backlog)
{
	SOCKET *sock;


	if((sock = (SOCKET*)getFd(s)) == NULL)
		return -EBADF;
	if(sock->fd.form != FD_FORM_NETSOKET)
		return -ENOTSOCK;

	return ((SOCKET_INFO*)sock->sinf)->listen(sock,s);
}


int sys_accept(int s, struct sockaddr *addr, int *addrlen)
{
	int fd;
	int rest;
	SOCKET *sock,*newsock;
	uint32_t srcip;


	if (isFileOpenMax() == TRUE){
		return -EMFILE;
	}
	if ((sock = (SOCKET*)getFd(s)) == NULL){
		return -EBADF;
	}
	if (sock->fd.form != FD_FORM_NETSOKET){
		return -ENOTSOCK;
	}
	if ((sock->sockType != SOCK_STREAM) && (sock->sockType != SOCK_DGRAM)){
		return -EOPNOTSUPP;
	}
	if (addr != NULL){
		if (checkMem(addr,sizeof(struct sockaddr) == ERR) || checkMem(addrlen,sizeof(int)) == ERR){
			return -EFAULT;
		}
	}

	/* 接続要求待ち。 */
	rest = ((SOCKET_INFO*)sock->sinf)->accept(sock, &newsock, &srcip);
	if (rest < 0){
		return rest;
	}
	initFd((VENTRY*)&newsock->fs, FD_FORM_NETSOKET, O_RDWR, (F_DSC*)newsock);
	fd = setFd((F_DSC*)newsock, 0);
	if (fd < 0){
		kfree(newsock);
		return fd;
	}

	if (addr != NULL){
		if (*addrlen >= sizeof(struct sockaddr) + sizeof(uint32_t)){
			*(uint32_t*)addr->sa_data = srcip;
			*addrlen = sizeof(struct sockaddr) + sizeof(uint32_t);
		}
	}

	return fd;
}


int sys_getsockname(int sd, struct sockaddr *o_addr, socklen_t *m_len)
{
	SOCKET *sock;
	struct sockaddr_in addr;
	
	// メモリーチェック
	if (checkMem(m_len,sizeof(*m_len)) == ERR){
		return -EFAULT;
	}

	// ソケットのチェック
	if ((sock = (SOCKET*)getFd(sd)) == NULL){
		return -EBADF;
	}
	if (sock->fd.form != FD_FORM_NETSOKET){
		return -ENOTSOCK;
	}
	
	// ローカルアドレスの取得
	addr.sin_family = AF_INET;
	if (sock->srcip == 0){
		addr.sin_addr.s_addr = getSrcip(sock->dstip);
	}
	else{
		addr.sin_addr.s_addr = sock->srcip;
	}
	addr.sin_port = sock->srcport;

	// コピー
	if (*m_len < sizeof(addr)){
		// メモリーチェック
		if (checkMem(o_addr,sizeof(*m_len)) == ERR){
			return -EFAULT;
		}
		memcpy(o_addr, &addr, *m_len);
	}
	else{
		// メモリーチェック
		if (checkMem(o_addr,sizeof(addr)) == ERR){
			return -EFAULT;
		}
		*m_len = sizeof(addr);
		memcpy(o_addr, &addr, sizeof(addr));
	}
	
	return 0;
}


int sys_getpeername(int sd, struct sockaddr *o_addr, socklen_t *m_len)
{
	SOCKET *sock;
	struct sockaddr_in addr;
	
	// メモリーチェック
	if (checkMem(m_len,sizeof(*m_len)) == ERR){
		return -EFAULT;
	}

	// ソケットのチェック
	if ((sock = (SOCKET*)getFd(sd)) == NULL){
		return -EBADF;
	}
	if (sock->fd.form != FD_FORM_NETSOKET){
		return -ENOTSOCK;
	}
	
	// 接続しているか
	if ((sock->stat & SOCK_CNCT_ON) == 0)
		return -ENOTCONN;
	
	// リモートアドレスの取得
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = sock->dstip;
	addr.sin_port = sock->dstport;

	// コピー
	if (*m_len < sizeof(addr)){
		// メモリーチェック
		if (checkMem(o_addr,sizeof(*m_len)) == ERR){
			return -EFAULT;
		}
		memcpy(o_addr, &addr, *m_len);
	}
	else{
		// メモリーチェック
		if (checkMem(o_addr,sizeof(addr)) == ERR){
			return -EFAULT;
		}
		*m_len = sizeof(addr);
		memcpy(o_addr, &addr, sizeof(addr));
	}
	
	return 0;
}


int sys_getsockopt(int fd, int level, int optname,void *optval,int *optlen)
{
	SOCKET *sock;

	/* メモリーチェック */
	if (checkMem(optlen,sizeof(*optlen)) == ERR)
		return -EFAULT;
	if (checkMem(optval,*optlen) == ERR)
		return -EFAULT;

	/* ソケットの取得 */
	if ((sock = (SOCKET*)getFd(fd)) == NULL)
		return -EBADF;

	/* ソケットオプションの取得 */
	return getOpt(sock,level,optname,optval,optlen);
}


int sys_setsockopt(int fd, int level, int optname, const void *optval,int optlen)
{
	SOCKET *sock;

	/* メモリーチェック */
	if (checkMem(optval,optlen) == ERR)
		return -EFAULT;

	/* ソケットの取得 */
	if ((sock = (SOCKET*)getFd(fd)) == NULL)
		return -EBADF;

	/* ソケットオプションの設定 */
	return setOpt(sock,level,optname,optval,optlen);
}


/************************************************************************************
 *
 * File system interface
 *
 ************************************************************************************/


static int mount(const int din, void **o_rootInodeObj)
{
	return -EOPNOTSUPP;
}

static int umount(const int din, void *rootInodeObj)
{
	return -EOPNOTSUPP;
}

static int lookup(const char *path, const int din, void *inodeObj, uint *o_inodeNum)
{
	return -EOPNOTSUPP;
}

static int creat(const char *path, const int din, void *inodeObj, const int mode, uint *o_inodeNum)
{
	return -EOPNOTSUPP;
}

static int open(const int din, const uint inodeNum, int oflag, void **o_inodeObj, int *o_type)
{
	return -EOPNOTSUPP;
}

static int close(const int din, void *inodeObj)
{
	return NOERR;
}

static int read(const int din, void *inodeObj, void *buf, size_t size, size_t begin)
{
	SOCKET *sock = (SOCKET*)inodeObj;

	struct iovec iov = {
		.iov_base = buf, 
		.iov_len = size,
	};
	struct msghdr msg = {
		.msg_iov = &iov,
		.msg_iovlen = 1,
		.msg_control = NULL,
		.msg_controllen = 0,
	};

	return ((SOCKET_INFO*)sock->sinf)->recv(sock, &msg);
}

static int write(const int din, void *inodeObj, void *buf, size_t size, size_t begin)
{
	SOCKET *sock = (SOCKET*)inodeObj;

	return ((SOCKET_INFO*)sock->sinf)->send(sock, buf, size, 0, NULL);
}

static int ioctl(int din, void *inodeObj, int cmd, caddr_t param, int fflag)
{
	switch(cmd){
		case SIOCATMARK:
			return 0;
	}

	return -EOPNOTSUPP;
}


static int poll(void *inodeObj, int events)
{
	SOCKET *sock = (SOCKET*)inodeObj;
	
	return ((SOCKET_INFO*)sock->sinf)->poll(sock);
}


static int opendir(const char *path, const int din, void *dirInodeObj, void **o_inodeObj, uint *o_block, int *o_index)
{
	return -EOPNOTSUPP;
}

static int readdir(int din, void *inodeObj, uint *m_block, int *m_index, char *o_name)
{
	return -EOPNOTSUPP;
}

static int rename(int din, void *OldDirInodeObj, const char *oldPath, void *newDirInodeObj, const char *newPath)
{
	return -EOPNOTSUPP;
}

static int mkdir(const char *path, const int din, void *dirInodeObj, int mode)
{
	return -EOPNOTSUPP;
}

static int stat(int din, void *inodeObj, struct stat *m_stat)
{
	return -EOPNOTSUPP;
}

static int delete(const char *path, const int din, void *dirInodeObj, int type)
{
	return -EOPNOTSUPP;
}

static int chattr(int din, void *inodeObj, uint mode, uint uid, uint gid, uint atime, uint mtime)
{
	return -EOPNOTSUPP;
}

static int link(const int din, void *dstInodeObj, void *srcInodeObj, const char *srcPath)
{
	return -EOPNOTSUPP;
}

static int symlink(const char *filePath, const int din, void *dirInodeObj, char *linkPath)
{
	return -EOPNOTSUPP;
}


static int statfs(const int din, struct statfs *m_statfs)
{
	return -EOPNOTSUPP;
}


/************************************************************************************
 *
 * 初期化
 *
 ************************************************************************************/


static FS netfs={
	"net",
	mount,
	umount,
	lookup,
	creat,
	open,
	close,
	read,
	write,
	ioctl,
	poll,
	opendir,
	readdir,
	rename,
	mkdir,
	stat,
	delete,
	chattr,
	link,
	symlink,
	statfs,
};


/*
 * GLOBAL
 * Init network.
 */
int initNet()
{
	initIpBuf();
	initArp();
	initIp();
	initIcmp();
	initUdp();
	initTcp();
	fs = &netfs;

	return 0;
}
