View Javadoc
1   /*
2    * Copyright 2015 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.channel.unix;
17  
18  import io.netty.util.internal.EmptyArrays;
19  
20  import java.io.FileNotFoundException;
21  import java.io.IOException;
22  import java.net.ConnectException;
23  import java.net.NoRouteToHostException;
24  import java.nio.channels.AlreadyConnectedException;
25  import java.nio.channels.ClosedChannelException;
26  import java.nio.channels.ConnectionPendingException;
27  import java.nio.channels.NotYetConnectedException;
28  
29  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEAGAIN;
30  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEBADF;
31  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoECONNRESET;
32  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEINPROGRESS;
33  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOENT;
34  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoENOTCONN;
35  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEPIPE;
36  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errnoEWOULDBLOCK;
37  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEALREADY;
38  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorECONNREFUSED;
39  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEHOSTUNREACH;
40  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorEISCONN;
41  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.errorENETUNREACH;
42  import static io.netty.channel.unix.ErrorsStaticallyReferencedJniMethods.strError;
43  
44  /**
45   * <strong>Internal usage only!</strong>
46   * <p>Static members which call JNI methods must be defined in {@link ErrorsStaticallyReferencedJniMethods}.
47   */
48  public final class Errors {
49      // As all our JNI methods return -errno on error we need to compare with the negative errno codes.
50      public static final int ERRNO_ENOENT_NEGATIVE = -errnoENOENT();
51      public static final int ERRNO_ENOTCONN_NEGATIVE = -errnoENOTCONN();
52      public static final int ERRNO_EBADF_NEGATIVE = -errnoEBADF();
53      public static final int ERRNO_EPIPE_NEGATIVE = -errnoEPIPE();
54      public static final int ERRNO_ECONNRESET_NEGATIVE = -errnoECONNRESET();
55      public static final int ERRNO_EAGAIN_NEGATIVE = -errnoEAGAIN();
56      public static final int ERRNO_EWOULDBLOCK_NEGATIVE = -errnoEWOULDBLOCK();
57      public static final int ERRNO_EINPROGRESS_NEGATIVE = -errnoEINPROGRESS();
58      public static final int ERROR_ECONNREFUSED_NEGATIVE = -errorECONNREFUSED();
59      public static final int ERROR_EISCONN_NEGATIVE = -errorEISCONN();
60      public static final int ERROR_EALREADY_NEGATIVE = -errorEALREADY();
61      public static final int ERROR_ENETUNREACH_NEGATIVE = -errorENETUNREACH();
62      public static final int ERROR_EHOSTUNREACH_NEGATIVE = -errorEHOSTUNREACH();
63  
64      /**
65       * Holds the mappings for errno codes to String messages.
66       * This eliminates the need to call back into JNI to get the right String message on an exception
67       * and thus is faster.
68       *
69       * Choose an array length which should give us enough space in the future even when more errno codes
70       * will be added.
71       */
72      private static final String[] ERRORS = new String[2048];
73  
74      /**
75       * <strong>Internal usage only!</strong>
76       */
77      public static final class NativeIoException extends IOException {
78          private static final long serialVersionUID = 8222160204268655526L;
79          private final int expectedErr;
80          private final boolean fillInStackTrace;
81  
82          public NativeIoException(String method, int expectedErr) {
83              this(method, expectedErr, true);
84          }
85  
86          public NativeIoException(String method, int expectedErr, boolean fillInStackTrace) {
87              super(method + "(..) failed: " + errnoString(-expectedErr));
88              this.expectedErr = expectedErr;
89              this.fillInStackTrace = fillInStackTrace;
90          }
91  
92          public int expectedErr() {
93              return expectedErr;
94          }
95  
96          @Override
97          public synchronized Throwable fillInStackTrace() {
98              if (fillInStackTrace) {
99                  return super.fillInStackTrace();
100             }
101             return this;
102         }
103     }
104 
105     static final class NativeConnectException extends ConnectException {
106         private static final long serialVersionUID = -5532328671712318161L;
107         private final int expectedErr;
108         NativeConnectException(String method, int expectedErr) {
109             super(method + "(..) failed: " + errnoString(-expectedErr));
110             this.expectedErr = expectedErr;
111         }
112 
113         int expectedErr() {
114             return expectedErr;
115         }
116     }
117 
118     static {
119         for (int i = 0; i < ERRORS.length; i++) {
120             // This is ok as strerror returns 'Unknown error i' when the message is not known.
121             ERRORS[i] = strError(i);
122         }
123     }
124 
125     public static boolean handleConnectErrno(String method, int err) throws IOException {
126         if (err == ERRNO_EINPROGRESS_NEGATIVE || err == ERROR_EALREADY_NEGATIVE) {
127             // connect not complete yet need to wait for EPOLLOUT event.
128             // EALREADY has been observed when using tcp fast open on centos8.
129             return false;
130         }
131         throw newConnectException0(method, err);
132     }
133 
134     /**
135      * @deprecated Use {@link #handleConnectErrno(String, int)}.
136      * @param method The native method name which caused the errno.
137      * @param err the negative value of the errno.
138      * @throws IOException The errno translated into an exception.
139      */
140     @Deprecated
141     public static void throwConnectException(String method, int err) throws IOException {
142         if (err == ERROR_EALREADY_NEGATIVE) {
143             throw new ConnectionPendingException();
144         }
145         throw newConnectException0(method, err);
146     }
147 
148     private static String errnoString(int err) {
149         // Check first if we had it cached, if not we need to do a JNI call.
150         if (err < ERRORS.length - 1) {
151             return ERRORS[err];
152         }
153         return strError(err);
154     }
155 
156     private static IOException newConnectException0(String method, int err) {
157         if (err == ERROR_ENETUNREACH_NEGATIVE || err == ERROR_EHOSTUNREACH_NEGATIVE) {
158             return new NoRouteToHostException();
159         }
160         if (err == ERROR_EISCONN_NEGATIVE) {
161             throw new AlreadyConnectedException();
162         }
163         if (err == ERRNO_ENOENT_NEGATIVE) {
164             return new FileNotFoundException();
165         }
166         return new ConnectException(method + "(..) failed: " + errnoString(-err));
167     }
168 
169     public static NativeIoException newConnectionResetException(String method, int errnoNegative) {
170         NativeIoException exception = new NativeIoException(method, errnoNegative, false);
171         exception.setStackTrace(EmptyArrays.EMPTY_STACK_TRACE);
172         return exception;
173     }
174 
175     public static NativeIoException newIOException(String method, int err) {
176         return new NativeIoException(method, err);
177     }
178 
179     @Deprecated
180     public static int ioResult(String method, int err, NativeIoException resetCause,
181                                ClosedChannelException closedCause) throws IOException {
182         // network stack saturated... try again later
183         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
184             return 0;
185         }
186         if (err == resetCause.expectedErr()) {
187             throw resetCause;
188         }
189         if (err == ERRNO_EBADF_NEGATIVE) {
190             throw closedCause;
191         }
192         if (err == ERRNO_ENOTCONN_NEGATIVE) {
193             throw new NotYetConnectedException();
194         }
195         if (err == ERRNO_ENOENT_NEGATIVE) {
196             throw new FileNotFoundException();
197         }
198 
199         // TODO: We could even go further and use a pre-instantiated IOException for the other error codes, but for
200         //       all other errors it may be better to just include a stack trace.
201         throw newIOException(method, err);
202     }
203 
204     public static int ioResult(String method, int err) throws IOException {
205         // network stack saturated... try again later
206         if (err == ERRNO_EAGAIN_NEGATIVE || err == ERRNO_EWOULDBLOCK_NEGATIVE) {
207             return 0;
208         }
209         if (err == ERRNO_EBADF_NEGATIVE) {
210             throw new ClosedChannelException();
211         }
212         if (err == ERRNO_ENOTCONN_NEGATIVE) {
213             throw new NotYetConnectedException();
214         }
215         if (err == ERRNO_ENOENT_NEGATIVE) {
216             throw new FileNotFoundException();
217         }
218 
219         throw new NativeIoException(method, err, false);
220     }
221 
222     private Errors() { }
223 }