1 /*
2  * Copyright (c) 1997, 2017, 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 <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 
29 #include "jvm.h"
30 #include "net_util.h"
31 
32 #include "java_net_SocketInputStream.h"
33 
34 /*
35  * SocketInputStream
36  */
37 
38 static jfieldID IO_fd_fdID;
39 
40 /*
41  * Class:     java_net_SocketInputStream
42  * Method:    init
43  * Signature: ()V
44  */
45 JNIEXPORT void JNICALL
Java_java_net_SocketInputStream_init(JNIEnv * env,jclass cls)46 Java_java_net_SocketInputStream_init(JNIEnv *env, jclass cls) {
47     IO_fd_fdID = NET_GetFileDescriptorID(env);
48 }
49 
NET_ReadWithTimeout(JNIEnv * env,int fd,char * bufP,int len,long timeout)50 static int NET_ReadWithTimeout(JNIEnv *env, int fd, char *bufP, int len, long timeout) {
51     int result = 0;
52     jlong prevNanoTime = JVM_NanoTime(env, 0);
53     jlong nanoTimeout = (jlong) timeout * NET_NSEC_PER_MSEC;
54     while (nanoTimeout >= NET_NSEC_PER_MSEC) {
55         result = NET_Timeout(env, fd, nanoTimeout / NET_NSEC_PER_MSEC, prevNanoTime);
56         if (result <= 0) {
57             if (result == 0) {
58                 JNU_ThrowByName(env, "java/net/SocketTimeoutException", "Read timed out");
59             } else if (result == -1) {
60                 if (errno == EBADF) {
61                     JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
62                 } else if (errno == ENOMEM) {
63                     JNU_ThrowOutOfMemoryError(env, "NET_Timeout native heap allocation failed");
64                 } else {
65                     JNU_ThrowByNameWithMessageAndLastError
66                             (env, "java/net/SocketException", "select/poll failed");
67                 }
68             }
69             return -1;
70         }
71         result = NET_NonBlockingRead(fd, bufP, len);
72         if (result == -1 && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) {
73             jlong newtNanoTime = JVM_NanoTime(env, 0);
74             nanoTimeout -= newtNanoTime - prevNanoTime;
75             if (nanoTimeout >= NET_NSEC_PER_MSEC) {
76                 prevNanoTime = newtNanoTime;
77             }
78         } else {
79             break;
80         }
81     }
82     return result;
83 }
84 
85 /*
86  * Class:     java_net_SocketInputStream
87  * Method:    socketRead0
88  * Signature: (Ljava/io/FileDescriptor;[BIII)I
89  */
90 JNIEXPORT jint JNICALL
Java_java_net_SocketInputStream_socketRead0(JNIEnv * env,jobject this,jobject fdObj,jbyteArray data,jint off,jint len,jint timeout)91 Java_java_net_SocketInputStream_socketRead0(JNIEnv *env, jobject this,
92                                             jobject fdObj, jbyteArray data,
93                                             jint off, jint len, jint timeout)
94 {
95     char BUF[MAX_BUFFER_LEN];
96     char *bufP;
97     jint fd, nread;
98 
99     if (IS_NULL(fdObj)) {
100         JNU_ThrowByName(env, "java/net/SocketException",
101                         "Socket closed");
102         return -1;
103     }
104     fd = (*env)->GetIntField(env, fdObj, IO_fd_fdID);
105     if (fd == -1) {
106         JNU_ThrowByName(env, "java/net/SocketException", "Socket closed");
107         return -1;
108     }
109 
110     /*
111      * If the read is greater than our stack allocated buffer then
112      * we allocate from the heap (up to a limit)
113      */
114     if (len > MAX_BUFFER_LEN) {
115         if (len > MAX_HEAP_BUFFER_LEN) {
116             len = MAX_HEAP_BUFFER_LEN;
117         }
118         bufP = (char *)malloc((size_t)len);
119         if (bufP == NULL) {
120             bufP = BUF;
121             len = MAX_BUFFER_LEN;
122         }
123     } else {
124         bufP = BUF;
125     }
126     if (timeout) {
127         nread = NET_ReadWithTimeout(env, fd, bufP, len, timeout);
128         if ((*env)->ExceptionCheck(env)) {
129             if (bufP != BUF) {
130                 free(bufP);
131             }
132             return nread;
133         }
134     } else {
135         nread = NET_Read(fd, bufP, len);
136     }
137 
138     if (nread <= 0) {
139         if (nread < 0) {
140 
141             switch (errno) {
142                 case ECONNRESET:
143                 case EPIPE:
144                     JNU_ThrowByName(env, "sun/net/ConnectionResetException",
145                         "Connection reset");
146                     break;
147 
148                 case EBADF:
149                     JNU_ThrowByName(env, "java/net/SocketException",
150                         "Socket closed");
151                     break;
152 
153                 case EINTR:
154                      JNU_ThrowByName(env, "java/io/InterruptedIOException",
155                            "Operation interrupted");
156                      break;
157                 default:
158                     JNU_ThrowByNameWithMessageAndLastError
159                         (env, "java/net/SocketException", "Read failed");
160             }
161         }
162     } else {
163         (*env)->SetByteArrayRegion(env, data, off, nread, (jbyte *)bufP);
164     }
165 
166     if (bufP != BUF) {
167         free(bufP);
168     }
169     return nread;
170 }
171