1 /*
   2  * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 #include <malloc.h>
  26 
  27 #include "net_util.h"
  28 
  29 #include "java_net_SocketInputStream.h"
  30 
  31 /*************************************************************************
  32  * SocketInputStream
  33  */
  34 static jfieldID IO_fd_fdID;
  35 
  36 /*
  37  * Class:     java_net_SocketInputStream
  38  * Method:    init
  39  * Signature: ()V
  40  */
  41 JNIEXPORT void JNICALL
  42 Java_java_net_SocketInputStream_init(JNIEnv *env, jclass cls) {
  43     IO_fd_fdID = NET_GetFileDescriptorID(env);
  44 }
  45 
  46 /*
  47  * Class:     java_net_SocketInputStream
  48  * Method:    socketRead
  49  * Signature: (Ljava/io/FileDescriptor;[BIII)I
  50  */
  51 JNIEXPORT jint JNICALL
  52 Java_java_net_SocketInputStream_socketRead0(JNIEnv *env, jobject this,
  53                                             jobject fdObj, jbyteArray data,
  54                                             jint off, jint len, jint timeout)
  55 {
  56     char BUF[MAX_BUFFER_LEN];
  57     char *bufP;
  58     jint fd, newfd, nread;
  59 
  60     if (IS_NULL(fdObj)) {
  61         JNU_ThrowByName(env, "java/net/SocketException",
  62                         "Socket closed");
  63         return -1;
  64     }
  65     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
  66     if (fd == -1) {
  67         JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
  68         return -1;
  69     }
  70 
  71     /*
  72      * If the caller buffer is large than our stack buffer then we allocate
  73      * from the heap (up to a limit). If memory is exhausted we always use
  74      * the stack buffer.
  75      */
  76     if (len <= MAX_BUFFER_LEN) {
  77         bufP = BUF;
  78     } else {
  79         if (len > MAX_HEAP_BUFFER_LEN) {
  80             len = MAX_HEAP_BUFFER_LEN;
  81         }
  82         bufP = (char *)malloc((size_t)len);
  83         if (bufP == NULL) {
  84             /* allocation failed so use stack buffer */
  85             bufP = BUF;
  86             len = MAX_BUFFER_LEN;
  87         }
  88     }
  89 
  90 
  91     if (timeout) {
  92         if (timeout <= 5000 || !isRcvTimeoutSupported) {
  93             int ret = NET_Timeout (fd, timeout);
  94 
  95             if (ret <= 0) {
  96                 if (ret == 0) {
  97                     JNU_ThrowByName(env, "java/net/SocketTimeoutException",
  98                                     "Read timed out");
  99                 } else if (ret == -1) {
 100                     JNU_ThrowByName(env, "java/net/SocketException", "socket closed");
 101                 }
 102                 if (bufP != BUF) {
 103                     free(bufP);
 104                 }
 105                 return -1;
 106             }
 107 
 108             /*check if the socket has been closed while we were in timeout*/
 109             newfd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
 110             if (newfd == -1) {
 111                 JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 112                 if (bufP != BUF) {
 113                     free(bufP);
 114                 }
 115                 return -1;
 116             }
 117         }
 118     }
 119 
 120     nread = recv(fd, bufP, len, 0);
 121     if (nread > 0) {
 122         JVM_callNetworkReadBytes(env, nread);
 123         (*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)bufP);
 124     } else {
 125         if (nread < 0) {
 126             int err = WSAGetLastError();
 127             // Check if the socket has been closed since we last checked.
 128             // This could be a reason for recv failing.
 129             if ((*env)->GetIntField(env, fdObj, IO_fd_fdID) == -1) {
 130                 JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
 131             } else {
 132                 switch (err) {
 133                     case WSAEINTR:
 134                         JNU_ThrowByName(env, "java/net/SocketException",
 135                             "socket closed");
 136                         break;
 137 
 138                     case WSAECONNRESET:
 139                     case WSAESHUTDOWN:
 140                         /*
 141                          * Connection has been reset - Windows sometimes reports
 142                          * the reset as a shutdown error.
 143                          */
 144                         JNU_ThrowByName(env, "sun/net/ConnectionResetException",
 145                             "");
 146                         break;
 147 
 148                     case WSAETIMEDOUT :
 149                         JNU_ThrowByName(env, "java/net/SocketTimeoutException",
 150                                        "Read timed out");
 151                         break;
 152 
 153                     default:
 154                         NET_ThrowCurrent(env, "recv failed");
 155                 }
 156             }
 157         }
 158     }
 159     if (bufP != BUF) {
 160         free(bufP);
 161     }
 162     return nread;
 163 }