/* cpnet.c -
   Copyright (C) 2003, 2004, 2005, 2006  Free Software Foundation, Inc.

This file is part of GNU Classpath.

GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.

GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING.  If not, write to the
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 USA.

Linking this library statically or dynamically with other modules is
making a combined work based on this library.  Thus, the terms and
conditions of the GNU General Public License cover the whole
combination.

As a special exception, the copyright holders of this library give you
permission to link this library with independent modules to produce an
executable, regardless of the license terms of these independent
modules, and to copy and distribute the resulting executable under
terms of your choice, provided that you also meet, for each linked
independent module, the terms and conditions of the license of that
module.  An independent module is a module which is not derived from
or based on this library.  If you modify this library, you may extend
this exception to your version of the library, but you are not
obligated to do so.  If you do not wish to do so, delete this
exception statement from your version. */

#include "config.h"
#include <jni.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <netdb.h>
#include <sys/time.h>
#include <unistd.h>
#include <arpa/inet.h>
#include "cpnet.h"
#include "wm.h"


// ソケット情報管理用
struct _socket_prop_element {
	int timeout;		// タイムアウト値（-1は指定なし、0は無限）
	BOOL blocking;		// ブロッキング
};

typedef struct _socket_prop_element	socket_prop_element;

static socket_prop_element socketProperties[FD_SETSIZE];
static struct CRITICAL_SECTION	g_critical_section;

void init_socket_properties();
static int set_socket_timeout(SOCKET socket, int timeout);
static socket_prop_element* find_socket_property(SOCKET sock);

/**
 * ソケットプロパティ値を初期化する
 */
void init_socket_properties() {
	int i;
	for (i = 0; i < FD_SETSIZE; ++i) {
		socketProperties[i].blocking = TRUE;
		socketProperties[i].timeout  = -1;	// タイムアウト指定なし
	}
	InitializeCriticalSection(&g_critical_section);
}

/**
 * ソケットがブロッキングモードかどうかを返す。
 *
 * @param	sock	ソケット
 * @return	ブロッキングモードの場合はTRUE
 */
BOOL cpnet_is_socket_blocking(SOCKET sock) {
	BOOL result = TRUE;
	socket_prop_element* elem;

	EnterCriticalSection(&g_critical_section);
	elem = find_socket_property(sock);
	if (elem) {
		result = elem->blocking;
	}
	LeaveCriticalSection(&g_critical_section);


	return result;

}

/**
 * ソケットのブロッキングモードを変更する
 */
int cpnet_set_socket_blocking(SOCKET socket, BOOL blocking) {
	int result = 0;
	socket_prop_element* elem;
	int index = (int) socket;

	EnterCriticalSection(&g_critical_section);
	// 一致するスロットを検索する
	elem = find_socket_property(socket);
	
	if (elem) {
		// ブロッキングモードを変更する
		elem->blocking = blocking;
	} else {
		// エラー
		result = -1;
	}
	LeaveCriticalSection(&g_critical_section);

	return result;
}

/**
 * ソケットタイムアウト値を設定する
 */
static int set_socket_timeout(SOCKET socket, int timeout) {
	int result = 0;
	socket_prop_element* elem;
	int index = (int) socket;

	EnterCriticalSection(&g_critical_section);

	elem = find_socket_property(socket);
	if (elem) {
		elem->timeout = timeout;
	} else {
		// エラー
		result = -1;
	}
	LeaveCriticalSection(&g_critical_section);
	return result;
}
			

/**
 * ソケットのプロパティをクリアする
 */
static int clear_socket_property(SOCKET socket) {
	socket_prop_element* elem;

	EnterCriticalSection(&g_critical_section);
	elem = find_socket_property(socket);
	if (elem) {
		elem->timeout = -1;
		elem->blocking = TRUE;
	}
	LeaveCriticalSection(&g_critical_section);
	return 0;
}

/**
 * ソケットプロパティを返す。
 */
static socket_prop_element* find_socket_property(SOCKET sock) {
	int index = (int) sock;
	socket_prop_element* result = NULL;
	if (index >= 0 && index < FD_SETSIZE) {
		result = &(socketProperties[index]);
	}
	return result;
}

/**
 * ソケットタイムアウト値を取得する
 */
static int cpnet_get_socket_timeout(SOCKET socket) {
	int result = -1;
	socket_prop_element* elem;

	EnterCriticalSection(&g_critical_section);
	
	elem = find_socket_property(socket);
	if (elem) {
		result = elem->timeout;
	}
	LeaveCriticalSection(&g_critical_section);

	return result;
}

/**
 * 指定されたイベントが発生するか、指定されたタイムアウトがおきるまでブロックする。
 *
 * @param	sock	ソケット
 * @param	flag	フラグ
 * @param	timeout	タイムアウト値
 * @return	エラーコード。エラーが発生しなかった場合はNOERROR
 */
static jint waitForTimeout(SOCKET sock, long flag, jint timeout) {
	int result;
	DWORD dwWaitResult;
	DWORD dwError = NOERROR;
	DWORD dwTimeout = (timeout == 0) ? WSA_INFINITE : (DWORD) timeout;
	WSAEVENT hEvent = WSACreateEvent();

	// この呼び出しで自動的にノンブロッキング・モードになる
	WSAEventSelect(sock, hEvent, flag);
	// 送信可能になるまで待つ
	dwWaitResult = WSAWaitForMultipleEvents(1,			// 待機イベント数
				 						    &hEvent,	// イベント配列のアドレス
											FALSE,		// すべてのイベントを待つか
											dwTimeout,	// タイムアウト値
											FALSE);		// Alertable
	switch (dwWaitResult) {
		case WSA_WAIT_EVENT_0:
			// イベント発生
			result = 0;
			break;
		case WSA_WAIT_TIMEOUT:
			// タイムアウト発生
			result = WSAETIMEDOUT;
			break;

		default:
			// その他のエラー
			result = WSAGetLastError();
			break;
	}
		
	if (result != WSAETIMEDOUT) {
		// エラー発生をチェックする
		WSANETWORKEVENTS events;
		if (SOCKET_ERROR == WSAEnumNetworkEvents(sock, hEvent, &events)) {
			result = WSAGetLastError();
		} else {
			int index;
			switch (flag) {
				case FD_READ:
					index = FD_READ_BIT;
					break;
				case FD_WRITE:
					index = FD_WRITE_BIT;
					break;
				case FD_ACCEPT:
					index = FD_ACCEPT_BIT;
					break;
				case FD_CONNECT:
					index = FD_CONNECT_BIT;
					break;
				case FD_CLOSE:
					index = FD_CLOSE_BIT;
					break;
				default:
					index = -1;
					break;
			}
			assert(index != -1);
			result = events.iErrorCode[index];
		}
	}
	// ブロッキングモードに戻す
	// こうしないと、後続処理で再びWSAEWOULDBLOCKエラーが発生してしまう
	// また、accept()の戻り値にはWSAEventSelect()で設定したフラグが継承されてしまう
	// http://msdn.microsoft.com/en-us/library/aa920080.aspx
	// Remarks 
	// This function extracts the first connection on the queue of pending connections on socket s.
	// It then creates a new socket and returns a handle to the new socket. 
	// The newly created socket is the socket that will handle the actual connection and has the same properties as socket s,
	// including the asynchronous events registered with the WSAEventSelect function

	WSAEventSelect(sock, hEvent, 0);	// イベントをクリアしないとノンブロッキングモードに戻せない
	do {
		u_long arg = 0;
		if (ioctlsocket(sock, FIONBIO, &arg) == SOCKET_ERROR) {
			result = WSAGetLastError();
		}
	} while(0);
	// イベントをクローズする
	WSACloseEvent(hEvent);
	return result;
}

/**
 * イベントを待機する
 * 指定されたイベントが発生するか、またはsetSocketTimeout()で指定されたタイムアウト時間が経過するまでブロックする
 * ToDo: ノンブロッキングモードのサポート
 *
 * @param	sock	ソケット
 * @flag	フラグ。FD_READ, FD_WRITE, FD_CONNECT, FD_ACCEPTのいずれか
 *          2つ以上の組み合わせはサポートしていない
 * @return	正常終了時はNOERROR。エラー発生時はエラーコード
 *			
 */
static jint waitForEvent(SOCKET sock, long flag) {
	jint result = NOERROR;
	int timeout = -1;
	socket_prop_element* elem;

	assert(flag == FD_READ || flag == FD_WRITE || flag == FD_CONNECT || flag == FD_ACCEPT);

	EnterCriticalSection(&g_critical_section);
	elem = find_socket_property(sock);
	if (elem) {
		timeout = elem->timeout;
	}
	LeaveCriticalSection(&g_critical_section);

	if (timeout >= 0) {
		result = waitForTimeout(sock, flag, timeout);
	}
	return result;
}

jint cpnet_waitForWritable(SOCKET sock) {
	return waitForEvent(sock, FD_WRITE);
}

jint cpnet_waitForReadable(SOCKET sock) {
	return waitForEvent(sock, FD_READ);
}

jint cpnet_waitForConnect(SOCKET sock, jint timeout) {
	return waitForTimeout(sock, FD_CONNECT, (long) timeout);
}

jint cpnet_waitForAcceptable(SOCKET sock) {
	return waitForEvent(sock, FD_ACCEPT);
}

#ifndef _WIN32_WCE
jint cpnet_openSocketStream(JNIEnv *env UNUSED, jint *fd, jint family)
{
  *fd = socket(family, SOCK_STREAM, 0);
  if (*fd == -1)
    return WSAGetLastError();

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_openSocketDatagram(JNIEnv *env UNUSED, jint *fd, jint family)
{
  *fd = socket(family, SOCK_DGRAM, 0);
  if (*fd == -1)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_shutdown (JNIEnv *env UNUSED, jint fd, jbyte flag)
{
  int ret;
  int shut_flag = 0;

  if (flag == CPNET_SHUTDOWN_READ)
    shut_flag = SHUT_RD;
  else if (flag == CPNET_SHUTDOWN_WRITE)
    shut_flag = SHUT_WR;

  ret = shutdown (fd, shut_flag);
  if (ret != 0)
    return errno;
  return 0;
}
#endif

jint cpnet_close(JNIEnv *env UNUSED, jint fd)
{
  clear_socket_property((SOCKET) fd);
  
  if (SOCKET_ERROR == closesocket((SOCKET) fd)) {
	return -1;
  }
  return 0;
}

#ifndef _WIN32_WCE
jint cpnet_listen(JNIEnv *env UNUSED, jint fd, jint queuelen)
{
  if (listen ((SOCKET) fd, queuelen) != 0)
    return errno;
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_accept(JNIEnv *env UNUSED, jint fd, jint *newfd)
{
  if (waitForReadable (fd) < 0)
    return ETIMEDOUT;

  *newfd = accept((SOCKET) fd, NULL, 0);
  if (*newfd != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_bind(JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
{
  int ret;

  ret = bind((SOCKET) fd, (struct sockaddr *)addr->data, addr->len);
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_connect(JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
{
  int ret;
  ret = connect((SOCKET) fd, (struct sockaddr *) addr->data, addr->len);
  if (ret != 0)
    return ret;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getLocalAddr(JNIEnv *env, jint fd, cpnet_address **addr)
{
  socklen_t slen = 1024;
  int ret;

  *addr = JCL_malloc(env, slen);

  slen -= sizeof(jint);
  ret = getsockname(fd, (struct sockaddr *)(*addr)->data, &slen );
  if (ret != 0)
    {
      int err = errno;
      JCL_free(env, *addr);
      return err;
    }

  (*addr)->len = slen;
  
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getRemoteAddr(JNIEnv *env, jint fd, cpnet_address **addr)
{
  socklen_t slen = 1024;
  int ret;

  *addr = JCL_malloc(env, slen);

  slen -= sizeof(jint);
  ret = getpeername(fd, (struct sockaddr *)(*addr)->data, &slen );
  if (ret != 0)
    {
      int err = errno;
      JCL_free(env, *addr);
      return err;
    }

  (*addr)->len = slen;
  
  return 0;
}
#endif
#ifndef _WIN32_WCE
jint cpnet_setBroadcast(JNIEnv *env UNUSED, jint fd, jint flag)
{
  int ret;

  ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*) &flag, sizeof(flag));
  if (ret != 0)
    return errno;

  return 0;
}
#endif
#if defined (HAVE_MSG_NOSIGNAL)
#elif defined (HAVE_SO_NOSIGPIPE)
static int setsockopt_NOSIGPIPE (int fd)
{
  int setToTrue = 1;
  return setsockopt(fd, SOL_SOCKET, SO_NOSIGPIPE, &setToTrue, sizeof(setToTrue));
}
#endif

#ifndef _WIN32_WCE
jint cpnet_send (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, jint *bytes_sent)
{
  ssize_t ret;

  if (waitForWritable(fd) < 0)
    return ETIMEDOUT;

#if defined (HAVE_MSG_NOSIGNAL)
  ret = send(fd, data, len, MSG_NOSIGNAL);
#elif defined (HAVE_SO_NOSIGPIPE)
  ret = setsockopt_NOSIGPIPE(fd);
  if (ret == 0) ret = send(fd, data, len, 0);
#else
  /* We want SIGPIPE to be omitted. But this configuration does not have an
   * option for that.
   */
  ret = send(fd, data, len, 0);
#endif
  if (ret < 0)
    return errno;

  *bytes_sent = ret;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_sendTo (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, cpnet_address *addr, jint *bytes_sent)
{
  ssize_t ret;

  if (waitForWritable(fd) < 0)

#if defined (HAVE_MSG_NOSIGNAL)
  ret = sendto(fd, data, len, MSG_NOSIGNAL, (struct sockaddr *)addr->data,
	       addr->len);
#elif defined (HAVE_SO_NOSIGPIPE)
  ret = setsockopt_NOSIGPIPE(fd);
  if (ret == 0)
  {
    ret = sendto(fd, data, len, 0, (struct sockaddr *)addr->data,
	       addr->len);
  }
#else
  /* We want SIGPIPE to be omitted. But this configuration does not have an
   * option for that.
   */
  ret = sendto(fd, data, len, 0, (struct sockaddr *)addr->data,
	       addr->len);
#endif

  if (ret < 0)
    return errno;

  *bytes_sent = ret;
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_recv (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, jint *bytes_recv)
{
  ssize_t ret;

  if (waitForReadable(fd) < 0)
    return ETIMEDOUT;

  ret = recv(fd, data, len, 0);
  if (ret < 0)
    return errno;

  *bytes_recv = ret;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_recvFrom (JNIEnv *env, jint fd, jbyte *data, jint len, cpnet_address **addr, jint *bytes_recv)
{
  socklen_t slen = 1024;
  ssize_t ret;

  if (waitForReadable(fd) < 0)
    return ETIMEDOUT;

  *addr = JCL_malloc(env, slen);

  slen -= sizeof(jint);
  ret = recvfrom(fd, data, len, 0, (struct sockaddr *) (*addr)->data, &slen);
  if (ret < 0)
    {
      int err = errno;
      JCL_free(env, *addr);
      return err;
    }

  (*addr)->len = slen;
  *bytes_recv = ret;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_setSocketTCPNoDelay (JNIEnv *env UNUSED, jint fd, jint nodelay)
{
  socklen_t len = sizeof(jint);
  int ret;

  ret = setsockopt((SOCKET) fd, IPPROTO_TCP, TCP_NODELAY, (const char*) &nodelay, len);
  if (ret < 0)
    return errno;

  return 0; 
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getSocketTCPNoDelay (JNIEnv *env UNUSED, jint fd, jint *nodelay)
{
  socklen_t len = sizeof(jint);
  int ret;

  ret = getsockopt((SOCKET) fd, IPPROTO_TCP, TCP_NODELAY, (char*) nodelay, &len);
  if (ret < 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_setLinger (JNIEnv *env UNUSED, jint fd, jint flag, jint value)
{
  socklen_t len = sizeof(struct linger);
  int ret;
  struct linger __linger;

  if (flag)
    {
      __linger.l_onoff = 0;
    }
  else
    {
      __linger.l_linger = value;
      __linger.l_onoff = 1;
    }

  ret = setsockopt((SOCKET) fd, SOL_SOCKET, SO_LINGER, (const char*) &__linger, len);
  if (ret < 0)
    return errno;

  return 0; 
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getLinger (JNIEnv *env UNUSED, jint fd, jint *flag, jint *value)
{
  socklen_t slen = sizeof(struct linger);
  struct linger __linger;
  int ret;

  ret = getsockopt(fd, SOL_SOCKET, SO_LINGER, (char*) &__linger, &slen);
  if (ret != 0)
    return errno;

  *flag = __linger.l_onoff;
  *value = __linger.l_linger;

  return ret;
}
#endif

jint cpnet_setSocketTimeout (JNIEnv *env UNUSED, jint fd, jint value)
{
  set_socket_timeout((SOCKET) fd, value);
  return 0;
}

jint cpnet_getSocketTimeout (JNIEnv *env UNUSED, jint fd, jint *value)
{
	*value = cpnet_get_socket_timeout((SOCKET) fd);
	return 0;
}

#ifndef _WIN32_WCE
jint cpnet_setSendBuf (JNIEnv *env UNUSED, jint fd, jint value)
{
  int ret;

  ret = setsockopt((SOCKET) fd, SOL_SOCKET, SO_SNDBUF, (const char*) &value, sizeof(value));
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getSendBuf (JNIEnv *env UNUSED, jint fd, jint *value)
{
  int ret;
  socklen_t slen = sizeof(*value);

  ret = getsockopt((SOCKET) fd, SOL_SOCKET, SO_SNDBUF, (char*) value, &slen);
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_setRecvBuf (JNIEnv *env UNUSED, jint fd, jint value)
{
  int ret;

  ret = setsockopt((SOCKET) fd, SOL_SOCKET, SO_RCVBUF, (const char*) &value, sizeof(value));
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getRecvBuf (JNIEnv *env UNUSED, jint fd, jint *value)
{
  int ret;
  socklen_t slen = sizeof(*value);

  ret = getsockopt((SOCKET) fd, SOL_SOCKET, SO_RCVBUF, (char*) value, &slen);
  if (ret != 0)
    return errno;
  
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_setTTL (JNIEnv *env UNUSED, jint fd, jint value)
{ 
  int ret;

  /* Windows CEではIP_TTLはサポートされていない。代わりにIP_MULTICAST_TTLを用いることにする */
//  ret = setsockopt(fd, IPPROTO_IP, IP_TTL, &value, sizeof(value));
  ret = setsockopt((SOCKET) fd, IPPROTO_IP, IP_MULTICAST_TTL, (const char*) &value, sizeof(value));
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getTTL (JNIEnv *env UNUSED, jint fd, jint *value)
{
  int ret;
  socklen_t slen = sizeof(*value);

  /* Windows CEではIP_TTLはサポートされていない。代わりにIP_MULTICAST_TTLを用いることにする */
//  ret = getsockopt(fd, IPPROTO_IP, IP_TTL, value, &slen);
  ret = getsockopt((SOCKET) fd, IPPROTO_IP, IP_MULTICAST_TTL, (char*) value, &slen);
  if (ret != 0)
    return errno;
  
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_setMulticastIF (JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
{
  int ret;

  ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (const char*)addr->data, addr->len);
  if (ret != 0)
    return errno;

  return 0;
}
#endif

jint cpnet_getMulticastIF (JNIEnv *env, jint fd, cpnet_address **addr)
{
  socklen_t slen = 1024;
  int ret;

  *addr = JCL_malloc(env, slen);

  slen -= sizeof(jint);
  ret = getsockopt((SOCKET) fd, IPPROTO_IP, IP_MULTICAST_IF, (char *)(*addr)->data, &slen);
  (*addr)->len = slen;

  if (ret != 0)
    return errno;
  
  return 0;
}

#ifndef _WIN32_WCE
jint cpnet_setReuseAddress (JNIEnv *env UNUSED, jint fd, jint reuse)
{
  int ret;

  ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuse, sizeof(reuse));
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getReuseAddress (JNIEnv *env UNUSED, jint fd, jint *reuse)
{
  int ret;
  socklen_t slen = sizeof(*reuse);

  ret = getsockopt((SOCKET) fd, SOL_SOCKET, SO_REUSEADDR, (char*) reuse, &slen);
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_setKeepAlive (JNIEnv *env UNUSED, jint fd, jint keep)
{
  int ret;

  ret = setsockopt((SOCKET) fd, SOL_SOCKET, SO_KEEPALIVE, (const char*) &keep, sizeof(keep));
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getKeepAlive (JNIEnv *env UNUSED, jint fd, jint *keep)
{
  int ret;
  socklen_t slen = sizeof(*keep);

  ret = getsockopt((SOCKET) fd, SOL_SOCKET, SO_KEEPALIVE, (char*) keep, &slen);
  if (ret != 0)
    return errno;

  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_addMembership (JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
{
  struct ip_mreq req;
  int ret;
  
  memset(&req, 0, sizeof(req));
  req.imr_multiaddr = ((struct sockaddr_in *)addr->data)->sin_addr;
  req.imr_interface.s_addr = INADDR_ANY;
  ret = setsockopt((SOCKET) fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, (const char*) &req, sizeof(req));
  if (ret != 0)
    return errno;
  
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_dropMembership (JNIEnv *env UNUSED, jint fd, cpnet_address *addr)
{
  struct ip_mreq req;
  int ret;
  
  memset(&req, 0, sizeof(req));
  req.imr_multiaddr = ((struct sockaddr_in *)addr->data)->sin_addr;
  req.imr_interface.s_addr = INADDR_ANY;
  ret = setsockopt((SOCKET) fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, (const char*) &req, sizeof(req));
  if (ret != 0)
    return errno;
  
  return 0;
}
#endif

#ifndef _WIN32_WCE
jint cpnet_getAvailableBytes (JNIEnv *env UNUSED, jint fd, jint *availableBytes)
{
  int ret;

  ret = ioctlsocket(fd, FIONREAD, availableBytes);
  if (ret != 0)
    return errno;

  return 0;
}
#endif

jint cpnet_getHostname (JNIEnv *env UNUSED, char *hostname, jint hostname_len)
{
  int ret;

  ret = gethostname(hostname, hostname_len);
  if (ret != 0)
    return errno;

  hostname[hostname_len-1] = 0;
  return 0;
}

jint cpnet_getHostByName (JNIEnv *env, const char *hostname, cpnet_address ***addresses, jint *addresses_count)
{
  int ret = 0;
  int counter = 0;
  cpnet_address **addr_arr;
  int i;
  struct addrinfo* addr_res;
  struct addrinfo* rp;
  int result;

  // アドレス情報を取得する
  result = getaddrinfo(hostname, NULL, NULL, &addr_res);
  if (result) {
	  return result;
  }

  // 取得した名前の数を数える
  for (rp = addr_res; rp != NULL; rp = rp->ai_next) {
	counter++;
  }

  // 返却用のメモリを確保する
  addr_arr = *addresses = JCL_malloc(env, sizeof(cpnet_address *) * counter);

  // 内容をコピーする
  for (i = 0, rp = addr_res; rp != NULL; rp = rp->ai_next) {
	  switch (rp->ai_family)
		{
		case AF_INET:
		  // IPv4
			addr_arr[i] = cpnet_newIPV4Address(env);
			memcpy(addr_arr[i]->data, rp->ai_addr, sizeof(struct sockaddr_in));
			i++;
		    break;

	#ifdef HAVE_INET6
		case AF_INET6:
		  // IPv6
			addr_arr[i] = cpnet_newIPV6Address(env);
			memcpy(addr_arr[i]->data, rp->ai_addr, sizeof(struct sockaddr_in6));
			i++;
		    break;
	#endif
		default:
			// スキップ
			break;

	  }
  }
  *addresses_count = i;

  freeaddrinfo(addr_res);

  return 0;
}

jint cpnet_getHostByAddr (JNIEnv *env UNUSED, cpnet_address *addr, char *hostname, jint hostname_len)
{
  union 
  {
    struct sockaddr_in *addr_v4;
    struct sockaddr_in6 *addr_v6;
    char *data;
  } haddr;
  int salen;

  haddr.data = addr->data;
  
  if (haddr.addr_v4->sin_family == AF_INET)
    {
	  salen = sizeof(struct sockaddr_in);
    }
  else if (haddr.addr_v6->sin6_family == AF_INET6)
    {
	  salen = sizeof(struct sockaddr_in6);
    }
  else
    return EINVAL;

	// Windows Mobileのgetnameinfo()はアドレス 0.0.0.0に対応していないため、特別扱いする
	if (haddr.addr_v4->sin_addr.S_un.S_addr == 0) {
		strcpy(hostname, "");
		return 0;
	}
	if (getnameinfo((const struct sockaddr*) haddr.data, salen, hostname, hostname_len, NULL, 0, 0)) {
	  return WSAGetLastError();
	}
	return 0;
}

jint cpnet_aton (JNIEnv *env, const char *hostname, cpnet_address **addr)
{
	// Windows CEにはinet_aton()が存在しないので、getaddrinfo()を使う
	struct addrinfo hints = {0};
	struct addrinfo* res;
	int result;

	*addr = NULL;

	hints.ai_flags = AI_NUMERICHOST;
	hints.ai_family = PF_UNSPEC;
	
	result = getaddrinfo(hostname, NULL, &hints, &res);
	if (result) {
		if (result == WSAHOST_NOT_FOUND) {
			// 「見つからない」のはエラーではない
			return 0;
		}
		return -1;
	}

	// 一番先頭のアドレス情報を得る
	// (AI_NUMERICHOSTフラグを付与しているので、2つ以上のアドレスは返ってこないはず）
#ifdef HAVE_INET6
	if (res->ai_family == PF_INET6) {
		*addr = cpnet_newIPV6Address(env);
		memcpy((*addr)->data, res->ai_addr, sizeof(struct sockaddr_in6));
	} else
#endif
	if (res->ai_family == PF_INET) { 
		*addr = cpnet_newIPV4Address(env);
		memcpy((*addr)->data, res->ai_addr, sizeof(struct sockaddr_in));
	} else {
			return -1;
	}
	freeaddrinfo(res);
	return 0;
}

void cpnet_freeAddresses(JNIEnv * env, cpnet_address **addr, jint addresses_count)
{
  jint i;

  for (i = 0; i < addresses_count; i++)
    cpnet_freeAddress(env, addr[i]);
}
