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