Ejemplo n.º 1
0
jint
NET_Wait(JNIEnv *env, jint fd, jint flags, jint timeout)
{
    jlong prevTime = JVM_CurrentTimeMillis(env, 0);
    jint read_rv;

    while (1) {
	jlong newTime;
	fd_set rd, wr, ex;
	struct timeval t;

	t.tv_sec = timeout / 1000;
	t.tv_usec = (timeout % 1000) * 1000;

	FD_ZERO(&rd);
	FD_ZERO(&wr);
	FD_ZERO(&ex);
	if (flags & NET_WAIT_READ) {
	  FD_SET(fd, &rd);
	}
	if (flags & NET_WAIT_WRITE) {
	  FD_SET(fd, &wr);
	}
	if (flags & NET_WAIT_CONNECT) {
	  FD_SET(fd, &wr);
	  FD_SET(fd, &ex);
	}
	  
	errno = 0;
	read_rv = select(fd+1, &rd, &wr, &ex, &t);

	newTime = JVM_CurrentTimeMillis(env, 0);
	timeout -= (newTime - prevTime);
	if (timeout <= 0) {
	  return read_rv > 0 ? 0 : -1;
	}
	newTime = prevTime;

	if (read_rv > 0) {
	  break;
	}

	
      } /* while */
      
    return timeout;
}
/*
 * Class:     java_net_DualStackPlainDatagramSocketImpl
 * Method:    socketReceiveOrPeekData
 * Signature: (ILjava/net/DatagramPacket;IZZ)I
 */
JNIEXPORT jint JNICALL Java_java_net_DualStackPlainDatagramSocketImpl_socketReceiveOrPeekData
  (JNIEnv *env, jclass clazz, jint fd, jobject dpObj,
   jint timeout, jboolean connected, jboolean peek) {
    SOCKETADDRESS sa;
    int sa_len = sizeof(sa);
    int port, rv, flags=0;
    char BUF[MAX_BUFFER_LEN];
    char *fullPacket;
    BOOL retry;
    jlong prevTime = 0;

    jint packetBufferOffset, packetBufferLen;
    jbyteArray packetBuffer;

    /* if we are only peeking. Called from peekData */
    if (peek) {
        flags = MSG_PEEK;
    }

    packetBuffer = (*env)->GetObjectField(env, dpObj, dp_bufID);
    packetBufferOffset = (*env)->GetIntField(env, dpObj, dp_offsetID);
    packetBufferLen = (*env)->GetIntField(env, dpObj, dp_bufLengthID);
    /* Note: the buffer needn't be greater than 65,536 (0xFFFF)
    * the max size of an IP packet. Anything bigger is truncated anyway.
    */
    if (packetBufferLen > MAX_PACKET_LEN) {
        packetBufferLen = MAX_PACKET_LEN;
    }

    if (packetBufferLen > MAX_BUFFER_LEN) {
        fullPacket = (char *)malloc(packetBufferLen);
        if (!fullPacket) {
            JNU_ThrowOutOfMemoryError(env, "Native heap allocation failed");
            return -1;
        }
    } else {
        fullPacket = &(BUF[0]);
    }

    do {
        retry = FALSE;

        if (timeout) {
            if (prevTime == 0) {
                prevTime = JVM_CurrentTimeMillis(env, 0);
            }
            rv = NET_Timeout(fd, timeout);
            if (rv <= 0) {
                if (rv == 0) {
                    JNU_ThrowByName(env,JNU_JAVANETPKG "SocketTimeoutException",
                                    "Receive timed out");
                } else if (rv == JVM_IO_ERR) {
                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException",
                                    "Socket closed");
                } else if (rv == JVM_IO_INTR) {
                    JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
                                    "operation interrupted");
                }
                if (packetBufferLen > MAX_BUFFER_LEN) {
                    free(fullPacket);
                }
                return -1;
            }
        }

        /* receive the packet */
        rv = recvfrom(fd, fullPacket, packetBufferLen, flags,
                    (struct sockaddr *)&sa, &sa_len);

        if (rv == SOCKET_ERROR && (WSAGetLastError() == WSAECONNRESET)) {
            /* An icmp port unreachable - we must receive this as Windows
             * does not reset the state of the socket until this has been
             * received.
             */
            purgeOutstandingICMP(env, fd);

            if (connected) {
                JNU_ThrowByName(env, JNU_JAVANETPKG "PortUnreachableException",
                                "ICMP Port Unreachable");
                if (packetBufferLen > MAX_BUFFER_LEN)
                    free(fullPacket);
                return -1;
            } else if (timeout) {
                /* Adjust timeout */
                jlong newTime = JVM_CurrentTimeMillis(env, 0);
                timeout -= (jint)(newTime - prevTime);
                if (timeout <= 0) {
                    JNU_ThrowByName(env, JNU_JAVANETPKG "SocketTimeoutException",
                                    "Receive timed out");
                    if (packetBufferLen > MAX_BUFFER_LEN)
                        free(fullPacket);
                    return -1;
                }
                prevTime = newTime;
            }
            retry = TRUE;
        }
    } while (retry);

    port = (int) ntohs ((u_short) GET_PORT((SOCKETADDRESS *)&sa));

    /* truncate the data if the packet's length is too small */
    if (rv > packetBufferLen) {
        rv = packetBufferLen;
    }
    if (rv < 0) {
        if (WSAGetLastError() == WSAEMSGSIZE) {
            /* it is because the buffer is too small. It's UDP, it's
             * unreliable, it's all good. discard the rest of the
             * data..
             */
            rv = packetBufferLen;
        } else {
            /* failure */
            (*env)->SetIntField(env, dpObj, dp_lengthID, 0);
        }
    }

    if (rv == -1) {
        JNU_ThrowByName(env, JNU_JAVANETPKG "SocketException", "socket closed");
    } else if (rv == -2) {
        JNU_ThrowByName(env, JNU_JAVAIOPKG "InterruptedIOException",
                        "operation interrupted");
    } else if (rv < 0) {
        NET_ThrowCurrent(env, "Datagram receive failed");
    } else {
        jobject packetAddress;
        /*
         * Check if there is an InetAddress already associated with this
         * packet. If so, we check if it is the same source address. We
         * can't update any existing InetAddress because it is immutable
         */
        packetAddress = (*env)->GetObjectField(env, dpObj, dp_addressID);
        if (packetAddress != NULL) {
            if (!NET_SockaddrEqualsInetAddress(env, (struct sockaddr *)&sa,
                                               packetAddress)) {
                /* force a new InetAddress to be created */
                packetAddress = NULL;
            }
        }
        if (packetAddress == NULL) {
            packetAddress = NET_SockaddrToInetAddress(env, (struct sockaddr *)&sa,
                                                      &port);
            if (packetAddress != NULL) {
                /* stuff the new Inetaddress into the packet */
                (*env)->SetObjectField(env, dpObj, dp_addressID, packetAddress);
            }
        }

        if (!(*env)->ExceptionCheck(env)) {
            /* populate the packet */
            (*env)->SetByteArrayRegion(env, packetBuffer, packetBufferOffset, rv,
                                   (jbyte *)fullPacket);
            (*env)->SetIntField(env, dpObj, dp_portID, port);
            (*env)->SetIntField(env, dpObj, dp_lengthID, rv);
        }
    }

    if (packetBufferLen > MAX_BUFFER_LEN) {
        free(fullPacket);
    }
    return port;
}