Milán Major

netscape-revival

1 branch
Code

sun-java/md/socket_md.c

/*
 * @(#)socket.c	1.29 95/12/04 Jonathan Payne
 *
 * Copyright (c) 1994 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Permission to use, copy, modify, and distribute this software
 * and its documentation for NON-COMMERCIAL purposes and without
 * fee is hereby granted provided that this copyright notice
 * appears in all copies. Please refer to the file "copyright.html"
 * for further important copyright and licensing information.
 *
 * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF
 * THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
 * TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 * PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR
 * ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
 * DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
 */

#ifdef __linux
# include <endian.h>
#endif

#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include "socket_md.h"
#include <stdio.h>
#include "iomgr.h"

#include "java_net_Socket.h"
#include "java_net_SocketImpl.h"
#include "java_net_PlainSocketImpl.h"
#include "java_net_ServerSocket.h"
#include "java_net_SocketInputStream.h"
#include "java_net_SocketOutputStream.h"
#include "java_net_InetAddress.h"
#include "java_net_DatagramPacket.h"
#include "java_net_DatagramSocket.h"
#include "java_io_FileDescriptor.h"
#include "fd_md.h"

#define JAVANETPKG 	"java/net/"

#define BODYOF(h)   unhand(h)->body

extern long
sysSocketAvailableFD(Classjava_io_FileDescriptor * fdobj, long *pbytes);

extern void 
sysSocketInitializeFD(Classjava_io_FileDescriptor *fdptr, int infd);

extern int 
sysListenFD(Classjava_io_FileDescriptor *fdobj, long count);

extern int
sysConnectFD(Classjava_io_FileDescriptor *fdobj, struct sockaddr *him, int len);

extern int 
sysAcceptFD(Classjava_io_FileDescriptor *fdobj, struct sockaddr *him, 
int *len);

extern int 
sysCloseSocketFD(Classjava_io_FileDescriptor *fd);

extern int 
sysSendtoFD(Classjava_io_FileDescriptor * fdobj, char *buf, int len, int flags, struct sockaddr *to, int tolen);

extern int
sysRecvfromFD(Classjava_io_FileDescriptor * fdobj, char *buf, int nbytes, int flags, struct sockaddr *from, int *fromlen);

/*
 * InetAddress
 */

struct Hjava_lang_String *
java_net_InetAddress_getLocalHostName(struct Hjava_net_InetAddress *this)
{
    char    hostname[MAXHOSTNAMELEN+1];

    sysGethostname(hostname, sizeof(hostname)-1);
    return makeJavaString(hostname, strlen(hostname));
}

void 
java_net_InetAddress_makeAnyLocalAddress(struct Hjava_net_InetAddress *this, struct Hjava_net_InetAddress *address)
{
    Classjava_net_InetAddress *addr = unhand(address);
    addr->address = htonl(INADDR_ANY);
    addr->family = AF_INET;
}

long 
java_net_InetAddress_getInetFamily(struct Hjava_net_InetAddress *dummy)
{
    return AF_INET;
}

/*
 * Find an internet address for a given hostname.  Note that this
 * code only works for addresses of type INET.  Furthurmore while
 * the APIs are prepared for expansion of IP addresses to 64 bits,
 * the implementaiton, particularly around the translation of
 * "xxx.xxx.xxx.xxx" to an address, will need some work.
 */
HArrayOfByte *
java_net_InetAddress_lookupHostAddr(Hjava_net_InetAddress *dummy, HString *host)
{
    char hostname[MAXHOSTNAMELEN+1];
    char buf[MAXHOSTNAMELEN*2 + 512];
    struct hostent *hp, hpbuf;
    HArrayOfByte *ret;
    int h_error = 0;

    if (host == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return 0;
    }

    javaString2CString(host, hostname, sizeof (hostname));
    if (isdigit(hostname[0])) {
	/*
	 * This is totally bogus. inet_addr returns a 4-byte value
	 * by definition, thereby making the transition to 64-bit IP
	 * addresses much harder.  It should instead write into a
	 * user-supplied "struct in_addr"!  We'll deal with this
	 * later when 64-bit IP addresses are more of a reality -
	 * csw.
	 */
	unsigned long iaddr = inet_addr(hostname);
	if ((int)iaddr == -1) {
	    SignalError(0, JAVANETPKG "UnknownHostException", hostname);
	    return 0;
	}
	ret = (HArrayOfByte *) ArrayAlloc(T_BYTE, sizeof(iaddr));
	if (ret == 0) {
	    return 0;
	}
	memcpy((char *)BODYOF(ret), (char *)&iaddr, sizeof(iaddr));
    } else if ((hp = sysGethostbyname(hostname, &hpbuf, buf, sizeof(buf),
				      &h_error)) != NULL) {
	ret = (HArrayOfByte *) ArrayAlloc(T_BYTE, sizeof(struct in_addr));
	if (ret == 0) {
	    return 0;
	}
	memcpy((char *)BODYOF(ret), hp->h_addr, sizeof(struct in_addr));
    } else {
	SignalError(0, JAVANETPKG "UnknownHostException", hostname);
	return 0;
    }
    return (HArrayOfByte *) ret;
}

/*
 * Find an internet address for a given hostname.  Not this this
 * code only works for addresses of type INET.  Furthurmore while
 * the APIs are prepared for expansion of IP addresses to 64 bits,
 * the implementaiton, particularly around the translation of
 * "xxx.xxx.xxx.xxx" to an address, will need some work.
 */
HArrayOfArray *
java_net_InetAddress_lookupAllHostAddr(Hjava_net_InetAddress *dummy, HString *host)
{
    char hostname[MAXHOSTNAMELEN+1];
    char buf[MAXHOSTNAMELEN*2 + 512];
    struct hostent *hp, hpbuf;
    HArrayOfArray *ret;
#undef h_errno
    int h_errno = 0;

    if (host == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return 0;
    }

    javaString2CString(host, hostname, sizeof (hostname));
    if (isdigit(hostname[0])) { /* presume IP address spelling */
        HArrayOfByte *barray = java_net_InetAddress_lookupHostAddr(dummy, host);
        if (!barray)
              return 0;

        ret = (HArrayOfArray *) ArrayAlloc(T_CLASS, 1);
	if (!ret) {
	    return 0;
	}
	unhand(ret)->body[1] = (HObject*)FindClass(0, "[B", TRUE);

        unhand(ret)->body[0] = (Hjava_lang_Object *) barray;

	return ret;
    } else if ((hp = sysGethostbyname(hostname, &hpbuf, buf, sizeof(buf),
				      &h_errno)) != NULL) {
	struct in_addr **addrp = (struct in_addr **) hp->h_addr_list;
	int i = 0;

	while (*addrp != (struct in_addr *) 0) {
	    i++;
	    addrp++;
	}

        ret = (HArrayOfArray *) ArrayAlloc(T_CLASS, i);
	if (ret == 0) {
	    return 0;
	}
	unhand(ret)->body[i] = (HObject*)FindClass(0, "[B", TRUE);

	addrp = (struct in_addr **) hp->h_addr_list;
	i = 0;
	while (*addrp != (struct in_addr *) 0) {
	    HArrayOfByte *barray = (HArrayOfByte *) 
		ArrayAlloc(T_BYTE, sizeof(struct in_addr));
	    if (barray == 0) {
		return 0;
	    }
	    memcpy((char *)BODYOF(barray),
		   (char *) (*addrp), sizeof(struct in_addr));
	    unhand(ret)->body[i] = (Hjava_lang_Object *) barray;
	    addrp++;
	    i++;
	}
	return ret;
    } else {
	SignalError(0, JAVANETPKG "UnknownHostException", hostname);
	return 0;
    }
}

/*
 * Find a hostname for a given internet address.  Only the first
 * hostname is returned.
 */
HString *
java_net_InetAddress_getHostByAddr(Hjava_net_InetAddress *dummy, long addr)
{
    long netAddr;
    struct hostent hent, *hp;
    char buf[2*MAXHOSTNAMELEN + 512];
    int h_errno = 0;

    /*
     * We are careful here to use the reentrant version of
     * gethostbyname because at the Java level this routine is not
     * protected by any synchronization.
     */

    netAddr = htonl(addr);
    hp = sysGethostbyaddr((char *) &netAddr, sizeof(netAddr), AF_INET, &hent,
			  buf, sizeof(buf), &h_errno);
    if (hp == NULL) {
	SignalError(0, JAVANETPKG "UnknownHostException", NULL);
	return 0;
    }
    return makeJavaString(hp->h_name, strlen(hp->h_name));
}


/*
 * PlainSocketImpl
 */

void 
java_net_PlainSocketImpl_socketCreate(struct Hjava_net_PlainSocketImpl *this, long stream)
{
    Classjava_net_PlainSocketImpl *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    int fd;

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    fd = sysSocket(AF_INET, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
    if (fd == -1) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	return;
    }
    sysSocketInitializeFD(fdptr, fd);
}


void 
java_net_PlainSocketImpl_socketConnect(struct Hjava_net_PlainSocketImpl *this, struct Hjava_net_InetAddress *address, long port)
{
    Classjava_net_PlainSocketImpl *thisptr = unhand(this);
    Classjava_net_InetAddress *addrptr;
    struct sockaddr_in him;
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    if (address == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return;
    }
    addrptr = unhand(address);

    /* connect */
    memset((char *) &him, 0, sizeof(him));
    him.sin_port = htons((short) port);
    him.sin_addr.s_addr = (unsigned long) htonl(addrptr->address);
    him.sin_family = addrptr->family;

    if (sysConnectFD(fdptr, (struct sockaddr *) &him, sizeof(him)) < 0) {
	struct execenv *ee = EE();
	if (ee && exceptionOccurred(ee)) {
	    return;
	}
	if (errno == EPROTO) {
	    SignalError(0, JAVANETPKG "ProtocolException", strerror(errno));
	} else {
	    SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	}
	return;
    }

    /* set the remote peer address and port */
    thisptr->address = address;
    thisptr->port = port;

    /* we need to intialize the local port field
       If bind was called previously to the connect (by the client) then
       localport field will already be initialized 
    */
    if (thisptr->localport == 0) {
	/* Now that we're a connected socket, let's extract the port number
	 * that the system chose for us and store it in the Socket object. 
  	 */
	int len = sizeof(him);
	if (getsockname(fdptr->fd-1, (struct sockaddr *)&him, &len) == -1) {
	    SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	    return;
	}
	thisptr->localport = ntohs(him.sin_port);
    } 
}

void 
java_net_PlainSocketImpl_socketBind(struct Hjava_net_PlainSocketImpl *this, struct Hjava_net_InetAddress *address, long lport)
{
    Classjava_net_PlainSocketImpl *thisptr = unhand(this);
    Classjava_net_InetAddress *addrptr;
    struct sockaddr_in him;
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    if (address == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return;
    }
    addrptr = unhand(address);

    /* bind */
    memset((char *) &him, 0, sizeof(him));
    him.sin_port = htons((short)lport);
    him.sin_addr.s_addr = (unsigned long) htonl(addrptr->address);
    him.sin_family = addrptr->family;

    if (sysBindFD(fdptr, (struct sockaddr *)&him, sizeof(him)) == -1) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	return;
    }

    /* set the address */
    thisptr->address = address;

    /* intialize the local port */
    if (lport == 0) {
	/* Now that we're a connected socket, let's extract the port number
	 * that the system chose for us and store it in the Socket object. 
  	 */
	int len = sizeof(him);
	if (getsockname(fdptr->fd - 1, (struct sockaddr *)&him, &len) == -1) {
	    SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	    return;
	}
	thisptr->localport = ntohs(him.sin_port);
    } else {
	thisptr->localport = lport;
    }
}

void 
java_net_PlainSocketImpl_socketListen(struct Hjava_net_PlainSocketImpl *this, long count)
{
    Classjava_net_PlainSocketImpl *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }
   
    if (sysListenFD(fdptr, count) == -1) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
    }
}

void 
java_net_PlainSocketImpl_socketAccept(struct Hjava_net_PlainSocketImpl *this, struct Hjava_net_SocketImpl *s)
{
    Classjava_net_PlainSocketImpl *thisptr = unhand(this);
    Classjava_io_FileDescriptor *in_fdptr = unhand(thisptr->fd);
    Classjava_net_InetAddress *addrptr;
    Classjava_net_SocketImpl *sptr;
    Classjava_io_FileDescriptor *sptr_fdptr;
    struct sockaddr_in him;
    int len = sizeof(him);
    struct execenv *ee = EE();
    int fd;

    if (in_fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    if (s == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return;
    }
    sptr = unhand(s);
    sptr_fdptr = unhand(sptr->fd);
    if ((sptr->address == 0) || (sptr_fdptr == 0)) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return;
    }
    addrptr = unhand(sptr->address);

    fd = sysAcceptFD(in_fdptr, (struct sockaddr *)&him, &len);
    /*
     * This is a gross temporary workaround for a bug in libsocket
     * of solaris2.3.  They exit a mutex twice without entering it
     * and our monitor stuff throws an exception.  The workaround is
     * to simply ignore InternalErrors from accept.  If we get
     * an exception, and it has an exception object, and we can find
     * the class InternalError in the java pakage, and the
     * exception that was thrown was an instance of
     * InternalError, then ignore it.
     */
    if (exceptionOccurred(ee)) {
	JHandle *h = ee->exception.exc;
	if (h != NULL) {
	    ClassClass *cb;
	    cb = FindClassFromClass(ee, JAVAPKG "InternalError", TRUE, NULL);
	    if (cb != NULL && is_instance_of(h, cb, ee)) {
		exceptionClear(ee);	
	    }
	}
    }

    if (fd < 0) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	return;
    }

    sysSocketInitializeFD(sptr_fdptr, fd);

    /* fill up the remote peer port and address in the new socket structure */
    sptr->port = ntohs(him.sin_port);
    addrptr->family = him.sin_family;
    addrptr->address = ntohl(him.sin_addr.s_addr);
    /* also fill up the local port information */
    sptr->localport = thisptr->localport;
}

long
java_net_PlainSocketImpl_socketAvailable(struct Hjava_net_PlainSocketImpl *Hthis)
{
    long ret = 0;
    Classjava_net_PlainSocketImpl *this = unhand(Hthis);
    Classjava_io_FileDescriptor *fdptr = unhand(this->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return ret;
    }

    /* sysSocketAvailableFD returns 0 for failure, 1 for success */
    if (!sysSocketAvailableFD(fdptr, &ret)){
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
    }

    return ret;
}

void 
java_net_PlainSocketImpl_socketClose(struct Hjava_net_PlainSocketImpl *this)
{
    Classjava_net_PlainSocketImpl *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    sysCloseSocketFD(fdptr);
}
/*
 * SocketInputStream
 */

long 
java_net_SocketInputStream_socketRead(struct Hjava_net_SocketInputStream *this, 
				      HArrayOfByte *data, long off, long len)
{
    Classjava_net_SocketInputStream *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    char *dataptr;
    int datalen, n;
 
    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return -1;
    }

    if (data == 0) {
        SignalError(0, JAVAPKG "NullPointerException", 0);
        return -1;
    }
 
    datalen = obj_length(data);
    dataptr = unhand(data)->body;
 
    if (len < 0 || len + off > datalen) {
        SignalError(0, JAVAPKG "ArrayIndexOutOfBoundsException", 0);
        return 0;
    }
 
    n = sysRecvFD(fdptr, dataptr + off, len, 0);
    if (n == -1) {
        struct execenv *ee = EE();
        if (!(ee && exceptionOccurred(ee))) {
            SignalError(0,  "java/io/IOException", strerror(errno));
        }
    }
    /* AVH: this is bogus but it stops the pointer from being gc'd */
    if (dataptr == 0) {
        SignalError(0, JAVAPKG "NullPointerException", 0);
    }
    return n;
}

/*
 * SocketOutputStream
 */

void
java_net_SocketOutputStream_socketWrite(
	struct Hjava_net_SocketOutputStream *this,
	HArrayOfByte *data, long off, long len)
{
    Classjava_net_SocketOutputStream *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    char *dataptr;
    int datalen, n;

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    if (data == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return;
    }

    datalen = obj_length(data);
    dataptr = unhand(data)->body;

    if ((len < 0) || (off < 0) || (len + off > datalen)) {
        SignalError(0, JAVAPKG "ArrayIndexOutOfBoundsException", 0);
        return;
    }

    while (len > 0) {
	n = sysSendFD(fdptr, dataptr + off, len, 0);
	if (n == -1) {
	    struct execenv *ee = EE();
	    if (!(ee && exceptionOccurred(ee))) {
		SignalError(0,  "java/io/IOException", strerror(errno));
	    }
	    break;
	}
        off += n;
	len -= n;
    }
}

/* Datagram support */



void java_net_DatagramSocket_datagramSocketCreate(struct Hjava_net_DatagramSocket *this)
{
    Classjava_net_DatagramSocket *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    int fd;

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    fd =  socket(AF_INET, SOCK_DGRAM, 0);
    if (fd == -1) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	return;
    }
    sysSocketInitializeFD(fdptr, fd);
}

void java_net_DatagramSocket_datagramSocketClose(struct Hjava_net_DatagramSocket *this)
{
    Classjava_net_DatagramSocket *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    sysCloseSocketFD(fdptr);
}

long java_net_DatagramSocket_datagramSocketBind(struct Hjava_net_DatagramSocket *this, long port)
{
    Classjava_net_DatagramSocket *thisptr = unhand(this);
    struct sockaddr_in lcladdr;
    int lcladdrlen= sizeof(lcladdr);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return -1;
    }

    /* bind - pick a port number for local addr*/
    memset((char *) &lcladdr, 0, sizeof(lcladdr));
    lcladdr.sin_family      = AF_INET;
    lcladdr.sin_port        = htons((short)port);
    lcladdr.sin_addr.s_addr = INADDR_ANY;

    if (sysBindFD(fdptr, (struct sockaddr *)&lcladdr, sizeof(lcladdr)) == -1) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	return -1;
    }

    /* find what port system picked for us - obviously brain dead interface,
       should have told me in the bind call itself */
    if (getsockname(fdptr->fd - 1, (struct sockaddr *)&lcladdr, &lcladdrlen) == -1) {
	SignalError(0, JAVANETPKG "SocketException", strerror(errno));
	return -1;
    }
    thisptr->localPort = ntohs(lcladdr.sin_port);
    return thisptr->localPort;
}

void
java_net_DatagramSocket_datagramSocketSend(struct Hjava_net_DatagramSocket *this,
struct Hjava_net_DatagramPacket *packet)
{
    Classjava_net_DatagramSocket *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    Classjava_net_DatagramPacket *packetptr = unhand(packet);
    Classjava_net_InetAddress *addrptr = unhand(packetptr->address);
    HArrayOfByte *data = packetptr->buf;
    char *dataptr;
    int datalen;
    int n;
    struct sockaddr_in rmtaddr;

    if (fdptr == 0) {
	SignalError(0, JAVAPKG "NullPointerException", "null FileDescriptor");
	return;
    }

    if (data == 0) {
	SignalError(0, JAVAPKG "NullPointerException", 0);
	return;
    }

    dataptr = unhand(data)->body;
    datalen = packetptr->length;
    rmtaddr.sin_port = htons((short)(packetptr->port));
    rmtaddr.sin_addr.s_addr = htonl(addrptr->address);
    rmtaddr.sin_family = addrptr->family;

    n = sysSendtoFD(fdptr, dataptr, datalen, 0, (struct sockaddr *)&rmtaddr, sizeof(rmtaddr));
    if (n == -1) {
      struct execenv *ee = EE();
      if (!(ee && exceptionOccurred(ee))) {
		SignalError(0, "java/io/IOException", strerror(errno));
      }
      packetptr->length = 0;
    }

    packetptr->length = n;
    return;
}

long
java_net_DatagramSocket_datagramSocketPeek(struct Hjava_net_DatagramSocket *this, struct Hjava_net_InetAddress *in)
{
    Classjava_net_DatagramSocket *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    Classjava_net_InetAddress *addrptr = unhand(in);
    int n;
    size_t	peekBufSize;
    struct sockaddr_in remote_addr;
    int remote_addrsize = sizeof (remote_addr);
    char buf[1];

    if (addrptr==0 || fdptr==0) {
        SignalError(0, JAVAPKG "NullPointerException", 0);
        return 0;
    }

#ifdef XP_MAC
	peekBufSize = 0;				// Mac canÕt do true data peeking.
#else
	peekBufSize = 1;
#endif

    n = sysRecvfromFD(fdptr, buf, peekBufSize, MSG_PEEK, 
		 (struct sockaddr *)&remote_addr, &remote_addrsize);

    if (n==-1) {
        SignalError(0, "java/io/IOException", 0);
        return 0;
    }
 
    addrptr->family = remote_addr.sin_family;
    addrptr->address = ntohl(remote_addr.sin_addr.s_addr);

    /* return port */
    return ntohs(remote_addr.sin_port);
}

void
java_net_DatagramSocket_datagramSocketReceive(struct Hjava_net_DatagramSocket *this, struct Hjava_net_DatagramPacket *packet)
{
    Classjava_net_DatagramSocket *thisptr = unhand(this);
    Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
    Classjava_net_DatagramPacket *packetptr = unhand(packet);
    Classjava_net_InetAddress *addrptr=0;
    HArrayOfByte *data = packetptr->buf;
    char *dataptr;
    int datalen;
    int n;
    struct sockaddr_in remote_addr;
    int remote_addrsize = sizeof (remote_addr);

    packetptr->address = (Hjava_net_InetAddress *) execute_java_constructor(EE(),
				  "java/net/InetAddress",
				  (ClassClass *) 0, "()"); 

    if (data == 0 || (packetptr->address==0) || fdptr==0) {
        SignalError(0, JAVAPKG "NullPointerException", 0);
        return;
    }
    addrptr = unhand(packetptr->address);

    dataptr = unhand(data)->body;
    datalen = packetptr->length;
 
    while (1) {
      n = sysRecvfromFD(fdptr, dataptr, datalen, 0, 
		 (struct sockaddr *)&remote_addr, &remote_addrsize);
      if ((n!=-1) || (errno != EINTR)) break;
    }
    if (n == -1) {
        struct execenv *ee = EE();
        if (!(ee && exceptionOccurred(ee))) {
            SignalError(0, "java/io/IOException", strerror(errno));
        }
	packetptr->length = 0;
    } else { /* success */
      packetptr->port = ntohs(remote_addr.sin_port);
      packetptr->length = n;
      addrptr->family = remote_addr.sin_family;
      addrptr->address = ntohl(remote_addr.sin_addr.s_addr);
    }

    return;
}

/* Hook to support multicast receives for @Home */
void
java_net_DatagramSocket_multicastJoinLeave
	(struct Hjava_net_DatagramSocket *this, struct Hjava_net_InetAddress *inetaddr, long join)
{
#ifdef XP_PC
	Classjava_net_DatagramSocket *thisptr = unhand(this);
	Classjava_io_FileDescriptor *fdptr = unhand(thisptr->fd);
	
	int option = (join == TRUE ? IP_ADD_MEMBERSHIP : IP_DROP_MEMBERSHIP);
	
	struct ip_mreq		mreq;

    if (inetaddr == 0 || fdptr == 0) {
        SignalError(0, JAVAPKG "NullPointerException", 0);
        return;
    }

	mreq.imr_multiaddr.s_addr = htonl(unhand(inetaddr)->address);
	mreq.imr_interface.s_addr = htonl(INADDR_ANY);	  

	if ( setsockopt(fdptr->fd - 1, IPPROTO_IP, option,
					(char *) &mreq, sizeof(mreq)) == -1 )
	{
		char buf[63];
		sprintf(buf, "%i", WSAGetLastError());
		SignalError(0, JAVANETPKG "SocketException", buf);
		return;	  
	}
#endif
}